Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,27 +158,52 @@ public byte[] encrypt(ClientType clientType, byte[] publicKey, byte[] dataToEncr
}

public byte[] decrypt(byte[] dataToDecrypt) {
byte[] encryptedSecretKey = Arrays.copyOfRange(dataToDecrypt, 0, symmetricKeyLength);
byte[] secretKeyBytes = Objects.requireNonNull(getClientSecurity()).asymmetricDecrypt(encryptedSecretKey);
SecretKey secretKey = new SecretKeySpec(secretKeyBytes, "AES");
// Extract encrypted AES key and decrypt it
byte[] encryptedSecretKey = new byte[symmetricKeyLength];
System.arraycopy(dataToDecrypt, 0, encryptedSecretKey, 0, symmetricKeyLength);
byte[] secretKeyBytes = Objects.requireNonNull(getClientSecurity()).asymmetricDecrypt(encryptedSecretKey);
SecretKey secretKey = new SecretKeySpec(secretKeyBytes, "AES");

try {
byte[] iv = Arrays.copyOfRange(dataToDecrypt, symmetricKeyLength, symmetricKeyLength + ivLength);
byte[] aad = Arrays.copyOfRange(dataToDecrypt, symmetricKeyLength + ivLength, symmetricKeyLength + ivLength + aadLength);
byte[] cipher = Arrays.copyOfRange(dataToDecrypt, symmetricKeyLength + ivLength + aadLength,
dataToDecrypt.length);
return cryptoCore.symmetricDecrypt(secretKey, cipher, iv, aad);
} catch (Throwable t) {
LOGGER.error("Failed to decrypt the data due to : ", t.getMessage());
//1.1.4.4 backward compatibility code, for IV_LENGTH = 16 and AAD_LENGTH = 12;
byte[] iv = Arrays.copyOfRange(dataToDecrypt, symmetricKeyLength, symmetricKeyLength + 16);
byte[] aad = Arrays.copyOfRange(dataToDecrypt, symmetricKeyLength + 16, symmetricKeyLength + 16 + 12);
byte[] cipher = Arrays.copyOfRange(dataToDecrypt, symmetricKeyLength + 16 + 12,
dataToDecrypt.length);
return cryptoCore.symmetricDecrypt(secretKey, cipher, iv, aad);
}
// Pre-calculate offsets
final int ivOffset = symmetricKeyLength;
final int aadOffset = ivOffset + ivLength;
final int cipherOffset = aadOffset + aadLength;

try {
byte[] iv = new byte[ivLength];
byte[] aad = new byte[aadLength];
byte[] cipher = new byte[dataToDecrypt.length - cipherOffset];

System.arraycopy(dataToDecrypt, ivOffset, iv, 0, ivLength);
System.arraycopy(dataToDecrypt, aadOffset, aad, 0, aadLength);
System.arraycopy(dataToDecrypt, cipherOffset, cipher, 0, cipher.length);

return cryptoCore.symmetricDecrypt(secretKey, cipher, iv, aad);

} catch (Throwable t) {
LOGGER.error("Failed to decrypt using default IV/AAD lengths. Trying fallback. Error: ", t);

// 1.1.4.4 backward compatibility block
final int fallbackIvLength = 16;
final int fallbackAadLength = 12;

int fallbackIvOffset = symmetricKeyLength;
int fallbackAadOffset = fallbackIvOffset + fallbackIvLength;
int fallbackCipherOffset = fallbackAadOffset + fallbackAadLength;

byte[] iv = new byte[fallbackIvLength];
byte[] aad = new byte[fallbackAadLength];
byte[] cipher = new byte[dataToDecrypt.length - fallbackCipherOffset];

System.arraycopy(dataToDecrypt, fallbackIvOffset, iv, 0, fallbackIvLength);
System.arraycopy(dataToDecrypt, fallbackAadOffset, aad, 0, fallbackAadLength);
System.arraycopy(dataToDecrypt, fallbackCipherOffset, cipher, 0, cipher.length);

return cryptoCore.symmetricDecrypt(secretKey, cipher, iv, aad);
}
}


public static byte[] generateRandomBytes(int length) {
if(secureRandom == null)
secureRandom = new SecureRandom();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import javax.crypto.spec.PSource.PSpecified;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import jakarta.annotation.PreDestroy;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.encodings.OAEPEncoding;
Expand Down Expand Up @@ -96,7 +98,7 @@ public class CryptoCore implements CryptoCoreSpec<byte[], byte[], SecretKey, Pub
@Value("${mosip.kernel.crypto.gcm-tag-length:128}")
private int tagLength;

@Value("${mosip.kernel.crypto.symmetric-algorithm-name:AES/GCM/PKCS5Padding}")
@Value("${mosip.kernel.crypto.symmetric-algorithm-name:AES/GCM/NOPadding}")
private String symmetricAlgorithm;

@Value("${mosip.kernel.crypto.asymmetric-algorithm-name:RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING}")
Expand All @@ -117,28 +119,79 @@ public class CryptoCore implements CryptoCoreSpec<byte[], byte[], SecretKey, Pub
@Value("${mosip.kernel.keymanager.hsm.keystore-type:PKCS11}")
private String keystoreType;

private SecureRandom secureRandom;
private static final OAEPParameterSpec OAEP_SHA256_MGF1 =
new OAEPParameterSpec(HASH_ALGO, MGF1, MGF1ParameterSpec.SHA256, PSpecified.DEFAULT);

private static ThreadLocal<SecureRandom> secureRandomThreadLocal = null;
private ThreadLocal<Cipher> CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC;
private ThreadLocal<Cipher> CIPHER_GCM_ENCRYPT_DECRYPT_ASYMMETRIC;
private ThreadLocal<SecretKeyFactory> SK_FACTORY_PBKDF2;

public static String SYMMETRIC_ALGO;
public static String ASYMMETRIC_ALGO;

@PostConstruct
public void init() {
secureRandom = new SecureRandom();
secureRandomThreadLocal = ThreadLocal.withInitial(() -> {
try { return SecureRandom.getInstanceStrong(); } catch (Exception ignore) { return new SecureRandom(); }
});

SYMMETRIC_ALGO = symmetricAlgorithm;
ASYMMETRIC_ALGO = asymmetricAlgorithm;

CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(symmetricAlgorithm);
} catch (Exception e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e); }
});

CIPHER_GCM_ENCRYPT_DECRYPT_ASYMMETRIC = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(asymmetricAlgorithm);
} catch (Exception e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e); }
});

SK_FACTORY_PBKDF2 = ThreadLocal.withInitial(() -> {
try { return SecretKeyFactory.getInstance(passwordAlgorithm); }
catch (java.security.NoSuchAlgorithmException e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}
});
}

@PreDestroy
public void shutdown() {
if (secureRandomThreadLocal != null)
secureRandomThreadLocal.remove();

if (CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC != null)
CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC.remove();

if (CIPHER_GCM_ENCRYPT_DECRYPT_ASYMMETRIC != null)
CIPHER_GCM_ENCRYPT_DECRYPT_ASYMMETRIC.remove();

if (SK_FACTORY_PBKDF2 != null)
SK_FACTORY_PBKDF2.remove();
}

@Override
public byte[] symmetricEncrypt(SecretKey key, byte[] data, byte[] aad) {
Objects.requireNonNull(key, SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage());
CryptoUtils.verifyData(data);
Cipher cipher;
try {
cipher = Cipher.getInstance(symmetricAlgorithm);
} catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}
byte[] output = null;
byte[] randomIV = generateIV(cipher.getBlockSize());
try {
Cipher cipher = CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC.get();

byte[] randomIV = generateIV(cipher.getBlockSize());

SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), AES);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(tagLength, randomIV);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
Expand Down Expand Up @@ -167,15 +220,10 @@ public byte[] symmetricEncrypt(SecretKey key, byte[] data, byte[] iv, byte[] aad
if (iv == null) {
return symmetricEncrypt(key, data, aad);
}
Cipher cipher;
try {
cipher = Cipher.getInstance(symmetricAlgorithm);
} catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}

try {
Cipher cipher = CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC.get();

SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), AES);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(tagLength, iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
Expand All @@ -197,24 +245,34 @@ public byte[] symmetricEncrypt(SecretKey key, byte[] data, byte[] iv, byte[] aad
public byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] aad) {
Objects.requireNonNull(key, SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage());
CryptoUtils.verifyData(data);
Cipher cipher;
try {
cipher = Cipher.getInstance(symmetricAlgorithm);
} catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}
byte[] output = null;
try {
byte[] randomIV = Arrays.copyOfRange(data, data.length - cipher.getBlockSize(), data.length);
Cipher cipher = CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC.get();

int ivLength = cipher.getBlockSize(); // Will be 16

if (data.length <= ivLength + (tagLength / 8)) {
throw new InvalidDataException(
SecurityExceptionCodeConstant.MOSIP_INVALID_DATA_LENGTH_EXCEPTION.getErrorCode(),
"Encrypted data too short for ciphertext and IV.");
}

int cipherLen = data.length - ivLength;
byte[] cipherTextWithTag = new byte[cipherLen];
byte[] iv = new byte[ivLength];

System.arraycopy(data, 0, cipherTextWithTag, 0, cipherLen);
System.arraycopy(data, cipherLen, iv, 0, ivLength);

SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), AES);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(tagLength, randomIV);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
if (aad != null && aad.length != 0) {
GCMParameterSpec gcmSpec = new GCMParameterSpec(tagLength, iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);

if (aad != null && aad.length > 0) {
cipher.updateAAD(aad);
}
output = doFinal(Arrays.copyOf(data, data.length - cipher.getBlockSize()), cipher);

return doFinal(cipherTextWithTag, cipher);
} catch (java.security.InvalidKeyException e) {
throw new InvalidKeyException(SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage(), e);
Expand All @@ -227,7 +285,6 @@ public byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] aad) {
SecurityExceptionCodeConstant.MOSIP_INVALID_DATA_LENGTH_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_INVALID_DATA_LENGTH_EXCEPTION.getErrorMessage(), e);
}
return output;
}

@Override
Expand All @@ -237,15 +294,9 @@ public byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] iv, byte[] aad
if (iv == null) {
return symmetricDecrypt(key, data, aad);
}
Cipher cipher;
try {
cipher = Cipher.getInstance(symmetricAlgorithm);
} catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}
try {
Cipher cipher = CIPHER_GCM_ENCRYPT_DECRYPT_SYMMETRIC.get();

SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), AES);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(tagLength, iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
Expand All @@ -267,18 +318,11 @@ public byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] iv, byte[] aad
public byte[] asymmetricEncrypt(PublicKey key, byte[] data) {
Objects.requireNonNull(key, SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage());
CryptoUtils.verifyData(data);
Cipher cipher;
try {
cipher = Cipher.getInstance(asymmetricAlgorithm);
} catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}
final OAEPParameterSpec oaepParams = new OAEPParameterSpec(HASH_ALGO, MGF1, MGF1ParameterSpec.SHA256,
PSpecified.DEFAULT);

try {
cipher.init(Cipher.ENCRYPT_MODE, key, oaepParams);
Cipher cipher = CIPHER_GCM_ENCRYPT_DECRYPT_ASYMMETRIC.get();
cipher.init(Cipher.ENCRYPT_MODE, key, OAEP_SHA256_MGF1);
return doFinal(data, cipher);
} catch (java.security.InvalidKeyException e) {
throw new InvalidKeyException(SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorCode(),
e.getMessage(), e);
Expand All @@ -287,7 +331,6 @@ public byte[] asymmetricEncrypt(PublicKey key, byte[] data) {
SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorMessage(), e);
}
return doFinal(data, cipher);
}

@Override
Expand Down Expand Up @@ -358,7 +401,7 @@ private byte[] asymmetricDecrypt(PrivateKey privateKey, BigInteger keyModulus, b
/**
*
* @param paddedPlainText
* @param privateKey
* @param keyModulus
* @return
*/
private byte[] unpadOAEPPadding(byte[] paddedPlainText, BigInteger keyModulus) {
Expand All @@ -380,11 +423,9 @@ private byte[] jceAsymmetricDecrypt(PrivateKey privateKey, byte[] data, String s
CryptoUtils.verifyData(data);
Cipher cipher;
try {
cipher = Objects.isNull(storeType) ? Cipher.getInstance(asymmetricAlgorithm) :
cipher = Objects.isNull(storeType) ? CIPHER_GCM_ENCRYPT_DECRYPT_ASYMMETRIC.get() :
Cipher.getInstance(asymmetricAlgorithm, storeType);
OAEPParameterSpec oaepParams = new OAEPParameterSpec(HASH_ALGO, MGF1, MGF1ParameterSpec.SHA256,
PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
cipher.init(Cipher.DECRYPT_MODE, privateKey, OAEP_SHA256_MGF1);
return doFinal(data, cipher);
} catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException e) {
throw new NoSuchAlgorithmException(
Expand All @@ -400,27 +441,30 @@ private byte[] jceAsymmetricDecrypt(PrivateKey privateKey, byte[] data, String s
}
}


@Override
public String hash(byte[] data, byte[] salt) {
CryptoUtils.verifyData(data);
CryptoUtils.verifyData(salt, SecurityExceptionCodeConstant.SALT_PROVIDED_IS_NULL_OR_EMPTY.getErrorCode(),
SecurityExceptionCodeConstant.SALT_PROVIDED_IS_NULL_OR_EMPTY.getErrorMessage());
SecretKeyFactory secretKeyFactory;
char[] convertedData = new String(data).toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(convertedData, salt, iterations, symmetricKeyLength);

final char[] convertedData = new String(data).toCharArray();
final PBEKeySpec pbeKeySpec = new PBEKeySpec(convertedData, salt, iterations, symmetricKeyLength);
SecretKey key;
try {
secretKeyFactory = SecretKeyFactory.getInstance(passwordAlgorithm);
SecretKeyFactory secretKeyFactory = SK_FACTORY_PBKDF2.get();
key = secretKeyFactory.generateSecret(pbeKeySpec);
} catch (InvalidKeySpecException e) {
throw new InvalidParamSpecException(
SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorCode(), e.getMessage(), e);
} catch (java.security.NoSuchAlgorithmException e) {
} catch (Exception e) {
throw new NoSuchAlgorithmException(
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),
SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
}
finally {
// best-effort wipe of sensitive char[]
java.util.Arrays.fill(convertedData, '\0');
}
return DatatypeConverter.printHexBinary(key.getEncoded());
}

Expand Down Expand Up @@ -460,13 +504,12 @@ public boolean verifySignature(byte[] data, String sign, PublicKey publicKey) {
throw new SignatureException(SecurityExceptionCodeConstant.MOSIP_SIGNATURE_EXCEPTION.getErrorCode(),
e.getMessage(), e);
}

}

@SuppressWarnings("unchecked")
@Override
public SecureRandom random() {
return secureRandom;
return secureRandomThreadLocal.get();
}

/**
Expand All @@ -477,7 +520,7 @@ public SecureRandom random() {
*/
private byte[] generateIV(int blockSize) {
byte[] byteIV = new byte[blockSize];
secureRandom.nextBytes(byteIV);
secureRandomThreadLocal.get().nextBytes(byteIV);
return byteIV;
}

Expand Down Expand Up @@ -542,8 +585,5 @@ public boolean verifySignature(String sign) {
throw new SignatureException(SecurityExceptionCodeConstant.MOSIP_SIGNATURE_EXCEPTION.getErrorCode(),
e.getMessage(), e);
}

}


}
}
}
Loading