diff --git a/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java b/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java index 0d5dfe12..b4ff518e 100644 --- a/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java +++ b/certify-core/src/main/java/io/mosip/certify/core/constants/ErrorConstants.java @@ -25,4 +25,5 @@ public class ErrorConstants { public static final String UNSUPPORTED_OPENID_VERSION = "unsupported_openid4vci_draft_version"; public static final String INVALID_TEMPLATE_ID = "template_with_id_not_found"; public static final String EMPTY_TEMPLATE_CONTENT = "empty_template_content"; + public static final String JSON_TEMPLATING_ERROR = "json_templating_error"; } diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCModifier.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCModifier.java new file mode 100644 index 00000000..acf2c9b0 --- /dev/null +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCModifier.java @@ -0,0 +1,18 @@ +package io.mosip.certify.api.spi; + +import org.json.JSONObject; + +/** + * VCModifier is a modifier which takes @param templateInput and + * returns a modified VC as per configuration. + * + * Some implementations include + * - add an id which is a UUID + * + * Future possible implementations: + * - Support for SD-JWT + * - Support for additional validations + */ +public interface VCModifier { + JSONObject perform(String templateInput); +} diff --git a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java index 174105a9..b1070501 100644 --- a/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java +++ b/certify-integration-api/src/main/java/io/mosip/certify/api/spi/VCSigner.java @@ -1,10 +1,11 @@ package io.mosip.certify.api.spi; import io.mosip.certify.api.dto.VCResult; +import org.json.JSONObject; /** * VCSigner can sign any VC provided a vcHash & Signer inputs */ public interface VCSigner { - VCResult perform(String templatedVC); + VCResult perform(JSONObject templatedVC); } diff --git a/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java b/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java index 0e0a8d6a..1b78200f 100644 --- a/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java +++ b/certify-service/src/main/java/io/mosip/certify/services/CertifyIssuanceServiceImpl.java @@ -62,6 +62,9 @@ public class CertifyIssuanceServiceImpl implements VCIssuanceService { @Autowired private VCFormatter vcFormatter; + @Autowired + private VCModifier vcModifier; + @Autowired private VCSigner vcSigner; @@ -159,7 +162,9 @@ private VCResult getVerifiableCredential(CredentialRequest credentialRequest, templateParams.put(VelocityTemplatingConstants.SVG_TEMPLATE, svg); } String templatedVC = vcFormatter.format(jsonObject, templateParams); - vcResult = vcSigner.perform(templatedVC); + JSONObject credential = vcModifier.perform(templatedVC); + // vcFormatter.format() + vcResult = vcSigner.perform(credential); } catch(DataProviderExchangeException e) { throw new CertifyException(e.getErrorCode()); } diff --git a/certify-service/src/main/java/io/mosip/certify/services/ConfigurableJSONLDvcModifier.java b/certify-service/src/main/java/io/mosip/certify/services/ConfigurableJSONLDvcModifier.java new file mode 100644 index 00000000..b5d8caf6 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/ConfigurableJSONLDvcModifier.java @@ -0,0 +1,35 @@ +package io.mosip.certify.services; + +import io.mosip.certify.api.spi.VCModifier; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +@Slf4j +@ConditionalOnProperty(name = "mosip.certify.issuer.modifier.enabled", havingValue = "AddID") +public class ConfigurableJSONLDvcModifier implements VCModifier { + @Value("${mosip.certify.issuer.id.url:https://example.com/}") + private String url; + // TODO: Add support for more configurable "AddOns" to update the VC later + @Override + public JSONObject perform(String templateInput) { + JSONObject j; + try { + j = new JSONObject(templateInput); + j.put("id", url + UUID.randomUUID()); + return j; + } catch (JSONException e) { + log.error("Received JSON: " + templateInput); + log.error(e.getMessage()); + throw new CertifyException(ErrorConstants.JSON_TEMPLATING_ERROR); + } + } +} diff --git a/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java b/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java index f513f223..268f0971 100644 --- a/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java +++ b/certify-service/src/main/java/io/mosip/certify/services/KeymanagerLibSigner.java @@ -15,6 +15,7 @@ import io.mosip.certify.core.exception.CertifyException; import io.mosip.certify.services.ldsigner.ProofSignatureStrategy; import lombok.extern.slf4j.Slf4j; +import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -50,17 +51,17 @@ public class KeymanagerLibSigner implements VCSigner { private String hostedKey; @Override - public VCResult perform(String templatedVC) { + public VCResult perform(JSONObject c) { // Can the below lines be done at Templating side itself ? + JsonLDObject cred = JsonLDObject.fromJson(c.toString()); VCResult VC = new VCResult<>(); - JsonLDObject j = JsonLDObject.fromJson(templatedVC); - j.setDocumentLoader(null); + cred.setDocumentLoader(null); // NOTE: other aspects can be configured via keyMgrInput map String validFrom; - if (j.getJsonObject().containsKey(VCDM1Constants.ISSUANCE_DATE)) { - validFrom = j.getJsonObject().get(VCDM1Constants.ISSUANCE_DATE).toString(); - } else if (j.getJsonObject().containsKey(VCDM2Constants.VALID_FROM)){ - validFrom = j.getJsonObject().get(VCDM2Constants.VALID_FROM).toString(); + if (cred.getJsonObject().containsKey(VCDM1Constants.ISSUANCE_DATE)) { + validFrom = cred.getJsonObject().get(VCDM1Constants.ISSUANCE_DATE).toString(); + } else if (cred.getJsonObject().containsKey(VCDM2Constants.VALID_FROM)){ + validFrom = cred.getJsonObject().get(VCDM2Constants.VALID_FROM).toString(); } else { validFrom = ZonedDateTime.now(ZoneOffset.UTC) .format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)); @@ -80,7 +81,7 @@ public VCResult perform(String templatedVC) { Canonicalizer canonicalizer = signProps.getCanonicalizer(); byte[] vcSignBytes = null; try { - vcSignBytes = canonicalizer.canonicalize(vcLdProof, j); + vcSignBytes = canonicalizer.canonicalize(vcLdProof, cred); } catch (IOException | GeneralSecurityException | JsonLDException e) { log.error("Error during canonicalization", e.getMessage()); throw new CertifyException("Error during canonicalization"); @@ -88,8 +89,8 @@ public VCResult perform(String templatedVC) { String vcEncodedHash = Base64.getUrlEncoder().encodeToString(vcSignBytes); String sign = signProps.getProof(vcEncodedHash); LdProof ldProofWithJWS = signProps.buildProof(vcLdProof, sign); - ldProofWithJWS.addToJsonLDObject(j); - VC.setCredential(j); + ldProofWithJWS.addToJsonLDObject(cred); + VC.setCredential(cred); return 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 } diff --git a/certify-service/src/main/java/io/mosip/certify/services/NoOpVCModifier.java b/certify-service/src/main/java/io/mosip/certify/services/NoOpVCModifier.java new file mode 100644 index 00000000..a38b1fa1 --- /dev/null +++ b/certify-service/src/main/java/io/mosip/certify/services/NoOpVCModifier.java @@ -0,0 +1,27 @@ +package io.mosip.certify.services; + +import io.mosip.certify.api.spi.VCModifier; +import io.mosip.certify.core.constants.ErrorConstants; +import io.mosip.certify.core.exception.CertifyException; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@ConditionalOnProperty(name = "mosip.certify.issuer.modifier.enabled", havingValue = "NoOp") +public class NoOpVCModifier implements VCModifier { + @Override + public JSONObject perform(String templateInput) { + try { + JSONObject j = new JSONObject(templateInput); + return j; + } catch (JSONException e) { + log.error("Received JSON: " + templateInput); + log.error(e.getMessage()); + throw new CertifyException(ErrorConstants.JSON_TEMPLATING_ERROR); + } + } +} diff --git a/certify-service/src/main/resources/application-local.properties b/certify-service/src/main/resources/application-local.properties index bc0d2ac2..7fb56103 100644 --- a/certify-service/src/main/resources/application-local.properties +++ b/certify-service/src/main/resources/application-local.properties @@ -18,9 +18,10 @@ mosip.certify.domain.url=http://localhost:8090 mosipbox.public.url=https://localhost:8090 mosip.certify.authorization.url=http://localhost:8088 mosip.certify.discovery.issuer-id=${mosip.certify.domain.url}${server.servlet.path} -mosip.certify.issuer.vc-sign-algo=Ed25519Signature2018 +mosip.certify.issuer.vc-sign-algo=Ed25519Signature2020 mosip.certify.issuer=CertifyIssuer - +mosip.certify.issuer.modifier.enabled=NoOp +mosip.certify.issuer.svg.template.id= ##--------------change this later--------------------------------- mosip.certify.supported.jwt-proof-alg={'RS256','PS256','ES256'} @@ -31,7 +32,6 @@ mosip.certify.authn.filter-urls={ '${server.servlet.path}/issuance/credential', mosip.certify.authn.issuer-uri=http://localhost:8088/v1/esignet mosip.certify.authn.jwk-set-uri=http://localhost:8088/v1/esignet/oauth/.well-known/jwks.json mosip.certify.authn.allowed-audiences={ '${mosip.certify.domain.url}${server.servlet.path}/issuance/credential', 'http://localhost:8088/v1/esignet/vci/credential' } -mosip.certify.dataprovider.types={'MockVerifiableCredential','StudentCredential','UniversityCredential'} mosip.certify.key-values={\ 'vd12' : {\ 'credential_issuer': '${mosip.certify.identifier}', \ @@ -107,7 +107,7 @@ mosip.certify.key-values={\ 'credential_signing_alg_values_supported': {'Ed25519Signature2020'},\ 'proof_types_supported': {'jwt': {'proof_signing_alg_values_supported': {'RS256', 'PS256'}}},\ 'credential_definition': {\ - 'type': {'VerifiableCredential','MockVerifiableCredential'},\ + 'type': {'VerifiableCredential','FarmerCredential'},\ 'credentialSubject': {\ 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ @@ -135,7 +135,7 @@ mosip.certify.key-values={\ 'credential_signing_alg_values_supported': {'Ed25519Signature2020'},\ 'proof_types_supported': {'jwt': {'proof_signing_alg_values_supported': {'RS256', 'ES256'}}},\ 'credential_definition': {\ - 'type': {'VerifiableCredential', 'MockCredential'},\ + 'type': {'VerifiableCredential', 'FarmerCredential'},\ 'credentialSubject': {\ 'fullName': {'display': {{'name': 'Name','locale': 'en'}}}, \ 'mobile': {'display': {{'name': 'Phone Number','locale': 'en'}}},\ @@ -208,8 +208,8 @@ mosip.certify.mock.authenticator.get-identity-url=http://localhost:8082/v1/mock- mosip.certify.mock.vciplugin.issuer.key-cert="dummy-issuer-cert" # details of VC issuer's public key & controller for DataProvider plugin -mosip.certify.issuer.pub.key=https://vharsh.github.io/DID/mock-rsa.json -mosip.certify.issuer.uri=https://vharsh.github.io/DID/mock-rsac.json +mosip.certify.issuer.pub.key=did:web:vharsh.github.io:DID:harsh#key-0 +mosip.certify.issuer.uri=did:web:vharsh.github.io:DID:harsh ## ---------------------------------------- Cache configuration -------------------------------------------------------- diff --git a/certify-service/src/test/java/io/mosip/certify/services/ConfigurableJSONLDvcModifierTest.java b/certify-service/src/test/java/io/mosip/certify/services/ConfigurableJSONLDvcModifierTest.java new file mode 100644 index 00000000..d21f6ca5 --- /dev/null +++ b/certify-service/src/test/java/io/mosip/certify/services/ConfigurableJSONLDvcModifierTest.java @@ -0,0 +1,23 @@ +package io.mosip.certify.services; + +import lombok.SneakyThrows; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +import java.net.URI; + +import static org.junit.jupiter.api.Assertions.*; + +class ConfigurableJSONLDvcModifierTest { + ConfigurableJSONLDvcModifier modifier = new ConfigurableJSONLDvcModifier(); + @SneakyThrows + @Test + void perform() { + JSONObject json = new JSONObject(); + json.put("item", "apple"); + JSONObject actual = modifier.perform(json.toString()); + // assert that the id field is a valid URI :: as per the spec + assertDoesNotThrow(() -> URI.create(actual.get("id").toString())); + assertTrue(actual.has("item")); + } +} \ No newline at end of file diff --git a/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java b/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java index c0fff3fc..1a10f296 100644 --- a/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java +++ b/certify-service/src/test/java/io/mosip/certify/services/KeymanagerLibSignerTest.java @@ -4,10 +4,10 @@ import info.weboftrust.ldsignatures.LdProof; import info.weboftrust.ldsignatures.canonicalizer.URDNA2015Canonicalizer; import io.mosip.certify.api.dto.VCResult; -import io.mosip.certify.core.constants.VCDMConstants; import io.mosip.certify.services.ldsigner.ProofSignatureStrategy; -import io.mosip.certify.services.ldsigner.RsaProofSignature2018; import io.mosip.kernel.signature.dto.JWTSignatureResponseDto; +import org.json.JSONException; +import org.json.JSONObject; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -19,10 +19,8 @@ import org.springframework.test.util.ReflectionTestUtils; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.sql.Ref; import java.util.Map; @RunWith(MockitoJUnitRunner.class) @@ -71,9 +69,9 @@ public void setup() { } @Test - public void testPerformSuccess_VC2() { + public void testPerformSuccess_VC2() throws JSONException { // Mock Templated VC and Key Manager Input - String VCs[] = new String[]{VC_1, VC_2}; + String[] VCs = new String[]{VC_1, VC_2}; for (String templatedVC : VCs) { // Prepare a FakeSignature2018 implementation JWTSignatureResponseDto jwsSignedData = new JWTSignatureResponseDto(); @@ -84,14 +82,13 @@ public void testPerformSuccess_VC2() { when(signProps.getProof(anyString())).thenReturn("fake-jws-proof"); LdProof l = LdProof.builder().jws("fake-jws-proof").type("FakeSignature2018").proofPurpose("assertionMethod").build(); when(signProps.buildProof(any(), any())).thenReturn(l); - + JSONObject json = new JSONObject(templatedVC); // invoke - VCResult vcResult = signer.perform(templatedVC); + VCResult vcResult = signer.perform(json); // test assert vcResult != null; JsonLDObject credential = vcResult.getCredential(); - Assert.assertNotNull(credential.getJsonObject().containsKey("proof")); Map proof = (Map) credential.getJsonObject().get("proof"); Assert.assertEquals("fake-jws-proof", proof.get("jws")); } diff --git a/certify-service/src/test/resources/application-test.properties b/certify-service/src/test/resources/application-test.properties index 0c6aeaa1..530d7325 100644 --- a/certify-service/src/test/resources/application-test.properties +++ b/certify-service/src/test/resources/application-test.properties @@ -8,7 +8,8 @@ mosip.certify.integration.scan-base-package=io.mosip.certify mosip.certify.integration.audit-plugin=TestAuditPlugin mosip.certify.integration.vci-plugin=TestVCIPluginImpl mosip.certify.issuer=PluginIssuer -mosip.certify.issuer.vc-sign-algo=Ed25519Signature2018 +mosip.certify.issuer.vc-sign-algo=Ed25519Signature2020 +mosip.certify.issuer.modifier.enabled=NoOp # mosip.certify.issuer.vc-sign-algo:Ed25519Signature2018 for CertifyIssuer test ## ------------------------------------------ Discovery openid-configuration ------------------------------------------- diff --git a/db_scripts/mosip_certify/dml/certify-key_policy_def.csv b/db_scripts/mosip_certify/dml/certify-key_policy_def.csv index fc01ef11..d9b62bfa 100644 --- a/db_scripts/mosip_certify/dml/certify-key_policy_def.csv +++ b/db_scripts/mosip_certify/dml/certify-key_policy_def.csv @@ -4,3 +4,4 @@ CERTIFY_SERVICE,1095,60,NA,TRUE,mosipadmin,now() CERTIFY_PARTNER,1095,60,NA,TRUE,mosipadmin,now() CERTIFY_MOCK_RSA,1095,60,NA,TRUE,mosipadmin,now() CERTIFY_MOCK_ED25519,1095,60,NA,TRUE,mosipadmin,now() +BASE,730,60,NA,TRUE,mosipadmin,now()