Skip to content

Commit

Permalink
Merge pull request #32 from ase-101/develop
Browse files Browse the repository at this point in the history
ES-628
  • Loading branch information
ase-101 authored Jan 25, 2024
2 parents 556312c + d446f5f commit b14727a
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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"),
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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---
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()));

}

Expand Down
10 changes: 7 additions & 3 deletions signup-service/src/main/resources/application-default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 ------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions signup-service/src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
14 changes: 9 additions & 5 deletions signup-service/src/test/resources/application-test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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 ------------------------------------------------------------------
Expand Down

0 comments on commit b14727a

Please sign in to comment.