From dfa691da6e8122c00ee2ea7f280b05b4a6b73bdd Mon Sep 17 00:00:00 2001 From: Mohd Kaif Siddique Date: Wed, 4 Oct 2023 17:50:50 +0530 Subject: [PATCH 1/2] added create-vci-exchange api --- .../controller/AuthRequestController.java | 75 ++++++++++++++++--- .../src/main/resources/application.properties | 3 +- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java b/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java index c5c37461221..a45c3a29f27 100644 --- a/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java +++ b/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java @@ -47,6 +47,7 @@ import javax.crypto.NoSuchPaddingException; import javax.xml.bind.DatatypeConverter; +import io.mosip.testrig.authentication.demo.service.dto.*; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.bouncycastle.operator.OperatorCreationException; @@ -62,12 +63,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; @@ -90,9 +86,6 @@ import io.mosip.kernel.core.util.DateUtils; import io.mosip.kernel.core.util.HMACUtils2; import io.mosip.testrig.authentication.demo.service.controller.Encrypt.SplittedEncryptedData; -import io.mosip.testrig.authentication.demo.service.dto.CertificateChainResponseDto; -import io.mosip.testrig.authentication.demo.service.dto.EncryptionRequestDto; -import io.mosip.testrig.authentication.demo.service.dto.EncryptionResponseDto; import io.mosip.testrig.authentication.demo.service.helper.CertificateTypes; import io.mosip.testrig.authentication.demo.service.helper.KeyMgrUtil; import io.mosip.testrig.authentication.demo.service.helper.PartnerTypes; @@ -177,6 +170,8 @@ public class AuthRequestController { private static final String IDA_KYC_EXCHANGE_REQUEST_TEMPLATE = "ida.kycExchangeRequest.template"; + private static final String IDA_VCI_EXCHANGE_REQUEST_TEMPLATE = "ida.vciExchangeRequest.template"; + private static final String ID = "id"; private static final String CLASSPATH = "classpath"; @@ -574,6 +569,68 @@ public ResponseEntity createKycExchangeRequest(@RequestParam(name = ID, String.format(IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage(), IDENTITY)); } } + + @PostMapping(path = "/create-vci-exchange-request", consumes = MediaType.APPLICATION_JSON_VALUE, produces = { + MediaType.APPLICATION_JSON_VALUE }) + public ResponseEntity createVciExchangeRequest(@RequestParam(name = ID, required = true) @Nullable String id, + @RequestParam(name = ID_TYPE, required = false) @Nullable String idType, + @RequestParam(name = "Authtype", required = false) @Nullable String reqAuth, + @RequestParam(name = TRANSACTION_ID, required = false) @Nullable String transactionId, + @RequestParam(name = "requestTime", required = false) @Nullable String requestTime, + @RequestParam(name = "vcFormat", required = false) @Nullable String vcFormat, + @RequestParam(name = "credSubjectId", required = false)@Nullable String credSubjectId, + @RequestParam(name = "vcAuthToken", required = false)@Nullable String vcAuthToken, + @RequestParam(name = "keyFileNameByPartnerName", required = false)boolean keyFileNameByPartnerName, + @RequestParam(name = "partnerName", required = false)@Nullable String partnerName, + @RequestBody Map request, + @RequestParam(name = "certsDir", required = false) String certsDir, + @RequestParam(name = "moduleName", required = false) String moduleName) throws Exception { + String authRequestTemplate = environment.getProperty(IDA_VCI_EXCHANGE_REQUEST_TEMPLATE); + Map reqValues = new HashMap<>(); + + if (requestTime == null) { + requestTime = DateUtils.getUTCCurrentDateTimeString(environment.getProperty("datetime.pattern")); + } + + reqValues.put(ID, id); + reqValues.put("individualIdType", idType == null || idType.trim().length() == 0 ? IdType.UIN.toString() : idType); + reqValues.put(AUTH_TYPE, reqAuth); + reqValues.put(TIMESTAMP, requestTime); + reqValues.put(TXN, transactionId == null ? "1234567890" : transactionId); + reqValues.put(VER, environment.getProperty(IDA_API_VERSION)); + reqValues.put("vcFormat", vcFormat); + reqValues.put("credSubjectId", credSubjectId); + reqValues.put("vcAuthToken", vcAuthToken); + + StringWriter writer = new StringWriter(); + InputStream templateValue; + if (request != null && request.size() > 0) { + templateValue = templateManager + .merge(new ByteArrayInputStream(authRequestTemplate.getBytes(StandardCharsets.UTF_8)), reqValues); + + if (templateValue != null) { + IOUtils.copy(templateValue, writer, StandardCharsets.UTF_8); + String res = writer.toString(); + ObjectNode response = mapper.readValue(res.getBytes(), ObjectNode.class); + + HttpHeaders httpHeaders = new HttpHeaders(); + String responseStr = response.toString(); + + String rpSignature = signRequest(PartnerTypes.MISP, partnerName, keyFileNameByPartnerName, responseStr, certsDir, moduleName); + httpHeaders.add("signature", rpSignature); + return new ResponseEntity<>(responseStr, httpHeaders, HttpStatus.OK); + } else { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), String.format( + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage(), TEMPLATE)); + } + } else { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage(), IDENTITY)); + } + } + /** * this method is used to create the auth request. diff --git a/authentication-demo-service/src/main/resources/application.properties b/authentication-demo-service/src/main/resources/application.properties index cf22ff0f99e..0ca94feea60 100644 --- a/authentication-demo-service/src/main/resources/application.properties +++ b/authentication-demo-service/src/main/resources/application.properties @@ -20,6 +20,7 @@ ida-demo.api.version=v1 server.port = 8082 ida.authRequest.template={"consentObtained":true,"id":"mosip.identity.$authType","individualId":"$id","keyIndex":"string","request":"$encRequest","requestHMAC":"$encHmac","requestSessionKey":"$encSessionKey","requestTime":"$timestamp","transactionID":"$txn","version":"$ver","domainUri":"$domainUri","env":"$env","specVersion":"1.0","thumbprint":"$thumbprint"} ida.kycExchangeRequest.template={"consentObtained":["true"],"locales":["eng"],"id":"mosip.identity.$authType","individualId":"$id","individualIdType":"$individualIdType","kycToken":"$kycToken","respType":"$respType","metadata":"$request","requestTime":"$timestamp","transactionID":"$txn","version":"$ver"} +ida.vciExchangeRequest.template={"consentObtained":["true"],"locales":["eng"],"id":"mosip.identity.$authType","individualId":"$id","individualIdType":"$individualIdType","kycToken":"$kycToken","respType":"$respType","metadata":"$request","requestTime":"$timestamp","transactionID":"$txn","version":"$ver","vcAuthToken":"$vcAuthToken","credSubjectId":"$credSubjectId","vcAuthToken":"$vcAuthToken","vcFormat":"$vcFormat"} otpRequestTemplate={\ "id": "$reqId",\ "individualId": "$id",\ @@ -64,7 +65,7 @@ ida.api.version=1.0 javax.persistence.jdbc.driver=org.postgresql.Driver javax.persistence.jdbc.url=jdbc:postgresql://localhost:5432/ida_db javax.persistence.jdbc.user=postgres -javax.persistence.jdbc.password=admin +javax.persistence.jdbc.password=postgres # ********* Hibernate Properties *********** From cfe6a2c8cc7111ade458e1121405ae7469f3ed3e Mon Sep 17 00:00:00 2001 From: Mohd Kaif Siddique Date: Thu, 19 Oct 2023 19:56:11 +0530 Subject: [PATCH 2/2] added create-identity-key-binding api --- .../controller/AuthRequestController.java | 199 ++++++++++++++++-- .../demo/service/helper/KeyMgrUtil.java | 20 ++ 2 files changed, 203 insertions(+), 16 deletions(-) diff --git a/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java b/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java index a45c3a29f27..bb7430bb7b0 100644 --- a/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java +++ b/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/controller/AuthRequestController.java @@ -5,30 +5,21 @@ import static io.mosip.authentication.core.constant.IdAuthCommonConstants.UTF_8; -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableEntryException; +import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; @@ -47,12 +38,16 @@ import javax.crypto.NoSuchPaddingException; import javax.xml.bind.DatatypeConverter; +import io.mosip.kernel.core.util.StringUtils; import io.mosip.testrig.authentication.demo.service.dto.*; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; +import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.operator.OperatorCreationException; import org.jose4j.lang.JoseException; import org.json.JSONException; +import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; @@ -99,6 +94,7 @@ */ @RestController @Api(tags = { "Authentication Request Creation" }) +@Slf4j public class AuthRequestController { private static final String PHONE = "PHONE"; @@ -254,7 +250,7 @@ public ResponseEntity createAuthRequest(@RequestParam(name = ID, require @RequestParam(name = "moduleName", required = false) String moduleName) throws Exception { String authRequestTemplate = environment.getProperty(IDA_AUTH_REQUEST_TEMPLATE); Map reqValues = new HashMap<>(); - + if(isPreLTS) { reqValues.put(OTP, false); reqValues.put(DEMO, false); @@ -365,6 +361,177 @@ public ResponseEntity createAuthRequest(@RequestParam(name = ID, require String.format(IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage(), IDENTITY)); } } + + @PostMapping(path = "/create-identity-key-binding", consumes = MediaType.APPLICATION_JSON_VALUE, produces = { + MediaType.TEXT_PLAIN_VALUE }) + public ResponseEntity createIdentityKeyBinding(@RequestParam(name = ID, required = true) @Nullable String id, + @RequestParam(name = ID_TYPE, required = false) @Nullable String idType, + @RequestParam(name = "isKyc", required = false) @Nullable boolean isKyc, + @RequestParam(name = "isInternal", required = false) @Nullable boolean isInternal, + @RequestParam(name = "Authtype", required = false) @Nullable String reqAuth, + @RequestParam(name = TRANSACTION_ID, required = false) @Nullable String transactionId, + @RequestParam(name = "requestTime", required = false) @Nullable String requestTime, + @RequestParam(name = "isNewInternalAuth", required = false) @Nullable boolean isNewInternalAuth, + @RequestParam(name = "isPreLTS", required = false) @Nullable boolean isPreLTS, + @RequestParam(name = "signWithMisp", required = false) @Nullable boolean signWithMisp, + @RequestParam(name = "partnerName", required = true) String partnerName, + @RequestParam(name = "authFactorType", required = false) String authFactorType, + @RequestParam(name = "keyFileNameByPartnerName", required = false) boolean keyFileNameByPartnerName, + @RequestBody Map request, + @RequestParam(name = "certsDir", required = false) String certsDir, + @RequestParam(name = "moduleName", required = false) String moduleName) throws Exception { + String authRequestTemplate = environment.getProperty(IDA_AUTH_REQUEST_TEMPLATE); + Map reqValues = new HashMap<>(); + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); + keyPairGen.initialize(2048); + KeyPair pair = keyPairGen.generateKeyPair(); + PublicKey publicKey = pair.getPublic(); + PrivateKey privateKey = pair.getPrivate(); + + RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + // Extract key components + byte[] modulusBytes = rsaPublicKey.getModulus().toByteArray(); + byte[] exponentBytes = rsaPublicKey.getPublicExponent().toByteArray(); + // Encode key components as Base64 strings + String modulusBase64 = Base64.getEncoder().encodeToString(modulusBytes); + String exponentBase64 = Base64.getEncoder().encodeToString(exponentBytes); + // Create JSON object for the public key + JSONObject publicKeyJson = new JSONObject(); + publicKeyJson.put("alg", "RSA256"); + publicKeyJson.put("n", modulusBase64); + publicKeyJson.put("e", exponentBase64); + publicKeyJson.put("kty","RSA"); + + + // Create a JSON string with the public key + String json = "{\"identityKeyBinding\": {\"publicKeyJWK\": {\"additionalProp1\": {},\"additionalProp2\": {},\"additionalProp3\": {}},\"authFactorType\": \"string\"}}"; + + ObjectMapper mapper = new ObjectMapper(); + reqValues= mapper.readValue(json, Map.class); + + Map identityKeyBinding = (Map) reqValues.get("identityKeyBinding"); + identityKeyBinding.put("publicKeyJWK",publicKeyJson); + identityKeyBinding.put("authFactorType", StringUtils.isEmpty(authFactorType)?"WLA":authFactorType); + + + //storing the private kye to p12 file + String dirPath=keyMgrUtil.getKeysDirPath(certsDir,moduleName); + String caFilePath = dirPath + '/' + id+"private.p12"; + KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign); + keyMgrUtil.savePrivateKey(privateKey,publicKey, "CA-", "CA-", caFilePath, keyUsage, LocalDateTime.now(), LocalDateTime.now().plus(30,ChronoUnit.DAYS), partnerName); + + if(isPreLTS) { + reqValues.put(OTP, false); + reqValues.put(DEMO, false); + reqValues.put(BIO, false); + reqValues.put(PIN, false); + } + + if(isNewInternalAuth) { + isInternal = true; + } + + boolean needsEncryption = !isInternal || !isNewInternalAuth; + String keysDirPath = keyMgrUtil.getKeysDirPath(certsDir, moduleName); + + if(needsEncryption) { + reqValues.put("thumbprint", + digest(getCertificateThumbprint(encrypt.getCertificate(isInternal, keysDirPath)))); + } + + if (requestTime == null) { + requestTime = DateUtils.getUTCCurrentDateTimeString(environment.getProperty("datetime.pattern")); + + } + + if(!request.containsKey(TIMESTAMP)) { + request.put(TIMESTAMP, "");//Initializing. Setting value is done in further steps. + } + + idValuesMap(id, isKyc, isInternal, reqValues, transactionId, requestTime); + getAuthTypeMap(reqAuth, reqValues, request); + applyRecursively(request, TIMESTAMP, requestTime); + applyRecursively(request, DATE_TIME, requestTime); + applyRecursively(request, TRANSACTION_ID, transactionId); + + if(isKyc && signWithMisp) { + reqValues.put(AUTH_TYPE, "kycauth"); + } + + if(needsEncryption) { + if (reqValues.get(BIO) != null && Boolean.valueOf(reqValues.get(BIO).toString())) { + Object bioObj = request.get(BIOMETRICS); + if (bioObj instanceof List) { + List> encipheredBiometrics = encipherBiometrics(isInternal, requestTime, + transactionId, partnerName, keyFileNameByPartnerName, (List>) bioObj, certsDir, moduleName); + request.put(BIOMETRICS, encipheredBiometrics); + } + } + log.info("reqMap is {}",request); + encryptValuesMap(request, reqValues, isInternal, certsDir, moduleName); + } + + StringWriter writer = new StringWriter(); + InputStream templateValue; + if (request != null && request.size() > 0) { + templateValue = templateManager + .merge(new ByteArrayInputStream(authRequestTemplate.getBytes(StandardCharsets.UTF_8)), reqValues); + + log.info("reqValue is {}",reqValues); + if (templateValue != null) { + IOUtils.copy(templateValue, writer, StandardCharsets.UTF_8); + String requestString = writer.toString(); + if(!needsEncryption) { + Map resMap = mapper.readValue(requestString.getBytes(StandardCharsets.UTF_8), Map.class); + resMap.put("request", request); + resMap.put("requestHMAC", null); + resMap.put("requestSessionKey", null); + resMap.put("thumbprint", null); + + requestString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(resMap); + } + if (reqValues.containsKey(SECONDARY_LANG_CODE)) { + Map resMap = mapper.readValue(requestString.getBytes(StandardCharsets.UTF_8), Map.class); + resMap.put(SECONDARY_LANG_CODE, reqValues.get(SECONDARY_LANG_CODE)); + requestString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(resMap); + } + if(isPreLTS) { + Map requestMap = mapper.readValue(requestString.getBytes(StandardCharsets.UTF_8), Map.class); + Map requestedAuth = new HashMap<>(); + requestMap.put("individualIdType", idType == null || idType.trim().length() == 0 ? IdType.UIN.toString() : idType); + requestMap.put("requestedAuth", requestedAuth); + if(Boolean.valueOf(String.valueOf(reqValues.get(OTP)))) { + requestedAuth.put("otp", true); + } + if(Boolean.valueOf(String.valueOf(reqValues.get(DEMO)))) { + requestedAuth.put("demo", true); + } + if(Boolean.valueOf(String.valueOf(reqValues.get(BIO)))) { + requestedAuth.put("bio", true); + } + if(Boolean.valueOf(String.valueOf(reqValues.get(PIN)))) { + requestedAuth.put("pin", true); + } + requestString = mapper.writeValueAsString(requestMap); + } + HttpHeaders httpHeaders = new HttpHeaders(); + PartnerTypes partnerTypes = isKyc ? PartnerTypes.EKYC : PartnerTypes.RELYING_PARTY; + + String rpSignature = signRequest(signWithMisp ? PartnerTypes.MISP : partnerTypes, partnerName, keyFileNameByPartnerName, requestString, certsDir, moduleName); + httpHeaders.add("signature", rpSignature); + return new ResponseEntity<>(requestString, httpHeaders, HttpStatus.OK); + } else { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), String.format( + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage(), TEMPLATE)); + } + + } else { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage(), IDENTITY)); + } + } /** * this method is used to create the auth request. @@ -954,7 +1121,7 @@ public List> encipherBiometrics( Map digitalIdMap = (Map) digitalId; String digitalIdStr = mapper.writeValueAsString(digitalIdMap); String signedDititalId; - if(!isInternal) { + if(!isInternal) { //String signedDititalId = jWSSignAndVerifyController.sign(digitalIdStr, true); signedDititalId = jWSSignAndVerifyController.sign(digitalIdStr, true, true, false, null, keysDirPath, PartnerTypes.FTM, partnerName, keyFileNameByPartnerName); diff --git a/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/helper/KeyMgrUtil.java b/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/helper/KeyMgrUtil.java index 22996dc8ffe..c9c14802e27 100644 --- a/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/helper/KeyMgrUtil.java +++ b/authentication-demo-service/src/main/java/io/mosip/testrig/authentication/demo/service/helper/KeyMgrUtil.java @@ -222,6 +222,26 @@ private PrivateKeyEntry generateKeys(PrivateKey signKey, String signCertType, St return new PrivateKeyEntry(keyPair.getPrivate(), chain); } + public void savePrivateKey(PrivateKey privateKey,PublicKey publicKey, String signCertType, String certType, String p12FilePath, KeyUsage keyUsage, + LocalDateTime dateTime, LocalDateTime dateTimeExp, String organization) throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, OperatorCreationException { + + X509Certificate signCert = generateX509Certificate(privateKey, publicKey, signCertType, certType, keyUsage, dateTime, dateTimeExp, organization); + X509Certificate[] chain = new X509Certificate[]{signCert}; + PrivateKeyEntry privateKeyEntry = new PrivateKeyEntry(privateKey, chain); + KeyStore keyStore = KeyStore.getInstance(KEY_STORE); + keyStore.load(null, getP12Pass()); + keyStore.setEntry("privatekey", privateKeyEntry, new PasswordProtection (getP12Pass())); + Path parentPath = Paths.get(p12FilePath).getParent(); + if (parentPath != null && !Files.exists(parentPath)) { + Files.createDirectories(parentPath); + } + + OutputStream outputStream = new FileOutputStream(p12FilePath); + keyStore.store(outputStream, getP12Pass()); + outputStream.flush(); + outputStream.close(); + } + private X509Certificate generateX509Certificate(PrivateKey signPrivateKey, PublicKey publicKey, String signCertType, String certType, KeyUsage keyUsage, LocalDateTime dateTime, LocalDateTime dateTimeExp, String organization) throws OperatorCreationException, NoSuchAlgorithmException, CertIOException, CertificateException {