Skip to content

Commit

Permalink
[INJICERT-585] refactor VC Sign algo impl & add Ed25519Signature2020 (#…
Browse files Browse the repository at this point in the history
…128)

* [INJICERT-585] refactor VC Sign algo impl & add Ed25519Signature2020

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-585] rename proof interface impl

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-585] re-organize packages & make obj singleton

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-585] refactor proof signing

move SignatureServiceX calls to ProofStrategy

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-585] bump up keymanager version

* add support for Ed25519-2020 signing to keymanager

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-585] remove unused class

Signed-off-by: Harsh Vardhan <[email protected]>

---------

Signed-off-by: Harsh Vardhan <[email protected]>
  • Loading branch information
vharsh authored Nov 13, 2024
1 parent 7151588 commit 57bb461
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ public class SignatureAlg {

public static final String ED25519_SIGNATURE_SUITE = "Ed25519Signature2018";

public static final String ED25519_SIGNATURE_SUITE_2020 = "Ed25519Signature2020";

// RS256, PS256, ES256 --> JWSAlgorithm.RS256.getName();
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package io.mosip.certify.api.spi;

import io.mosip.certify.api.dto.VCResult;
import java.util.Map;

/**
* VCSigner can sign any VC provided a vcHash & Signer inputs
*/
public interface VCSigner {
VCResult<?> perform(String templatedVC, Map<String, String> params);
VCResult<?> perform(String templatedVC);
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,25 +166,7 @@ private VCResult<?> getVerifiableCredential(CredentialRequest credentialRequest,
templateParams.put(VelocityTemplatingConstants.SVG_TEMPLATE, svg);
}
String templatedVC = vcFormatter.format(jsonObject, templateParams);
Map<String, String> vcSignerParams = new HashMap<>();
// TODO: Collate this into simpler APIs where just key-type is specified
if (VCSignAlgo.equals(SignatureAlg.RSA_SIGNATURE_SUITE)) {
vcSignerParams.put(KeyManagerConstants.VC_SIGN_ALGO,
SignatureAlg.RSA_SIGNATURE_SUITE);
vcSignerParams.put(KeyManagerConstants.PUBLIC_KEY_URL, hostedKey);
vcSignerParams.put(KeyManagerConstants.KEY_APP_ID, KeyManagerConstants.CERTIFY_MOCK_RSA);
vcSignerParams.put(KeyManagerConstants.KEY_REF_ID, KeyManagerConstants.EMPTY_REF_ID);
// Change it to PS256 as per --> https://w3c.github.io/vc-jws-2020/#dfn-jsonwebsignature2020
vcSignerParams.put(KeyManagerConstants.KEYMGR_SIGN_ALGO, JWSAlgorithm.RS256.getName());
} else if (VCSignAlgo.equals(SignatureAlg.ED25519_SIGNATURE_SUITE)) {
// https://w3c-ccg.github.io/lds-ed25519-2018/
vcSignerParams.put(KeyManagerConstants.VC_SIGN_ALGO, SignatureAlg.ED25519_SIGNATURE_SUITE);
vcSignerParams.put(KeyManagerConstants.PUBLIC_KEY_URL, hostedKey);
vcSignerParams.put(KeyManagerConstants.KEY_REF_ID, KeyManagerConstants.ED25519_REF_ID);
vcSignerParams.put(KeyManagerConstants.KEY_APP_ID, KeyManagerConstants.CERTIFY_MOCK_ED25519);
vcSignerParams.put(KeyManagerConstants.KEYMGR_SIGN_ALGO, JWSAlgorithm.EdDSA.getName());
}
vcResult = vcSigner.perform(templatedVC, vcSignerParams);
vcResult = vcSigner.perform(templatedVC);
} catch(DataProviderExchangeException e) {
throw new CertifyException(e.getErrorCode());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@
package io.mosip.certify.services;

public class KeyManagerConstants {
public static final String VC_SIGN_ALGO = "VCsignAlgo";
public static final String PUBLIC_KEY_URL = "publicKeyURL";
public static final String KEY_APP_ID = "keyAppId";
public static final String KEY_REF_ID = "keyRefId";
public static final String KEYMGR_SIGN_ALGO = "KeyMgrSignAlgo";
public static final String CERTIFY_MOCK_RSA = "CERTIFY_MOCK_RSA";
public static final String CERTIFY_MOCK_ED25519 = "CERTIFY_MOCK_ED25519";
public static final String ROOT_KEY = "ROOT";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@
import foundation.identity.jsonld.JsonLDException;
import foundation.identity.jsonld.JsonLDObject;
import info.weboftrust.ldsignatures.LdProof;
import info.weboftrust.ldsignatures.canonicalizer.URDNA2015Canonicalizer;
import info.weboftrust.ldsignatures.canonicalizer.Canonicalizer;
import io.mosip.certify.api.dto.VCResult;
import io.mosip.certify.api.spi.VCSigner;
import io.mosip.certify.core.constants.*;
import io.mosip.certify.core.exception.CertifyException;
import io.mosip.kernel.signature.dto.JWSSignatureRequestDto;
import io.mosip.kernel.signature.dto.JWTSignatureResponseDto;
import io.mosip.kernel.signature.service.SignatureService;
import io.mosip.certify.services.ldsigner.ProofSignatureStrategy;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
Expand All @@ -39,28 +37,26 @@
* These are the known external requirements:
* - the public key must be pre-hosted for the VC & should be available
* so long that VC should be verifiable
* - the VC should have a validFrom or issuanceDate in a specific UTC format
* - the VC should have a validFrom or issuanceDate in a specific UTC format,
* if missing it uses current time for proof creation timestamp.
*/
@Slf4j
@Service
public class KeymanagerLibSigner implements VCSigner {

@Autowired
SignatureService signatureService;
ProofSignatureStrategy signProps;
@Value("${mosip.certify.issuer.pub.key}")
private String hostedKey;

@Override
public VCResult<JsonLDObject> perform(String templatedVC, Map<String, String> keyMgrInput) {
public VCResult<JsonLDObject> perform(String templatedVC) {
// Can the below lines be done at Templating side itself ?
VCResult<JsonLDObject> VC = new VCResult<>();
JsonLDObject j = JsonLDObject.fromJson(templatedVC);
j.setDocumentLoader(null);
// NOTE: other aspects can be configured via keyMgrInput map
String validFrom;
String signatureAlgorithm = keyMgrInput.get(KeyManagerConstants.VC_SIGN_ALGO);
String publicKeyURL = keyMgrInput.get(KeyManagerConstants.PUBLIC_KEY_URL);
String keyAppId = keyMgrInput.get(KeyManagerConstants.KEY_APP_ID);
String keyRefId = keyMgrInput.get(KeyManagerConstants.KEY_REF_ID);
String keyManagerSignAlgo = keyMgrInput.get(KeyManagerConstants.KEYMGR_SIGN_ALGO);
if (j.getJsonObject().containsKey(VCDM1Constants.ISSUANCE_DATE)) {
validFrom = j.getJsonObject().get(VCDM1Constants.ISSUANCE_DATE).toString();
} else if (j.getJsonObject().containsKey(VCDM2Constants.VALID_FROM)){
Expand All @@ -76,42 +72,25 @@ public VCResult<JsonLDObject> perform(String templatedVC, Map<String, String> ke
.parse(validFrom,
DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN))
.atZone(ZoneId.systemDefault()).toInstant());
LdProof vcLdProof = LdProof.builder().defaultContexts(false).defaultTypes(false).type(signatureAlgorithm)
LdProof vcLdProof = LdProof.builder().defaultContexts(false).defaultTypes(false).type(signProps.getName())
.created(createDate).proofPurpose(VCDMConstants.ASSERTION_METHOD)
.verificationMethod(URI.create(publicKeyURL))
.verificationMethod(URI.create(hostedKey))
.build();
// 1. Canonicalize
URDNA2015Canonicalizer canonicalizer = new URDNA2015Canonicalizer();
Canonicalizer canonicalizer = signProps.getCanonicalizer();
byte[] vcSignBytes = null;
try {
vcSignBytes = canonicalizer.canonicalize(vcLdProof, j);
} catch (IOException | GeneralSecurityException | JsonLDException e) {
log.error("Error during canonicalization", e.getMessage());
throw new CertifyException("Error during canonicalization");
}

// 2. VC Sign
String vcEncodedData = Base64.getUrlEncoder().encodeToString(vcSignBytes);
JWSSignatureRequestDto payload = new JWSSignatureRequestDto();
payload.setDataToSign(vcEncodedData);
payload.setApplicationId(keyAppId);
payload.setReferenceId(keyRefId); // alg, empty = RSA
payload.setIncludePayload(false);
payload.setIncludeCertificate(false);
payload.setIncludeCertHash(true);
payload.setValidateJson(false);
payload.setB64JWSHeaderParam(false);
payload.setCertificateUrl("");
payload.setSignAlgorithm(keyManagerSignAlgo); // RSSignature2018 --> RS256, PS256, ES256
// TODO: Should this be a well defined Certify Exception for better comms b/w Certify & Support team?
JWTSignatureResponseDto jwsSignedData = signatureService.jwsSign(payload);
String sign = jwsSignedData.getJwtSignedData();
LdProof ldProofWithJWS = LdProof.builder().base(vcLdProof).defaultContexts(false)
.jws(sign).build();
String vcEncodedHash = Base64.getUrlEncoder().encodeToString(vcSignBytes);
String sign = signProps.getProof(vcEncodedHash);
LdProof ldProofWithJWS = signProps.buildProof(vcLdProof, sign);
ldProofWithJWS.addToJsonLDObject(j);
VC.setCredential(j);
return VC;
// TODO: Check if this is really a VC
// MOSIP ref: https://github.com/mosip/id-authentication/blob/master/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/impl/VciServiceImpl.java#L281
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.mosip.certify.services.ldsigner;

import com.danubetech.keyformats.jose.JWSAlgorithm;
import info.weboftrust.ldsignatures.LdProof;
import info.weboftrust.ldsignatures.canonicalizer.Canonicalizer;
import info.weboftrust.ldsignatures.canonicalizer.URDNA2015Canonicalizer;
import io.mosip.certify.core.constants.SignatureAlg;
import io.mosip.certify.services.KeyManagerConstants;
import io.mosip.kernel.signature.dto.JWSSignatureRequestDto;
import io.mosip.kernel.signature.dto.JWTSignatureResponseDto;
import io.mosip.kernel.signature.service.SignatureService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

/**
* Ed25519SignatureAlgorithm2018 as per https://w3c-ccg.github.io/lds-ed25519-2018/
*/
@Component
@ConditionalOnProperty(name = "mosip.certify.issuer.vc-sign-algo", havingValue = SignatureAlg.ED25519_SIGNATURE_SUITE)
public class Ed25519ProofSignature2018 implements ProofSignatureStrategy {
@Autowired
SignatureService signatureService;

Canonicalizer canonicalizer = new URDNA2015Canonicalizer();

@Override
public String getName() {
return SignatureAlg.ED25519_SIGNATURE_SUITE;
}

@Override
public Canonicalizer getCanonicalizer() {
return canonicalizer;
}

@Override
public String getProof(String vcEncodedHash) {
JWSSignatureRequestDto payload = new JWSSignatureRequestDto();
payload.setDataToSign(vcEncodedHash);
payload.setApplicationId(KeyManagerConstants.CERTIFY_MOCK_ED25519);
payload.setReferenceId(KeyManagerConstants.ED25519_REF_ID); // alg, empty = RSA
payload.setIncludePayload(false);
payload.setIncludeCertificate(false);
payload.setIncludeCertHash(true);
payload.setValidateJson(false);
payload.setB64JWSHeaderParam(false);
payload.setCertificateUrl("");
payload.setSignAlgorithm(JWSAlgorithm.EdDSA); // RSSignature2018 --> RS256, PS256, ES256
JWTSignatureResponseDto jwsSignedData = signatureService.jwsSign(payload);
return jwsSignedData.getJwtSignedData();
}

@Override
public LdProof buildProof(LdProof vcLdProof, String sign) {
return LdProof.builder().base(vcLdProof).defaultContexts(false)
.jws(sign).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.mosip.certify.services.ldsigner;

import com.danubetech.keyformats.jose.JWSAlgorithm;
import info.weboftrust.ldsignatures.LdProof;
import info.weboftrust.ldsignatures.canonicalizer.Canonicalizer;
import info.weboftrust.ldsignatures.canonicalizer.URDNA2015Canonicalizer;
import io.mosip.certify.core.constants.SignatureAlg;
import io.mosip.certify.services.KeyManagerConstants;
import io.mosip.kernel.signature.dto.SignRequestDtoV2;
import io.mosip.kernel.signature.dto.SignResponseDto;
import io.mosip.kernel.signature.service.SignatureServicev2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

/**
* Ed25519SignatureAlgorithm2020 as per
* https://www.w3.org/community/reports/credentials/CG-FINAL-di-eddsa-2020-20220724/
*/
@Component
@ConditionalOnProperty(name = "mosip.certify.issuer.vc-sign-algo", havingValue = SignatureAlg.ED25519_SIGNATURE_SUITE_2020)
public class Ed25519ProofSignature2020 implements ProofSignatureStrategy {

@Autowired
SignatureServicev2 signatureService;

Canonicalizer canonicalizer = new URDNA2015Canonicalizer();

@Override
public String getName() {
return SignatureAlg.ED25519_SIGNATURE_SUITE_2020;
}

@Override
public Canonicalizer getCanonicalizer() {
return canonicalizer;
}

@Override
public String getProof(String vcEncodedHash) {
SignRequestDtoV2 srd = new SignRequestDtoV2();
srd.setApplicationId(KeyManagerConstants.CERTIFY_MOCK_ED25519);
srd.setReferenceId(KeyManagerConstants.ED25519_REF_ID);
srd.setDataToSign(vcEncodedHash);
srd.setResponseEncodingFormat("base58btc");
srd.setSignAlgorithm(JWSAlgorithm.EdDSA);
SignResponseDto s = signatureService.signv2(srd);
return s.getSignature();
}

@Override
public LdProof buildProof(LdProof vcLdProof, String sign) {
return LdProof.builder().base(vcLdProof).defaultContexts(false)
.proofValue(sign).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.mosip.certify.services.ldsigner;

import info.weboftrust.ldsignatures.LdProof;
import info.weboftrust.ldsignatures.canonicalizer.Canonicalizer;

import java.util.Map;

/**
* ProofSignatureStrategy is a helper class for KeymanagerLibSigner
* to better deal with multiple signature algorithms for JSON-LD VCs.
*/
public interface ProofSignatureStrategy {
/**
* @return returns the name of the Algorithm
*/
String getName();

/**
* @return the Canonicalizer which will be used to Canonicalize the templated VC
*/
Canonicalizer getCanonicalizer();

/**
* getProof takes canonicalized VC hash and returns proof using a competent
* SignatureService implementation
* @param vcEncodedHash
* @return
*/
String getProof(String vcEncodedHash);
/**
* buildProof takes a proof String and attaches it to a proof object as per algorithm
* @param vcLdProof the proof object of the VC
* @param sign should be a string, can be a detached JWS, another proofString based on implementors choice
* @return
*/
LdProof buildProof(LdProof vcLdProof, String sign);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.mosip.certify.services.ldsigner;

import com.danubetech.keyformats.jose.JWSAlgorithm;
import info.weboftrust.ldsignatures.LdProof;
import info.weboftrust.ldsignatures.canonicalizer.Canonicalizer;
import info.weboftrust.ldsignatures.canonicalizer.URDNA2015Canonicalizer;
import io.mosip.certify.core.constants.SignatureAlg;
import io.mosip.certify.services.KeyManagerConstants;
import io.mosip.kernel.signature.dto.JWSSignatureRequestDto;
import io.mosip.kernel.signature.dto.JWTSignatureResponseDto;
import io.mosip.kernel.signature.service.SignatureService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

@Component
@ConditionalOnProperty(name = "mosip.certify.issuer.vc-sign-algo", havingValue = SignatureAlg.RSA_SIGNATURE_SUITE)
public class RsaProofSignature2018 implements ProofSignatureStrategy {
@Autowired
SignatureService signatureService;

Canonicalizer canonicalizer = new URDNA2015Canonicalizer();

@Override
public String getName() {
return SignatureAlg.RSA_SIGNATURE_SUITE;
}

@Override
public Canonicalizer getCanonicalizer() {
return canonicalizer;
}

@Override
public String getProof(String vcEncodedHash) {
String vcEncodedData = Base64.getUrlEncoder().encodeToString(vcEncodedHash.getBytes(StandardCharsets.UTF_8));
JWSSignatureRequestDto payload = new JWSSignatureRequestDto();
payload.setDataToSign(vcEncodedData);
payload.setApplicationId(KeyManagerConstants.CERTIFY_MOCK_RSA);
payload.setReferenceId(KeyManagerConstants.EMPTY_REF_ID); // alg, empty = RSA
payload.setIncludePayload(false);
payload.setIncludeCertificate(false);
payload.setIncludeCertHash(true);
payload.setValidateJson(false);
payload.setB64JWSHeaderParam(false);
payload.setCertificateUrl("");
payload.setSignAlgorithm(JWSAlgorithm.RS256); // RSSignature2018 --> RS256, PS256, ES256
JWTSignatureResponseDto jwsSignedData = signatureService.jwsSign(payload);
return jwsSignedData.getJwtSignedData();
}

@Override
public LdProof buildProof(LdProof vcLdProof, String sign) {
return LdProof.builder().base(vcLdProof).defaultContexts(false)
.jws(sign).build();
}
}
Loading

0 comments on commit 57bb461

Please sign in to comment.