diff --git a/signup-service/src/main/java/io/mosip/signup/helper/CryptoHelper.java b/signup-service/src/main/java/io/mosip/signup/helper/CryptoHelper.java index 0ab5a883..a405d442 100644 --- a/signup-service/src/main/java/io/mosip/signup/helper/CryptoHelper.java +++ b/signup-service/src/main/java/io/mosip/signup/helper/CryptoHelper.java @@ -1,39 +1,54 @@ package io.mosip.signup.helper; import io.mosip.esignet.core.util.IdentityProviderUtil; +import io.mosip.kernel.core.util.UUIDUtils; import io.mosip.signup.exception.SignUpException; import io.mosip.signup.services.CacheUtilService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import static io.mosip.kernel.core.util.UUIDUtils.NAMESPACE_OID; + @Slf4j @Component public class CryptoHelper { - private static final String AES_TRANSFORMATION = "AES/CFB/PKCS5Padding"; - public static final String CACHE_KEY = "aes"; + public static final String ALIAS_CACHE_KEY = "CURRENT_ACTIVE_ALIAS"; + + @Value("${mosip.signup.cache.symmetric-algorithm-name}") + private String symmetricAlgorithm; + + @Value("${mosip.signup.cache.symmetric-key.algorithm-name:AES}") + private String symmetricKeyAlgorithm; + + @Value("${mosip.signup.cache.symmetric-key.size:256}") + private int symmetricKeySize; @Autowired private CacheUtilService cacheUtilService; - public String symmetricEncrypt(String transactionId, String data, SecretKey secretKey) { + public String symmetricEncrypt(String data) { try { - Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); + + String keyAlias = getActiveKeyAlias(); + SecretKey secretKey = getSecretKey(keyAlias); + + Cipher cipher = Cipher.getInstance(symmetricAlgorithm); byte[] initializationVector = IdentityProviderUtil.generateSalt(cipher.getBlockSize()); byte[] secretDataBytes = data.getBytes(StandardCharsets.UTF_8); cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initializationVector)); byte[] encryptedBytes = cipher.doFinal(secretDataBytes, 0, secretDataBytes.length); - String keyAlias = getKeyAlias(transactionId); byte[] keyAliasBytes = keyAlias.getBytes(); - cacheUtilService.setSecretKeyBasedOnAlias(keyAlias, IdentityProviderUtil.b64Encode(secretKey.getEncoded())); byte[] output = new byte[cipher.getOutputSize(secretDataBytes.length)+cipher.getBlockSize()+keyAliasBytes.length]; System.arraycopy(encryptedBytes, 0, output, 0, encryptedBytes.length); @@ -49,12 +64,12 @@ public String symmetricEncrypt(String transactionId, String data, SecretKey secr public String symmetricDecrypt(String encryptedData) { try { - Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); + Cipher cipher = Cipher.getInstance(symmetricAlgorithm); byte[] data = IdentityProviderUtil.b64Decode(encryptedData); - byte[] keyAlias = Arrays.copyOfRange(data, data.length - 10, data.length); - byte[] iv = Arrays.copyOfRange(data, data.length-(cipher.getBlockSize()+10), data.length-10); - byte[] encryptedBytes = Arrays.copyOfRange(data, 0, data.length-(cipher.getBlockSize()+10)); + byte[] keyAlias = Arrays.copyOfRange(data, data.length-36, data.length); + byte[] iv = Arrays.copyOfRange(data, data.length-(cipher.getBlockSize()+36), data.length-36); + byte[] encryptedBytes = Arrays.copyOfRange(data, 0, data.length-(cipher.getBlockSize()+36)); String encodedSecretKey = cacheUtilService.getSecretKey(new String(keyAlias)); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(IdentityProviderUtil.b64Decode(encodedSecretKey), "AES"), @@ -67,23 +82,31 @@ public String symmetricDecrypt(String encryptedData) { } - public SecretKey getSecretKey() { - String encodedSecretKey = cacheUtilService.getSecretKey(); + public SecretKey getSecretKey(String alias) { + String encodedSecretKey = cacheUtilService.getSecretKey(alias); + return new SecretKeySpec(IdentityProviderUtil.b64Decode(encodedSecretKey), "AES"); + } + + private String getActiveKeyAlias() { + String alias = cacheUtilService.getActiveKeyAlias(); + if(alias != null) + return alias; + + log.debug("No active alias found, generating new alias and AES key."); + alias = UUIDUtils.getUUID(NAMESPACE_OID, "signup-service").toString(); + generateSecretKey(alias); + return alias; + } + + private void generateSecretKey(String alias) { try { - if(encodedSecretKey == null) { - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(256); - cacheUtilService.setSecretKey(CACHE_KEY, IdentityProviderUtil.b64Encode(keyGenerator.generateKey().getEncoded())); - encodedSecretKey = cacheUtilService.getSecretKey(); - } - return new SecretKeySpec(IdentityProviderUtil.b64Decode(encodedSecretKey), "AES"); - } catch (Exception e) { - log.error("Error getting secret key", e); + KeyGenerator keyGenerator = KeyGenerator.getInstance(symmetricKeyAlgorithm); + keyGenerator.init(symmetricKeySize); + cacheUtilService.setSecretKey(alias, IdentityProviderUtil.b64Encode(keyGenerator.generateKey().getEncoded())); + cacheUtilService.setActiveKeyAlias(ALIAS_CACHE_KEY, alias); + } catch (NoSuchAlgorithmException e) { + log.error("Error generating secret key", e); throw new SignUpException("crypto_error"); } } - - private String getKeyAlias(String transactionId) { - return transactionId.substring(transactionId.length()-10); - } } diff --git a/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java b/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java index cd76a887..e4f1c08e 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java +++ b/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java @@ -50,9 +50,9 @@ public String setSecretKey(String key, String secretKey) { return secretKey; } - @Cacheable(value = SignUpConstants.KEY_ALIAS, key = "#alias") - public String setSecretKeyBasedOnAlias(String alias, String secretKey) { - return secretKey; + @Cacheable(value = SignUpConstants.KEY_ALIAS, key = "#key") + public String setActiveKeyAlias(String key, String alias) { + return alias; } //---Getter--- @@ -75,11 +75,11 @@ public boolean isIdentifierBlocked(String identifier) { return value == null ? false : true; } - public String getSecretKey() { - return cacheManager.getCache(SignUpConstants.KEYSTORE).get(CryptoHelper.CACHE_KEY, String.class); + public String getSecretKey(String keyAlias) { + return cacheManager.getCache(SignUpConstants.KEYSTORE).get(keyAlias, String.class); } - public String getSecretKey(String alias) { - return cacheManager.getCache(SignUpConstants.KEY_ALIAS).get(alias, String.class); + public String getActiveKeyAlias() { + return cacheManager.getCache(SignUpConstants.KEY_ALIAS).get(CryptoHelper.ALIAS_CACHE_KEY, String.class); } } diff --git a/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java b/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java index 063377c1..4688cec7 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java +++ b/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java @@ -405,9 +405,7 @@ private void checkActiveIdentityExists(String transactionId, } //set UIN in the cache to be further used for update UIN endpoint - SecretKey secretKey = cryptoHelper.getSecretKey(); - registrationTransaction.setUin(cryptoHelper.symmetricEncrypt(transactionId, - restResponseWrapper.getResponse().getIdentity().getUIN(), secretKey)); + registrationTransaction.setUin(cryptoHelper.symmetricEncrypt(restResponseWrapper.getResponse().getIdentity().getUIN())); } diff --git a/signup-service/src/main/resources/application-default.properties b/signup-service/src/main/resources/application-default.properties index 553dc9f6..8a44e091 100644 --- a/signup-service/src/main/resources/application-default.properties +++ b/signup-service/src/main/resources/application-default.properties @@ -37,6 +37,7 @@ mosip.signup.supported.challenge.otp.length=6 ## ------------------------------------- Cache configuration ----------------------------------------------------------- +mosip.signup.cache.symmetric-algorithm-name=AES/CFB/PKCS5Padding spring.cache.type=simple #spring.cache.type=redis @@ -50,13 +51,16 @@ mosip.esignet.cache.size={'challenge_generated': 200, \ 'challenge_verified': 200,\ 'status_check': 200,\ 'blocked_identifier':2000,\ - 'keystore' : 5, \ - 'key_alias' : 200 } + 'keystore' : 10, \ + 'key_alias' : 1 } + +## Note: keystore TTL should be more than the key_alias cache TTL. +## So that key rotation happens before the actual key is removed from the keystore cache. mosip.esignet.cache.expire-in-seconds={'challenge_generated': ${mosip.signup.unauthenticated.txn.timeout},\ 'challenge_verified': ${mosip.signup.verified.txn.timeout},\ 'status_check': ${mosip.signup.status-check.txn.timeout}, \ 'blocked_identifier': ${mosip.signup.generate-challenge.blocked.timeout},\ - 'keystore' : 10, \ + 'keystore' : 600, \ 'key_alias' : ${mosip.signup.verified.txn.timeout} } ## ------------------------------------- Auth adapter ------------------------------------------------------------------ diff --git a/signup-service/src/main/resources/messages.properties b/signup-service/src/main/resources/messages.properties index 5e09cc01..7c253419 100644 --- a/signup-service/src/main/resources/messages.properties +++ b/signup-service/src/main/resources/messages.properties @@ -7,3 +7,4 @@ invalid_challenge_channel=Invalid Challenge channel provided. invalid_no_of_challenges=Null or empty authentication challenges not allowed. invalid_auth_factor_type=Null or empty authentication factor type not allowed. invalid_challenge=Invalid Authentication challenge provided. +crypto_error=Internal Error, Please try again. diff --git a/signup-service/src/test/resources/application-test.properties b/signup-service/src/test/resources/application-test.properties index 756dd857..4e10d344 100644 --- a/signup-service/src/test/resources/application-test.properties +++ b/signup-service/src/test/resources/application-test.properties @@ -28,9 +28,12 @@ mosip.signup.identifier.regex=^\\+855[1-9]\\d{7,8}$ mosip.signup.identifier.prefix=+855 mosip.signup.supported-languages={'khm','eng'} mosip.signup.password.pattern=^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\\x5F\\W])(?=.{8,20})[a-zA-Z0-9\\x5F\\W]{8,20}$ -mosip.signup.fullname.pattern=^[\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]{1,30}$ mosip.signup.password.max-length=20 -mosip.signup.password.min-length=6 +mosip.signup.generate-challenge.blocked.timeout=300 +mosip.signup.challenge.timeout=60 +mosip.signup.audit.description.max-length=2048 +mosip.signup.password.min-length=8 +mosip.signup.fullname.pattern=^[\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]{1,30}$ ## Time given to generate and verify the challenge in seconds. ## Default resend delay is 60 seconds, with 3 attempts, so 60*3=180 seconds. @@ -55,7 +58,7 @@ mosip.signup.supported.challenge-types={'OTP', 'KBA'} mosip.signup.supported.challenge.otp.length=6 ## ------------------------------------- Cache configuration ----------------------------------------------------------- - +mosip.signup.cache.symmetric-algorithm-name=AES/CFB/PKCS5Padding spring.cache.type=simple #spring.cache.type=redis @@ -70,12 +73,13 @@ mosip.esignet.cache.size={'challenge_generated': 200, \ 'status_check': 200,\ 'blocked_identifier':2000,\ 'keystore' : 5, \ - 'key_alias' : 200 } + 'key_alias' : 1 } + mosip.esignet.cache.expire-in-seconds={'challenge_generated': ${mosip.signup.unauthenticated.txn.timeout},\ 'challenge_verified': ${mosip.signup.verified.txn.timeout},\ 'status_check': ${mosip.signup.status-check.txn.timeout}, \ 'blocked_identifier': ${mosip.signup.generate-challenge.blocked.timeout},\ - 'keystore' : 10, \ + 'keystore' : 600, \ 'key_alias' : ${mosip.signup.verified.txn.timeout} } ## ------------------------------------- Auth adapter ------------------------------------------------------------------