From 559f4b2da1d0fee41b7448d13bb7e328a11adf51 Mon Sep 17 00:00:00 2001 From: Venkata Saidurga Polamraju Date: Wed, 30 Oct 2024 11:55:43 +0530 Subject: [PATCH] [ES-1806] Implemented kyc exchange method Signed-off-by: Venkata Saidurga Polamraju --- .../sunbirdrc/dto/KycExchangeResponseDto.java | 14 ++ .../SunbirdRCAuthenticationService.java | 128 +++++++++++++++++- .../main/resources/InsuranceCredential.json | 30 ++++ .../resources/application-local.properties | 35 +++++ .../SunbirdRCAuthenticaionServiceTest.java | 12 -- .../resources/application-local.properties | 35 +++++ 6 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/dto/KycExchangeResponseDto.java create mode 100644 sunbird-rc-plugin/src/main/resources/InsuranceCredential.json create mode 100644 sunbird-rc-plugin/src/main/resources/application-local.properties create mode 100644 sunbird-rc-plugin/src/test/resources/application-local.properties diff --git a/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/dto/KycExchangeResponseDto.java b/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/dto/KycExchangeResponseDto.java new file mode 100644 index 0000000..356b0a4 --- /dev/null +++ b/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/dto/KycExchangeResponseDto.java @@ -0,0 +1,14 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.esignet.plugin.sunbirdrc.dto; + +import lombok.Data; + +@Data +public class KycExchangeResponseDto { + + private String kyc; +} \ No newline at end of file diff --git a/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticationService.java b/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticationService.java index 8430891..10b77f1 100644 --- a/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticationService.java +++ b/sunbird-rc-plugin/src/main/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticationService.java @@ -7,14 +7,24 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.jwk.RSAKey; import io.mosip.esignet.api.dto.*; import io.mosip.esignet.api.exception.KycAuthException; import io.mosip.esignet.api.exception.KycExchangeException; import io.mosip.esignet.api.exception.SendOtpException; import io.mosip.esignet.api.spi.Authenticator; import io.mosip.esignet.api.util.ErrorConstants; -import io.mosip.esignet.sunbirdrc.integration.dto.RegistrySearchRequestDto; +import io.mosip.esignet.plugin.sunbirdrc.dto.RegistrySearchRequestDto; +import io.mosip.kernel.keymanagerservice.dto.AllCertificatesDataResponseDto; +import io.mosip.kernel.keymanagerservice.dto.CertificateDataResponseDto; +import io.mosip.kernel.keymanagerservice.service.KeymanagerService; +import io.mosip.kernel.signature.dto.JWTSignatureRequestDto; +import io.mosip.kernel.signature.dto.JWTSignatureResponseDto; +import io.mosip.kernel.signature.service.SignatureService; import lombok.extern.slf4j.Slf4j; +import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers; +import org.jose4j.jwe.JsonWebEncryption; +import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -23,6 +33,7 @@ import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.client.RestTemplate; @@ -57,12 +68,31 @@ public class SunbirdRCAuthenticationService implements Authenticator { @Value("${mosip.esignet.authenticator.sunbird-rc.kbi.entity-id-field}") private String entityIdField; + @Value("${mosip.mosip.ida.kyc.default-language:eng}") + private String defaultLanguage; + + @Value("${mosip.mock.ida.kyc.encrypt:false}") + private boolean encryptKyc; + + @Value("${mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-get-url}") + private String registryUrl; + @Autowired private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; + @Autowired + private KeymanagerService keymanagerService; + + @Autowired + private SignatureService signatureService; + + private static final Base64.Encoder urlSafeEncoder = Base64.getUrlEncoder().withoutPadding(); + + public static final String APPLICATION_ID = "OIDC_PARTNER"; + @PostConstruct public void initialize() throws KycAuthException { @@ -110,7 +140,92 @@ public KycAuthResult doKycAuth(@NotBlank String relyingPartyId, @NotBlank String @Override public KycExchangeResult doKycExchange(String relyingPartyId, String clientId, KycExchangeDto kycExchangeDto) throws KycExchangeException { - throw new KycExchangeException(ErrorConstants.NOT_IMPLEMENTED); + String kycToken=kycExchangeDto.getKycToken(); + Map responseRegistryMap; + try { + if (kycExchangeDto.getAcceptedClaims() == null) { + kycExchangeDto.setAcceptedClaims(new ArrayList<>()); + } + if (kycExchangeDto.getAcceptedClaims() != null) { + for (String acceptedClaim : kycExchangeDto.getAcceptedClaims()) { + if (!kycExchangeDto.getAcceptedClaims().contains(acceptedClaim)) { + kycExchangeDto.getAcceptedClaims().add(acceptedClaim); + } + } + } + responseRegistryMap =fetchRegistryObject(registryUrl+ kycToken); + if (responseRegistryMap == null) { + throw new KycExchangeException(ErrorConstants.DATA_EXCHANGE_FAILED); + } + Map kyc = buildKycDataBasedOnPolicy(responseRegistryMap, + kycExchangeDto.getAcceptedClaims(), kycExchangeDto.getClaimsLocales()); + String finalKyc = this.encryptKyc ? getJWE(relyingPartyId, signKyc(kyc)) : signKyc(kyc); + KycExchangeResult kycExchangeResult = new KycExchangeResult(); + kycExchangeResult.setEncryptedKyc(finalKyc); + return kycExchangeResult; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Map fetchRegistryObject(String entityUrl) throws KycExchangeException { + RequestEntity requestEntity = RequestEntity + .get(UriComponentsBuilder.fromUriString(entityUrl).build().toUri()).build(); + ResponseEntity> responseEntity = restTemplate.exchange(requestEntity, + new ParameterizedTypeReference>() {}); + if (responseEntity.getStatusCode().is2xxSuccessful() && responseEntity.getBody() != null) { + return responseEntity.getBody(); + }else { + log.error("Sunbird service is not running. Status Code: " ,responseEntity.getStatusCode()); + throw new KycExchangeException(ErrorConstants.DATA_EXCHANGE_FAILED); + } + } + + public Map buildKycDataBasedOnPolicy(Map credentialSubject, List claims, String[] locales) { + Map kyc = new HashMap<>(); + if (CollectionUtils.isEmpty(Arrays.asList(locales))) { + locales = Arrays.asList(defaultLanguage).toArray(new String[0]); + } + for (String key : claims) { + if (credentialSubject.containsKey(key)) { + kyc.put(key, String.valueOf(credentialSubject.get(key))); + } + } + return kyc; + } + + private String getJWE(String relyingPartyId, String signedJwt) throws Exception { + JsonWebEncryption jsonWebEncryption = new JsonWebEncryption(); + jsonWebEncryption.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256); + jsonWebEncryption.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM); + jsonWebEncryption.setPayload(signedJwt); + jsonWebEncryption.setContentTypeHeaderValue("JWT"); + RSAKey rsaKey = getRelyingPartyPublicKey(relyingPartyId); + jsonWebEncryption.setKey(rsaKey.toPublicKey()); + jsonWebEncryption.setKeyIdHeaderValue(rsaKey.getKeyID()); + return jsonWebEncryption.getCompactSerialization(); + } + + private RSAKey getRelyingPartyPublicKey(String relyingPartyId) throws KycExchangeException { + //TODO where to store relying-party public key + throw new KycExchangeException(ErrorConstants.DATA_EXCHANGE_FAILED); + } + + private String signKyc(Map kyc) throws JsonProcessingException { + String payload = objectMapper.writeValueAsString(kyc); + JWTSignatureRequestDto jwtSignatureRequestDto = new JWTSignatureRequestDto(); + jwtSignatureRequestDto.setApplicationId(APPLICATION_ID); + jwtSignatureRequestDto.setReferenceId(""); + jwtSignatureRequestDto.setIncludePayload(true); + jwtSignatureRequestDto.setIncludeCertificate(false); + jwtSignatureRequestDto.setDataToSign(b64Encode(payload)); + jwtSignatureRequestDto.setIncludeCertHash(false); + JWTSignatureResponseDto responseDto = signatureService.jwtSign(jwtSignatureRequestDto); + return responseDto.getJwtSignedData(); + } + + public static String b64Encode(String value) { + return urlSafeEncoder.encodeToString(value.getBytes(StandardCharsets.UTF_8)); } @Override @@ -126,7 +241,14 @@ public boolean isSupportedOtpChannel(String channel) { @Override public List getAllKycSigningCertificates() { - return new ArrayList<>(); + List certs = new ArrayList<>(); + AllCertificatesDataResponseDto allCertificatesDataResponseDto = keymanagerService.getAllCertificates(APPLICATION_ID, + Optional.empty()); + for (CertificateDataResponseDto dto : allCertificatesDataResponseDto.getAllCertificates()) { + certs.add(new KycSigningCertificateData(dto.getKeyId(), dto.getCertificateData(), + dto.getExpiryAt(), dto.getIssuedAt())); + } + return certs; } private KycAuthResult validateKnowledgeBasedAuth(String individualId, AuthChallenge authChallenge) throws KycAuthException { diff --git a/sunbird-rc-plugin/src/main/resources/InsuranceCredential.json b/sunbird-rc-plugin/src/main/resources/InsuranceCredential.json new file mode 100644 index 0000000..df4001a --- /dev/null +++ b/sunbird-rc-plugin/src/main/resources/InsuranceCredential.json @@ -0,0 +1,30 @@ +##Here we are injecting DateTool instance from java velocity library to set the expiry date +#set( $defaultLocale = $date.getLocale() ) +#set( $calenderNow = $date.getCalendar() ) +#set( $ISO8601DateTimeWithMillisUTC = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ) +## Adding no. of Days we want to set as expiry,here '5' represent Day and '30' represent no. of days +#set( $expiry =$calenderNow.add(5,30)) +{ +"@context": [ +"https://www.w3.org/2018/credentials/v1", +"https://holashchand.github.io/test_project/insurance-context.json" +], +"type": [ +"VerifiableCredential", +"InsuranceCredential" +], +"issuer": "${issuerId}", +"expirationDate": "${date.format($ISO8601DateTimeWithMillisUTC, $calenderNow, $defaultLocale)}", +"credentialSubject": { +"dob": "${dob}", +"gender": "${gender}", +"mobile": "${mobile}", +"benefits": #if($benefits)$benefits#else#set($benefits = '"[]"') $benefits#end, +"fullName": "${fullName}", +"email": "${email}", +"policyIssuedOn": "${policyIssuedOn}", +"policyExpiresOn": "${policyExpiresOn}", +"policyName": "${policyName}", +"policyNumber": "${policyNumber}" +} +} \ No newline at end of file diff --git a/sunbird-rc-plugin/src/main/resources/application-local.properties b/sunbird-rc-plugin/src/main/resources/application-local.properties new file mode 100644 index 0000000..80ad480 --- /dev/null +++ b/sunbird-rc-plugin/src/main/resources/application-local.properties @@ -0,0 +1,35 @@ +## eSignet mock plugin configuration +mosip.esignet.integration.scan-base-package=io.mosip.esignet.plugin.sunbirdrc +mosip.esignet.integration.authenticator=SunbirdRCAuthenticationService + +##---------------------------------Sunbird-RC Plugin Configurations------------------------------------------------------ + +mosip.esignet.authenticator.sunbird-rc.auth-factor.kbi.individual-id-field=policyNumber +mosip.esignet.authenticator.sunbird-rc.auth-factor.kbi.field-details={{"id":"policyNumber", "type":"text", "format":""},{"id":"fullName", "type":"text", "format":""},{"id":"dob", "type":"date", "format":"dd/mm/yyyy"}} +mosip.esignet.authenticator.sunbird-rc.auth-factor.kbi.registry-search-url=https://registry.dev1.mosip.net/api/v1/Insurance/search +mosip.esignet.authenticator.sunbird-rc.kbi.entity-id-field=osid + + +mosip.esignet.vciplugin.sunbird-rc.enable-psut-based-registry-search=false +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-search-url=http://demo-registry.sunbird-registry:8081/api/v1/Insurance/search +mosip.esignet.vciplugin.sunbird-rc.issue-credential-url=http://demo-registry-credentials-service.sunbird:3000/credentials/issue +mosip.esignet.vciplugin.sunbird-rc.supported-credential-types=LifeInsuranceCredential,InsuranceCredential,VehicleInsuranceCredential +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.static-value-map.issuerId=did:web:api.dev1.mosip.net:identity-service:8ebda1d0-665b-4bb7-abc7-d4bf56b6ee09 +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.template-url=${spring_config_url_env}/*/${active_profile_env}/${spring_config_label_env}/insurance-credential.json +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-get-url=https://registry.dev1.mosip.net/api/v1/Insurance/ +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-id=did:schema:e1f8df1d-c46c-483c-a882-3fc823666b9f +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-version=1.0.0 + +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.static-value-map.issuerId=did:web:api.dev1.mosip.net:identity-service:8ebda1d0-665b-4bb7-abc7-d4bf56b6ee09 +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.template-url=${spring_config_url_env}/*/${active_profile_env}/${spring_config_label_env}/life-insurance-credential.json +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-get-url=http://demo-registry.sunbird-registry:8081/api/v1/Insurance/ +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-id=did:schema:e1f8df1d-c46c-483c-a882-3fc823666b9f +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-version=1.0.0 +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-search-url=http://demo-registry.sunbird-registry:8081/api/v1/Insurance/search + +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.static-value-map.issuerId=did:web:holashchand.github.io:test_project:32b08ca7-9979-4f42-aacc-1d73f3ac5322 +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.template-url=${spring_config_url_env}/*/${active_profile_env}/${spring_config_label_env}/vehicle-insurance-credential.json +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.registry-get-url=http://10.3.148.107/registry/api/v1/Insurance/ +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.cred-schema-id=did:schema:e95a24f5-d65e-4b65-b497-e0671f8bd12d +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.cred-schema-version=1.0.0 +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.registry-search-url=http://10.3.148.107/registry/api/v1/Insurance/search \ No newline at end of file diff --git a/sunbird-rc-plugin/src/test/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticaionServiceTest.java b/sunbird-rc-plugin/src/test/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticaionServiceTest.java index a046643..41f9b29 100644 --- a/sunbird-rc-plugin/src/test/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticaionServiceTest.java +++ b/sunbird-rc-plugin/src/test/java/io/mosip/esignet/plugin/sunbirdrc/service/SunbirdRCAuthenticaionServiceTest.java @@ -260,18 +260,6 @@ public void doKycAuthWithOutChallenge_thenFail() { } } - - - @Test - public void doKycExchangeNotImplemented_thenFail() { - try{ - sunbirdRCAuthenticationService.doKycExchange("relyingPartyId","clientId",new KycExchangeDto()); - Assert.fail(); - } catch (KycExchangeException e) { - Assert.assertEquals(e.getErrorCode(), ErrorConstants.NOT_IMPLEMENTED); - } - } - @Test public void sendOtpNotImplemented_thenFail() { try{ diff --git a/sunbird-rc-plugin/src/test/resources/application-local.properties b/sunbird-rc-plugin/src/test/resources/application-local.properties new file mode 100644 index 0000000..80ad480 --- /dev/null +++ b/sunbird-rc-plugin/src/test/resources/application-local.properties @@ -0,0 +1,35 @@ +## eSignet mock plugin configuration +mosip.esignet.integration.scan-base-package=io.mosip.esignet.plugin.sunbirdrc +mosip.esignet.integration.authenticator=SunbirdRCAuthenticationService + +##---------------------------------Sunbird-RC Plugin Configurations------------------------------------------------------ + +mosip.esignet.authenticator.sunbird-rc.auth-factor.kbi.individual-id-field=policyNumber +mosip.esignet.authenticator.sunbird-rc.auth-factor.kbi.field-details={{"id":"policyNumber", "type":"text", "format":""},{"id":"fullName", "type":"text", "format":""},{"id":"dob", "type":"date", "format":"dd/mm/yyyy"}} +mosip.esignet.authenticator.sunbird-rc.auth-factor.kbi.registry-search-url=https://registry.dev1.mosip.net/api/v1/Insurance/search +mosip.esignet.authenticator.sunbird-rc.kbi.entity-id-field=osid + + +mosip.esignet.vciplugin.sunbird-rc.enable-psut-based-registry-search=false +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-search-url=http://demo-registry.sunbird-registry:8081/api/v1/Insurance/search +mosip.esignet.vciplugin.sunbird-rc.issue-credential-url=http://demo-registry-credentials-service.sunbird:3000/credentials/issue +mosip.esignet.vciplugin.sunbird-rc.supported-credential-types=LifeInsuranceCredential,InsuranceCredential,VehicleInsuranceCredential +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.static-value-map.issuerId=did:web:api.dev1.mosip.net:identity-service:8ebda1d0-665b-4bb7-abc7-d4bf56b6ee09 +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.template-url=${spring_config_url_env}/*/${active_profile_env}/${spring_config_label_env}/insurance-credential.json +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.registry-get-url=https://registry.dev1.mosip.net/api/v1/Insurance/ +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-id=did:schema:e1f8df1d-c46c-483c-a882-3fc823666b9f +mosip.esignet.vciplugin.sunbird-rc.credential-type.InsuranceCredential.cred-schema-version=1.0.0 + +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.static-value-map.issuerId=did:web:api.dev1.mosip.net:identity-service:8ebda1d0-665b-4bb7-abc7-d4bf56b6ee09 +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.template-url=${spring_config_url_env}/*/${active_profile_env}/${spring_config_label_env}/life-insurance-credential.json +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-get-url=http://demo-registry.sunbird-registry:8081/api/v1/Insurance/ +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-id=did:schema:e1f8df1d-c46c-483c-a882-3fc823666b9f +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.cred-schema-version=1.0.0 +mosip.esignet.vciplugin.sunbird-rc.credential-type.LifeInsuranceCredential.registry-search-url=http://demo-registry.sunbird-registry:8081/api/v1/Insurance/search + +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.static-value-map.issuerId=did:web:holashchand.github.io:test_project:32b08ca7-9979-4f42-aacc-1d73f3ac5322 +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.template-url=${spring_config_url_env}/*/${active_profile_env}/${spring_config_label_env}/vehicle-insurance-credential.json +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.registry-get-url=http://10.3.148.107/registry/api/v1/Insurance/ +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.cred-schema-id=did:schema:e95a24f5-d65e-4b65-b497-e0671f8bd12d +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.cred-schema-version=1.0.0 +mosip.esignet.vciplugin.sunbird-rc.credential-type.VehicleInsuranceCredential.registry-search-url=http://10.3.148.107/registry/api/v1/Insurance/search \ No newline at end of file