Committing to merge in changes. WIP, may delete a lot of this.
This commit is contained in:
parent
5d5f3f7588
commit
599155ca6e
@ -36,6 +36,13 @@
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>5.0.12.RELEASE</version>
|
||||
</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 -->
|
||||
<dependency>
|
||||
<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.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
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.x509.AlgorithmIdentifier;
|
||||
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.util.encoders.Hex;
|
||||
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.ObjectMapper;
|
||||
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;
|
||||
|
||||
@ -45,9 +61,42 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
//return fatal("msg", ctx);
|
||||
|
||||
@ -57,7 +106,7 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
||||
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
|
||||
ObjectMapper mapper = (ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER);
|
||||
@ -81,6 +130,55 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
||||
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 {
|
||||
//TODO: continue this implementation.
|
||||
//Pulled in bouncy castle library and made sure this sample compiled only.
|
||||
@ -93,11 +191,141 @@ public class ProofVerifierProbe extends Probe<Credential> {
|
||||
final var publicKey = kf.generatePublic(pkSpec);
|
||||
final var signedData = Signature.getInstance("ed25519", provider);
|
||||
signedData.initVerify(publicKey);
|
||||
//Temporarily remove timestamp
|
||||
signedData.update(timestamp.getBytes());
|
||||
signedData.update(message.getBytes());
|
||||
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 {
|
||||
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