WIP, signature additions.
This commit is contained in:
parent
a913ac97d8
commit
98e00ccf48
@ -10,6 +10,31 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.1edtech</groupId>
|
<groupId>org.1edtech</groupId>
|
||||||
<artifactId>inspector-core</artifactId>
|
<artifactId>inspector-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcpkix-jdk15on</artifactId>
|
||||||
|
<version>1.58</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>auth0</artifactId>
|
||||||
|
<version>1.20.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>jwks-rsa</artifactId>
|
||||||
|
<version>0.12.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>java-jwt</artifactId>
|
||||||
|
<version>3.10.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>5.0.12.RELEASE</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@ -52,6 +52,10 @@ public class Credential extends GeneratedObject {
|
|||||||
public Credential.Type getCredentialType() {
|
public Credential.Type getCredentialType() {
|
||||||
return credentialType;
|
return credentialType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getJwt() {
|
||||||
|
return jwt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the canonical schema for this credential if such exists.
|
* Get the canonical schema for this credential if such exists.
|
||||||
|
@ -6,6 +6,7 @@ import static org.oneedtech.inspect.core.report.ReportUtil.onProbeException;
|
|||||||
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
|
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
|
||||||
import static org.oneedtech.inspect.vc.EndorsementInspector.ENDORSEMENT_KEY;
|
import static org.oneedtech.inspect.vc.EndorsementInspector.ENDORSEMENT_KEY;
|
||||||
import static org.oneedtech.inspect.vc.util.JsonNodeUtil.getEndorsements;
|
import static org.oneedtech.inspect.vc.util.JsonNodeUtil.getEndorsements;
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -105,10 +106,12 @@ public class OB30Inspector extends VCInspector {
|
|||||||
probeCount++;
|
probeCount++;
|
||||||
accumulator.add(new InlineJsonSchemaProbe().run(crd, ctx));
|
accumulator.add(new InlineJsonSchemaProbe().run(crd, ctx));
|
||||||
|
|
||||||
//verify signatures TODO @Miles
|
//If this credential was originally contained in a JWT we must validate the jwt and external proof.
|
||||||
probeCount++;
|
if(!isNullOrEmpty(crd.getJwt())){
|
||||||
accumulator.add(new SignatureVerifierProbe().run(crd, ctx));
|
probeCount++;
|
||||||
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
accumulator.add(new SignatureVerifierProbe().run(crd, ctx));
|
||||||
|
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
||||||
|
}
|
||||||
|
|
||||||
//verify proofs TODO @Miles
|
//verify proofs TODO @Miles
|
||||||
probeCount++;
|
probeCount++;
|
||||||
|
@ -49,18 +49,13 @@ public class CredentialTypeProbe extends Probe<Resource> {
|
|||||||
|
|
||||||
if(type.isPresent()) {
|
if(type.isPresent()) {
|
||||||
resource.setType(type.get());
|
resource.setType(type.get());
|
||||||
//TODO: Refactor to return the entire credential so we can include optional encoded JWT.
|
|
||||||
if(type.get() == ResourceType.PNG) {
|
if(type.get() == ResourceType.PNG) {
|
||||||
//crd = new Credential(resource, fromPNG(resource, context));
|
|
||||||
crd = fromPNG(resource, context);
|
crd = fromPNG(resource, context);
|
||||||
} else if(type.get() == ResourceType.SVG) {
|
} else if(type.get() == ResourceType.SVG) {
|
||||||
//crd = new Credential(resource, fromSVG(resource, context));
|
|
||||||
crd = fromSVG(resource, context);
|
crd = fromSVG(resource, context);
|
||||||
} else if(type.get() == ResourceType.JSON) {
|
} else if(type.get() == ResourceType.JSON) {
|
||||||
//crd = new Credential(resource, fromJson(resource, context));
|
|
||||||
crd = fromJson(resource, context);
|
crd = fromJson(resource, context);
|
||||||
} else if(type.get() == ResourceType.JWT) {
|
} else if(type.get() == ResourceType.JWT) {
|
||||||
//crd = new Credential(resource, fromJWT(resource, context));
|
|
||||||
crd = fromJWT(resource, context);
|
crd = fromJWT(resource, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,30 @@
|
|||||||
package org.oneedtech.inspect.vc.probe;
|
package org.oneedtech.inspect.vc.probe;
|
||||||
|
|
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.RSAPublicKeySpec;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Base64.Decoder;
|
||||||
|
|
||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.Credential;
|
||||||
|
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.interfaces.DecodedJWT;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies credential signatures
|
* A Probe that verifies credential signatures
|
||||||
@ -17,11 +38,56 @@ public class SignatureVerifierProbe extends Probe<Credential> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
||||||
|
try {
|
||||||
//TODO @Miles -- if sigs fail, report OutCome.Fatal
|
testingSignatureValidationCode(crd);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return fatal("Error verifying jwt signature: " + e.getMessage(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void testingSignatureValidationCode(Credential crd) throws Exception {
|
||||||
|
DecodedJWT decodedJwt = null;
|
||||||
|
String jwt = crd.getJwt();
|
||||||
|
if(isNullOrEmpty(jwt)) throw new IllegalArgumentException("invalid jwt");
|
||||||
|
List<String> parts = Splitter.on('.').splitToList(jwt);
|
||||||
|
if(parts.size() != 3) throw new IllegalArgumentException("invalid jwt");
|
||||||
|
|
||||||
|
final Decoder decoder = Base64.getUrlDecoder();
|
||||||
|
String joseHeader = new String(decoder.decode(parts.get(0)));
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
JsonNode headerObj = mapper.readTree(joseHeader);
|
||||||
|
|
||||||
|
//MUST be "RS256"
|
||||||
|
|
||||||
|
//Option 1, fetch directly from header
|
||||||
|
JsonNode jwk = headerObj.get("jwk");
|
||||||
|
|
||||||
|
//Option 2, fetch from a hosting url
|
||||||
|
JsonNode kid = headerObj.get("kid");
|
||||||
|
|
||||||
|
if(jwk == null && kid == null) { throw new Exception("asdf"); }
|
||||||
|
if(kid != null){
|
||||||
|
//TODO @Miles load jwk JsonNode from url and do the rest the same below. Need to set up testing.
|
||||||
|
String kidUrl = kid.textValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clean up may be required. Currently need to cleanse extra double quoting.
|
||||||
|
String modulusString = jwk.get("n").textValue();
|
||||||
|
String exponentString = jwk.get("e").textValue();
|
||||||
|
|
||||||
|
BigInteger modulus = new BigInteger(1, org.springframework.util.Base64Utils.decodeFromUrlSafeString(modulusString));
|
||||||
|
BigInteger exponent = new BigInteger(1, org.springframework.util.Base64Utils.decodeFromUrlSafeString(exponentString));
|
||||||
|
|
||||||
|
PublicKey pub = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));
|
||||||
|
|
||||||
|
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey)pub, null);
|
||||||
|
JWTVerifier verifier = JWT.require(algorithm)
|
||||||
|
.build(); //Reusable verifier instance
|
||||||
|
decodedJwt = verifier.verify(jwt);
|
||||||
|
}
|
||||||
|
|
||||||
public static final String ID = SignatureVerifierProbe.class.getSimpleName();
|
public static final String ID = SignatureVerifierProbe.class.getSimpleName();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user