Committing to merge in changes. WIP, may delete a lot of this.
This commit is contained in:
parent
5d5f3f7588
commit
599155ca6e
@ -35,7 +35,14 @@
|
|||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-core</artifactId>
|
<artifactId>spring-core</artifactId>
|
||||||
<version>5.0.12.RELEASE</version>
|
<version>5.0.12.RELEASE</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google</groupId>
|
||||||
|
<artifactId>bitcoinj</artifactId>
|
||||||
|
<version>0.11.3</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Thanks for using https://jar-download.com -->
|
||||||
|
|
||||||
<!-- https://mvnrepository.com/artifact/com.apicatalog/titanium-json-ld -->
|
<!-- https://mvnrepository.com/artifact/com.apicatalog/titanium-json-ld -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.apicatalog</groupId>
|
<groupId>com.apicatalog</groupId>
|
||||||
|
@ -4,14 +4,28 @@ import static org.oneedtech.inspect.core.probe.RunContext.Key.JACKSON_OBJECTMAPP
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Base64.Encoder;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
|
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
|
||||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
|
import org.bouncycastle.crypto.Signer;
|
||||||
|
import org.bouncycastle.crypto.signers.Ed25519Signer;
|
||||||
|
import org.bouncycastle.crypto.signers.RSADigestSigner;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
@ -28,6 +42,8 @@ import com.apicatalog.rdf.RdfDataset;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.google.bitcoin.core.Base58;
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
|
||||||
import io.setl.rdf.normalization.RdfNormalize;
|
import io.setl.rdf.normalization.RdfNormalize;
|
||||||
|
|
||||||
@ -45,9 +61,42 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
|||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String canonical = canonicalize(crd, C14n.URDNA2015, MediaType.N_QUADS, ctx);
|
//String document = fetchConanicalDocument(crd, C14n.URDNA2015, MediaType.N_QUADS, ctx);
|
||||||
|
String document = "";
|
||||||
|
String proof = fetchConanicalProof(crd, C14n.URDNA2015, MediaType.N_QUADS, ctx);
|
||||||
//System.out.println(canonical);
|
//System.out.println(canonical);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Encoder encoder1 = Base64.getEncoder();
|
||||||
|
String testSignature = "z3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM";
|
||||||
|
String signature = encoder1.encodeToString(testSignature.getBytes());
|
||||||
|
|
||||||
|
Encoder encoder2 = Base64.getEncoder();
|
||||||
|
String testKey = "z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj";
|
||||||
|
String key64 = encoder2.encodeToString(testKey.getBytes());
|
||||||
|
String keyHex = Hex.toHexString(testKey.getBytes());
|
||||||
|
|
||||||
|
boolean test = validate(
|
||||||
|
keyHex,
|
||||||
|
signature,
|
||||||
|
"",
|
||||||
|
canonical
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
byte[] docHash = getBytes(document);
|
||||||
|
byte[] proofHash = getBytes(proof);
|
||||||
|
// concatenate hash of c14n proof options and hash of c14n document
|
||||||
|
byte[] combined = mergeArrays(proofHash, docHash);
|
||||||
|
|
||||||
|
boolean test = testSigner(combined);
|
||||||
|
|
||||||
|
|
||||||
|
boolean stophere = true;
|
||||||
//TODO if proofs fail, report OutCome.Fatal
|
//TODO if proofs fail, report OutCome.Fatal
|
||||||
//return fatal("msg", ctx);
|
//return fatal("msg", ctx);
|
||||||
|
|
||||||
@ -57,7 +106,7 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
|||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String canonicalize(Credential crd, C14n algo, MediaType mediaType, RunContext ctx) throws Exception {
|
private String fetchConanicalDocument(Credential crd, C14n algo, MediaType mediaType, RunContext ctx) throws Exception {
|
||||||
|
|
||||||
//clone the incoming credential object so we can modify it freely
|
//clone the incoming credential object so we can modify it freely
|
||||||
ObjectMapper mapper = (ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER);
|
ObjectMapper mapper = (ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER);
|
||||||
@ -81,6 +130,55 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String fetchConanicalProof(Credential crd, C14n algo, MediaType mediaType, RunContext ctx) throws Exception {
|
||||||
|
|
||||||
|
//clone the incoming credential object so we can modify it freely
|
||||||
|
ObjectMapper mapper = (ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER);
|
||||||
|
JsonNode copy = mapper.readTree(crd.asJson().toString());
|
||||||
|
|
||||||
|
//Get the context node to stitch back in.
|
||||||
|
JsonNode context = copy.get("@context");
|
||||||
|
|
||||||
|
//Pull out and use proof node only
|
||||||
|
JsonNode proof = copy.get("proof");
|
||||||
|
|
||||||
|
//TODO: Make this better at discarding all, but the linked data proof method.
|
||||||
|
if(proof.isArray()){
|
||||||
|
proof = proof.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove these if they exist
|
||||||
|
((ObjectNode)proof).remove("jwt");
|
||||||
|
((ObjectNode)proof).remove("signatureValue");
|
||||||
|
((ObjectNode)proof).remove("proofValue");
|
||||||
|
|
||||||
|
JsonNode newNode = mapper.createObjectNode();
|
||||||
|
((ObjectNode) newNode).set("@context", context);
|
||||||
|
//Try to structure this 'to the letter' per a slack with Markus
|
||||||
|
//((ObjectNode) newNode).set("proof", proof);
|
||||||
|
|
||||||
|
//So that we don't remove nodes while iterating over it save all nodes
|
||||||
|
Iterator<Entry<String,JsonNode>> iter = proof.fields();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Entry<String,JsonNode> next = iter.next();
|
||||||
|
((ObjectNode) newNode).set(next.getKey(), next.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
//create JSON-P Json-LD instance
|
||||||
|
JsonDocument jsonLdDoc = JsonDocument.of(new StringReader(newNode.toString()));
|
||||||
|
|
||||||
|
//create rdf and normalize
|
||||||
|
RdfDataset dataSet = JsonLd.toRdf(jsonLdDoc).ordered(true).get();
|
||||||
|
RdfDataset normalized = RdfNormalize.normalize(dataSet);
|
||||||
|
|
||||||
|
//serialize to string
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
Rdf.createWriter(mediaType, os).write(normalized);
|
||||||
|
String result = StringUtils.stripTrailing(os.toString());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean validate(String pubkey, String signature, String timestamp, String message) throws Exception {
|
private boolean validate(String pubkey, String signature, String timestamp, String message) throws Exception {
|
||||||
//TODO: continue this implementation.
|
//TODO: continue this implementation.
|
||||||
//Pulled in bouncy castle library and made sure this sample compiled only.
|
//Pulled in bouncy castle library and made sure this sample compiled only.
|
||||||
@ -93,10 +191,140 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
|||||||
final var publicKey = kf.generatePublic(pkSpec);
|
final var publicKey = kf.generatePublic(pkSpec);
|
||||||
final var signedData = Signature.getInstance("ed25519", provider);
|
final var signedData = Signature.getInstance("ed25519", provider);
|
||||||
signedData.initVerify(publicKey);
|
signedData.initVerify(publicKey);
|
||||||
|
//Temporarily remove timestamp
|
||||||
signedData.update(timestamp.getBytes());
|
signedData.update(timestamp.getBytes());
|
||||||
signedData.update(message.getBytes());
|
signedData.update(message.getBytes());
|
||||||
return signedData.verify(Hex.decode(signature));
|
return signedData.verify(Hex.decode(signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean testSigner(byte[] concatBytes) throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
final var provider = new BouncyCastleProvider();
|
||||||
|
Security.addProvider(provider);
|
||||||
|
|
||||||
|
//var publicKeyBytes = Base64.getUrlDecoder().decode("z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj");
|
||||||
|
//var publicKeyBytes = Base64.getUrlDecoder().decode("6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj");
|
||||||
|
|
||||||
|
|
||||||
|
//var publicKeyBytes = Base58.decode("z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj");
|
||||||
|
//Key with the first chracter stripped
|
||||||
|
//var publicKeyBytes = Base58.decode("6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj");
|
||||||
|
|
||||||
|
|
||||||
|
//A working sample key
|
||||||
|
//var publicKeyBytes = Base64.getUrlDecoder().decode("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Base 58 decode minus the z
|
||||||
|
var publicKeyBytes = Base58.decode("6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj");
|
||||||
|
//The slice out the first two array elements (???)
|
||||||
|
byte[] slicedArray = Arrays.copyOfRange(publicKeyBytes, 2, 34);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final var pki = new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), slicedArray);
|
||||||
|
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(concatBytes);
|
||||||
|
|
||||||
|
boolean whatever = true;
|
||||||
|
|
||||||
|
|
||||||
|
//Final step, add signature.
|
||||||
|
|
||||||
|
//Need to do this in java
|
||||||
|
//const signatureBytes = base58btc.decode(proofValue.substr(1));
|
||||||
|
|
||||||
|
|
||||||
|
var signatureBytes = Base58.decode("3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM");
|
||||||
|
|
||||||
|
return signedData.verify(signatureBytes);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
String hexEncodedPubKey = "z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj";
|
||||||
|
|
||||||
|
// Convert to JCA format
|
||||||
|
byte[] pubKeyBytes = BaseEncoding.base16().lowerCase().decode(hexEncodedPubKey);
|
||||||
|
SubjectPublicKeyInfo pubKeyInfo = new SubjectPublicKeyInfo(
|
||||||
|
new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), pubKeyBytes);
|
||||||
|
|
||||||
|
|
||||||
|
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyInfo.getEncoded());
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("Ed25519", provider);
|
||||||
|
PublicKey pk = keyFactory.generatePublic(keySpec);
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
var test = new RSADigestSigner(digest, digestOid)
|
||||||
|
|
||||||
|
test.verifySignature(signature);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private boolean testSigner3(String message, byte[] concateBytes) throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
// Test case defined in https://www.rfc-editor.org/rfc/rfc8037
|
||||||
|
var msg = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc".getBytes(StandardCharsets.UTF_8);
|
||||||
|
var expectedSig = "hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";
|
||||||
|
|
||||||
|
var privateKeyBytes = Base64.getUrlDecoder().decode("nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A");
|
||||||
|
var publicKeyBytes = Base64.getUrlDecoder().decode("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");
|
||||||
|
|
||||||
|
var privateKey = new Ed25519PrivateKeyParameters(privateKeyBytes, 0);
|
||||||
|
var publicKey = new Ed25519PublicKeyParameters(publicKeyBytes, 0);
|
||||||
|
|
||||||
|
// Generate new signature
|
||||||
|
Signer signer = new Ed25519Signer();
|
||||||
|
signer.init(true, privateKey);
|
||||||
|
signer.update(msg, 0, msg.length);
|
||||||
|
byte[] signature = signer.generateSignature();
|
||||||
|
var actualSignature = Base64.getUrlEncoder().encodeToString(signature).replace("=", "");
|
||||||
|
|
||||||
|
LOG.info("Expected signature: {}", expectedSig);
|
||||||
|
LOG.info("Actual signature : {}", actualSignature);
|
||||||
|
|
||||||
|
assertEquals(expectedSig, actualSignature);
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] getBytes(String value) throws Exception{
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
return digest.digest(
|
||||||
|
value.getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] mergeArrays(final byte[] array1, byte[] array2) {
|
||||||
|
byte[] joinedArray = Arrays.copyOf(array1, array1.length + array2.length);
|
||||||
|
System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
|
||||||
|
return joinedArray;
|
||||||
|
}
|
||||||
|
|
||||||
private enum C14n {
|
private enum C14n {
|
||||||
URDNA2015
|
URDNA2015
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/2018/credentials/v1",
|
|
||||||
"https://imsglobal.github.io/openbadges-specification/context.json",
|
|
||||||
"https://w3id.org/security/suites/ed25519-2020/v1"
|
|
||||||
],
|
|
||||||
"id": "http://example.edu/credentials/3732",
|
|
||||||
"type": [
|
|
||||||
"VerifiableCredential",
|
|
||||||
"OpenBadgeCredential"
|
|
||||||
],
|
|
||||||
"issuer": {
|
|
||||||
"id": "https://example.edu/issuers/565049",
|
|
||||||
"type": [
|
|
||||||
"Profile"
|
|
||||||
],
|
|
||||||
"name": "Example University"
|
|
||||||
},
|
|
||||||
"issuanceDate": "2010-01-01T00:00:00Z",
|
|
||||||
"name": "Example University Degree",
|
|
||||||
"credentialSubject": {
|
|
||||||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
|
||||||
"type": [
|
|
||||||
"AchievementSubject"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"proof": [
|
|
||||||
{
|
|
||||||
"type": "Ed25519Signature2020",
|
|
||||||
"created": "2022-06-09T22:56:28Z",
|
|
||||||
"verificationMethod": "https://example.edu/issuers/565049#key-1",
|
|
||||||
"proofPurpose": "assertionMethod",
|
|
||||||
"proofValue": "z58ieJCh4kN6eE2Vq4TyYURKSC4hWWEK7b75NNUL2taZMhKqwTteuByG1wRoGDdCqqNLW5Gq1diUi4qyZ63tQRtyN"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user