diff --git a/inspector-vc/pom.xml b/inspector-vc/pom.xml index 396f3d5..f253160 100644 --- a/inspector-vc/pom.xml +++ b/inspector-vc/pom.xml @@ -11,10 +11,10 @@ org.1edtech inspector-core - + org.bouncycastle - bcpkix-jdk15on - 1.58 + bcprov-jdk15to18 + 1.65 com.auth0 diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java index da95e80..d68c7e8 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java @@ -114,9 +114,12 @@ public class OB30Inspector extends VCInspector { } //verify proofs TODO @Miles - probeCount++; - accumulator.add(new ProofVerifierProbe().run(crd, ctx)); - if(broken(accumulator)) return abort(ctx, accumulator, probeCount); + //If this credential was not contained in a jwt it must have an internal proof. + if(isNullOrEmpty(crd.getJwt())){ + probeCount++; + accumulator.add(new ProofVerifierProbe().run(crd, ctx)); + if(broken(accumulator)) return abort(ctx, accumulator, probeCount); + } //check refresh service if we are not already refreshed probeCount++; diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ProofVerifierProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ProofVerifierProbe.java index e506aec..1618148 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ProofVerifierProbe.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ProofVerifierProbe.java @@ -1,5 +1,15 @@ package org.oneedtech.inspect.vc.probe; +import java.security.KeyFactory; +import java.security.Security; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; import org.oneedtech.inspect.core.probe.Probe; import org.oneedtech.inspect.core.probe.RunContext; import org.oneedtech.inspect.core.report.ReportItems; @@ -23,5 +33,22 @@ public class ProofVerifierProbe extends Probe { return success(ctx); } + public boolean validate(String pubkey, String signature, String timestamp, String message) throws Exception { + //TODO: continue this implementation. + //Pulled in bouncy castle library and made sure this sample compiled only. + final var provider = new BouncyCastleProvider(); + Security.addProvider(provider); + final var byteKey = Hex.decode(pubkey); + final var pki = new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), byteKey); + final var pkSpec = new X509EncodedKeySpec(pki.getEncoded()); + final var kf = KeyFactory.getInstance("ed25519", provider); + final var publicKey = kf.generatePublic(pkSpec); + final var signedData = Signature.getInstance("ed25519", provider); + signedData.initVerify(publicKey); + signedData.update(timestamp.getBytes()); + signedData.update(message.getBytes()); + return signedData.verify(Hex.decode(signature)); + } + public static final String ID = ProofVerifierProbe.class.getSimpleName(); } diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/SignatureVerifierProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/SignatureVerifierProbe.java index 91f4f64..e8c06ae 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/SignatureVerifierProbe.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/SignatureVerifierProbe.java @@ -21,6 +21,10 @@ import org.springframework.util.Base64Utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.AlgorithmMismatchException; +import com.auth0.jwt.exceptions.InvalidClaimException; +import com.auth0.jwt.exceptions.SignatureVerificationException; +import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -39,7 +43,7 @@ public class SignatureVerifierProbe extends Probe { @Override public ReportItems run(Credential crd, RunContext ctx) throws Exception { try { - testingSignatureValidationCode(crd); + verifySignature(crd); } catch (Exception e) { return fatal("Error verifying jwt signature: " + e.getMessage(), ctx); } @@ -47,7 +51,7 @@ public class SignatureVerifierProbe extends Probe { return success(ctx); } - private void testingSignatureValidationCode(Credential crd) throws Exception { + private void verifySignature(Credential crd) throws Exception { DecodedJWT decodedJwt = null; String jwt = crd.getJwt(); if(isNullOrEmpty(jwt)) throw new IllegalArgumentException("invalid jwt"); @@ -90,7 +94,21 @@ public class SignatureVerifierProbe extends Probe { Algorithm algorithm = Algorithm.RSA256((RSAPublicKey)pub, null); JWTVerifier verifier = JWT.require(algorithm) .build(); //Reusable verifier instance - decodedJwt = verifier.verify(jwt); + try { + decodedJwt = verifier.verify(jwt); + } + catch(SignatureVerificationException ex){ + throw new Exception("JWT Invalid signature", ex); + } + catch(AlgorithmMismatchException ex){ + throw new Exception("JWT Algorithm mismatch", ex); + } + catch(TokenExpiredException ex){ + throw new Exception("JWT Token expired", ex); + } + catch(InvalidClaimException ex){ + throw new Exception("JWT, one or more claims are invalid", ex); + } } public static final String ID = SignatureVerifierProbe.class.getSimpleName();