From 331add5513620dd43f3d59b156388cdc9dc08fb1 Mon Sep 17 00:00:00 2001 From: nagendra0721 Date: Fri, 20 Jun 2025 19:09:02 +0530 Subject: [PATCH 1/5] MOSIP-36428: ECC Algorithm Support for encryption and decryption Signed-off-by: nagendra0721 --- .../constant/CryptomanagerConstant.java | 12 + .../constant/CryptomanagerErrorCode.java | 2 + .../service/EcCryptoOperation.java | 42 +++ .../impl/CryptomanagerServiceImpl.java | 234 ++++++++++------ .../service/impl/EcCryptoOperationImpl.java | 264 ++++++++++++++++++ .../util/CryptomanagerUtils.java | 148 +++++++++- .../bouncycastle/KeyGenerator.java | 18 +- .../bouncycastle/util/KeyGeneratorUtils.java | 14 + .../hsm/constant/KeymanagerConstant.java | 5 + .../hsm/impl/pkcs/PKCS12KeyStoreImpl.java | 2 +- .../hsm/util/CertificateUtility.java | 22 +- .../constant/KeymanagerConstant.java | 5 + .../helper/SessionKeyDecryptorHelper.java | 2 +- .../service/impl/KeymanagerServiceImpl.java | 31 +- .../util/KeymanagerUtil.java | 90 ++++-- .../service/impl/KeyMigratorServiceImpl.java | 14 +- .../service/impl/SignatureServiceImpl.java | 9 +- .../kernel/signature/util/SignatureUtil.java | 33 ++- .../impl/ZKCryptoManagerServiceImpl.java | 35 ++- .../test/service/EcCryptoOperationTest.java | 26 ++ 20 files changed, 870 insertions(+), 138 deletions(-) create mode 100644 kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/EcCryptoOperation.java create mode 100644 kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/EcCryptoOperationImpl.java create mode 100644 kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerConstant.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerConstant.java index b5ef146e4..600a48378 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerConstant.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerConstant.java @@ -63,6 +63,18 @@ private CryptomanagerConstant() { public static final String CACHE_AES_KEY = "cacheAESKey"; public static final String CACHE_INT_COUNTER = "cacheIntCounter"; + + public static final byte[] VERSION_EC256_R1 = "VER_E2".getBytes(); // secp256R1 curve header + + public static final byte[] VERSION_EC256_K1 = "VER_K2".getBytes(); // secp256K1 curve header + + public static final byte[] VERSION_EC_X25519 = "VER_X2".getBytes(); // X25519 curve header + + public static final String EC_SECP256R1 = "SECP256R1"; + + public static final String EC_SECP256K1 = "SECP256K1"; + + public static final String EC_X25519 = "X25519"; } diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerErrorCode.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerErrorCode.java index 094a0d69d..909b12884 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerErrorCode.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/constant/CryptomanagerErrorCode.java @@ -67,6 +67,8 @@ public enum CryptomanagerErrorCode { JWE_DECRYPTION_INTERNAL_ERROR("KER-CRY-015", "Internal Error while decrypting data using JWE."), + UNSUPPORTED_EC_CURVE("KER-CRY-016", "Unsupported EC Curve Provided. Please check the curve name."), + INTERNAL_SERVER_ERROR("KER-CRY-500", "Internal server error"); diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/EcCryptoOperation.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/EcCryptoOperation.java new file mode 100644 index 000000000..c57de11e9 --- /dev/null +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/EcCryptoOperation.java @@ -0,0 +1,42 @@ +package io.mosip.kernel.cryptomanager.service; + +import java.security.PrivateKey; +import java.security.PublicKey; + +public interface EcCryptoOperation { + + /** + * + * Encrypts data using an asymmetric EC public key. + * + * @param publicKey the public key to use for encryption + * @param data the data to encrypt + * @param iv the initialization vector (IV) for encryption + * @param aad additional authenticated data (AAD) + * @return the encrypted data + */ + public byte[] asymmetricEcEncrypt(PublicKey publicKey, byte[] data, byte[] iv, byte[] aad, String algorithmName); + + /** + * + * Encrypts data using an asymmetric EC public key with a specified curve name. + * + * @param publicKey the public key to use for encryption + * @param data the data to encrypt + * @param curveName the name of the elliptic curve used + * @return the encrypted data + */ + public byte[] asymmetricEcEncrypt(PublicKey publicKey, byte[] data, String curveName); + + /** + * + * Decrypts data using an asymmetric EC private key. + * + * @param privateKey the private key to use for decryption + * @param data the data to decrypt + * @param aad additional authenticated data (AAD) + * @param curveName the name of the elliptic curve used + * @return the decrypted data + */ + public byte[] asymmetricEcDecrypt(PrivateKey privateKey, byte[] data, byte[] aad, String curveName); +} diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java index 4db658a32..ae689a902 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java @@ -4,6 +4,7 @@ import static io.mosip.kernel.cryptomanager.constant.CryptomanagerConstant.CACHE_INT_COUNTER; import static io.mosip.kernel.cryptomanager.constant.CryptomanagerConstant.DEFAULT_INCLUDES_FALSE; import static io.mosip.kernel.cryptomanager.constant.CryptomanagerConstant.DEFAULT_INCLUDES_TRUE; +import static java.util.Arrays.copyOfRange; import java.nio.ByteBuffer; import java.security.InvalidKeyException; @@ -15,10 +16,13 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import io.mosip.kernel.core.util.DateUtils2; +import io.mosip.kernel.cryptomanager.service.EcCryptoOperation; +import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant; import jakarta.annotation.PostConstruct; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -117,6 +121,9 @@ public class CryptomanagerServiceImpl implements CryptomanagerService { @Value("${mosip.keymanager.argon2.hash.generate.parallelism:2}") private int argon2Parallelism; + @Value("${mosip.kernel.keygenerator.ecc-curve-name:SECP256R1}") + private String ecCurveName; + private static SecureRandom secureRandom = null; /** @@ -143,8 +150,12 @@ public class CryptomanagerServiceImpl implements CryptomanagerService { @Autowired KeymanagerUtil keymanagerUtil; + @Autowired + EcCryptoOperation ecCryptoOperation; + private Cache saltGenParamsCache = null; + @PostConstruct public void init() { // Added Cache2kBuilder in the postConstruct because expire value @@ -179,60 +190,85 @@ public void init() { /* * (non-Javadoc) - * + * * @see * io.mosip.kernel.cryptography.service.CryptographyService#encrypt(io.mosip. * kernel.cryptography.dto.CryptographyRequestDto) */ @Override public CryptomanagerResponseDto encrypt(CryptomanagerRequestDto cryptoRequestDto) { - LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, - "Request for data encryption."); - + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, + "Request for data encryption."); + cryptomanagerUtil.validateKeyIdentifierIds(cryptoRequestDto.getApplicationId(), cryptoRequestDto.getReferenceId()); - SecretKey secretKey = keyGenerator.getSymmetricKey(); - final byte[] encryptedData; - byte[] headerBytes = new byte[0]; - if (cryptomanagerUtil.isValidSalt(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getSalt()))) { - encryptedData = cryptoCore.symmetricEncrypt(secretKey, cryptomanagerUtil.decodeBase64Data(cryptoRequestDto.getData()), - cryptomanagerUtil.decodeBase64Data(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getSalt())), - cryptomanagerUtil.decodeBase64Data(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getAad()))); - } else { - byte[] aad = cryptomanagerUtil.decodeBase64Data(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getAad())); - if (aad == null || aad.length == 0){ - encryptedData = generateAadAndEncryptData(secretKey, cryptoRequestDto.getData()); - headerBytes = CryptomanagerConstant.VERSION_RSA_2048; - } else { - encryptedData = cryptoCore.symmetricEncrypt(secretKey, cryptomanagerUtil.decodeBase64Data(cryptoRequestDto.getData()), - aad); - } - } Certificate certificate = cryptomanagerUtil.getCertificate(cryptoRequestDto); - LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, - "Found the cerificate, proceeding with session key encryption."); PublicKey publicKey = certificate.getPublicKey(); - final byte[] encryptedSymmetricKey = cryptoCore.asymmetricEncrypt(publicKey, secretKey.getEncoded()); - LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, - "Session key encryption completed."); - //boolean prependThumbprint = cryptoRequestDto.getPrependThumbprint() == null ? false : cryptoRequestDto.getPrependThumbprint(); + CryptomanagerResponseDto cryptoResponseDto = new CryptomanagerResponseDto(); - // support of 1.1.3 no thumbprint is configured as true & encryption request with no thumbprint - // request thumbprint flag will not be considered if support no thumbprint is set to false. - //------------------- - // no thumbprint flag will not be required to consider at the time of encryption. So commented the below code. - // from 1.2.0.1 version, support of no thumbprint flag will be removed in case of data encryption. + + if (publicKey.getAlgorithm().equalsIgnoreCase(io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant.RSA)) { + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant.RSA, + "Encrypting the Data Using RSA key."); + SecretKey secretKey = keyGenerator.getSymmetricKey(); + final byte[] encryptedData; + byte[] headerBytes = new byte[0]; + if (cryptomanagerUtil.isValidSalt(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getSalt()))) { + encryptedData = cryptoCore.symmetricEncrypt(secretKey, cryptomanagerUtil.decodeBase64Data(cryptoRequestDto.getData()), + cryptomanagerUtil.decodeBase64Data(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getSalt())), + cryptomanagerUtil.decodeBase64Data(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getAad()))); + } else { + byte[] aad = cryptomanagerUtil.decodeBase64Data(CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getAad())); + if (aad == null || aad.length == 0){ + encryptedData = generateAadAndEncryptData(secretKey, cryptoRequestDto.getData()); + headerBytes = CryptomanagerConstant.VERSION_RSA_2048; + } else { + encryptedData = cryptoCore.symmetricEncrypt(secretKey, cryptomanagerUtil.decodeBase64Data(cryptoRequestDto.getData()), + aad); + } + } + + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, + "Found the cerificate, proceeding with session key encryption."); + final byte[] encryptedSymmetricKey = cryptoCore.asymmetricEncrypt(publicKey, secretKey.getEncoded()); + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, + "Session key encryption completed."); + //boolean prependThumbprint = cryptoRequestDto.getPrependThumbprint() == null ? false : cryptoRequestDto.getPrependThumbprint(); + // support of 1.1.3 no thumbprint is configured as true & encryption request with no thumbprint + // request thumbprint flag will not be considered if support no thumbprint is set to false. + //------------------- + // no thumbprint flag will not be required to consider at the time of encryption. So commented the below code. + // from 1.2.0.1 version, support of no thumbprint flag will be removed in case of data encryption. /* if (noThumbprint && !prependThumbprint) { byte[] finalEncKeyBytes = cryptomanagerUtil.concatByteArrays(headerBytes, encryptedSymmetricKey); cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(CryptoUtil.combineByteArray(encryptedData, finalEncKeyBytes, keySplitter))); return cryptoResponseDto; - } */ - //--------------------- - byte[] certThumbprint = cryptomanagerUtil.getCertificateThumbprint(certificate); - byte[] concatedData = cryptomanagerUtil.concatCertThumbprint(certThumbprint, encryptedSymmetricKey); - byte[] finalEncKeyBytes = cryptomanagerUtil.concatByteArrays(headerBytes, concatedData); - cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(CryptoUtil.combineByteArray(encryptedData, - finalEncKeyBytes, keySplitter))); + } */ + //--------------------- + byte[] certThumbprint = cryptomanagerUtil.getCertificateThumbprint(certificate); + byte[] concatedData = cryptomanagerUtil.concatCertThumbprint(certThumbprint, encryptedSymmetricKey); + byte[] finalEncKeyBytes = cryptomanagerUtil.concatByteArrays(headerBytes, concatedData); + cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(CryptoUtil.combineByteArray(encryptedData, + finalEncKeyBytes, keySplitter))); + } else { + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, KeymanagerConstant.EC_KEY_TYPE, + "Encrypting the Data Using ECC key."); + + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, + "Found the cerificate, proceeding with ecc key encryption."); + + byte[] aad = cryptomanagerUtil.generateRandomBytes(CryptomanagerConstant.GCM_AAD_LENGTH); + byte[] encryptedData = ecCryptoOperation.asymmetricEcEncrypt(publicKey, cryptomanagerUtil.decodeBase64Data(cryptoRequestDto.getData()), null, aad, ecCurveName); + byte[] encryptedDataWithIv = cryptomanagerUtil.concatByteArrays(aad, encryptedData); + + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, + "ECC key encryption completed."); + + byte[] headerBytes = cryptomanagerUtil.getHeaderByte(ecCurveName); + + byte[] finalEncKeyBytes = CryptoUtil.combineByteArray(encryptedDataWithIv, headerBytes, keySplitter); + cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(finalEncKeyBytes)); + } return cryptoResponseDto; } @@ -249,14 +285,14 @@ private byte[] generateAadAndEncryptData(SecretKey secretKey, String data){ /* * (non-Javadoc) - * + * * @see * io.mosip.kernel.cryptography.service.CryptographyService#decrypt(io.mosip. * kernel.cryptography.dto.CryptographyRequestDto) */ @Override public CryptomanagerResponseDto decrypt(CryptomanagerRequestDto cryptoRequestDto) { - LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, CryptomanagerConstant.DECRYPT, + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, CryptomanagerConstant.DECRYPT, "Request for data decryption."); boolean hasAcccess = cryptomanagerUtil.hasKeyAccess(cryptoRequestDto.getApplicationId()); @@ -269,51 +305,77 @@ public CryptomanagerResponseDto decrypt(CryptomanagerRequestDto cryptoRequestDto byte[] encryptedHybridData = cryptomanagerUtil.decodeBase64Data(cryptoRequestDto.getData()); int keyDelimiterIndex = CryptoUtil.getSplitterIndex(encryptedHybridData, 0, keySplitter); - byte[] encryptedKey = new byte[keyDelimiterIndex]; - System.arraycopy(encryptedHybridData, 0, encryptedKey, 0, keyDelimiterIndex); - - int dataStartIndex = keyDelimiterIndex + keySplitter.length(); - int encryptedDataLength = encryptedHybridData.length - dataStartIndex; - byte[] encryptedData = new byte[encryptedDataLength]; - System.arraycopy(encryptedHybridData, dataStartIndex, encryptedData, 0, encryptedDataLength); - - byte[] headerBytes = cryptomanagerUtil.parseEncryptKeyHeader(encryptedKey); - - // Set only the actual key bytes (excluding header) into the DTO - int headerLength = headerBytes.length; - byte[] rawKey = new byte[encryptedKey.length - headerLength]; - System.arraycopy(encryptedKey, headerLength, rawKey, 0, rawKey.length); - cryptoRequestDto.setData(CryptoUtil.encodeToURLSafeBase64(rawKey)); - - SecretKey decryptedSymmetricKey = cryptomanagerUtil.getDecryptedSymmetricKey(cryptoRequestDto); - LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, - CryptomanagerConstant.DECRYPT, "Session Key Decryption completed."); - - final byte[] decryptedData; - String salt = CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getSalt()); - String aad = CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getAad()); - - if (cryptomanagerUtil.isValidSalt(salt)) { - decryptedData = cryptoCore.symmetricDecrypt( - decryptedSymmetricKey, - encryptedData, - cryptomanagerUtil.decodeBase64Data(salt), - cryptomanagerUtil.decodeBase64Data(aad)); - } else { - if (Arrays.equals(headerBytes, CryptomanagerConstant.VERSION_RSA_2048)) { - decryptedData = splitAadAndDecryptData(decryptedSymmetricKey, encryptedData); - } else { - decryptedData = cryptoCore.symmetricDecrypt( - decryptedSymmetricKey, - encryptedData, - cryptomanagerUtil.decodeBase64Data(aad)); - } - } - LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, CryptomanagerConstant.DECRYPT, - "Data decryption completed."); - CryptomanagerResponseDto cryptoResponseDto = new CryptomanagerResponseDto(); - cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(decryptedData)); - return cryptoResponseDto; + String algorithmName = cryptomanagerUtil.getAlgorithmNameFromHeader(encryptedHybridData); + + if (algorithmName.equalsIgnoreCase(KeymanagerConstant.RSA)) { + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, KeymanagerConstant.RSA, + "Decrypting the data with RSA key."); + + byte[] encryptedKey = new byte[keyDelimiterIndex]; + System.arraycopy(encryptedHybridData, 0, encryptedKey, 0, keyDelimiterIndex); + + int dataStartIndex = keyDelimiterIndex + keySplitter.length(); + int encryptedDataLength = encryptedHybridData.length - dataStartIndex; + byte[] encryptedData = new byte[encryptedDataLength]; + System.arraycopy(encryptedHybridData, dataStartIndex, encryptedData, 0, encryptedDataLength); + + byte[] headerBytes = cryptomanagerUtil.parseEncryptKeyHeader(encryptedKey); + + // Set only the actual key bytes (excluding header) into the DTO + int headerLength = headerBytes.length; + byte[] rawKey = new byte[encryptedKey.length - headerLength]; + System.arraycopy(encryptedKey, headerLength, rawKey, 0, rawKey.length); + cryptoRequestDto.setData(CryptoUtil.encodeToURLSafeBase64(rawKey)); + + SecretKey decryptedSymmetricKey = cryptomanagerUtil.getDecryptedSymmetricKey(cryptoRequestDto); + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, + CryptomanagerConstant.DECRYPT, "Session Key Decryption completed."); + + final byte[] decryptedData; + String salt = CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getSalt()); + String aad = CryptomanagerUtils.nullOrTrim(cryptoRequestDto.getAad()); + + if (cryptomanagerUtil.isValidSalt(salt)) { + decryptedData = cryptoCore.symmetricDecrypt( + decryptedSymmetricKey, + encryptedData, + cryptomanagerUtil.decodeBase64Data(salt), + cryptomanagerUtil.decodeBase64Data(aad)); + } else { + if (Arrays.equals(headerBytes, CryptomanagerConstant.VERSION_RSA_2048)) { + decryptedData = splitAadAndDecryptData(decryptedSymmetricKey, encryptedData); + } else { + decryptedData = cryptoCore.symmetricDecrypt( + decryptedSymmetricKey, + encryptedData, + cryptomanagerUtil.decodeBase64Data(aad)); + } + } + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, CryptomanagerConstant.DECRYPT, + "Data decryption completed."); + CryptomanagerResponseDto cryptoResponseDto = new CryptomanagerResponseDto(); + cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(decryptedData)); + return cryptoResponseDto; + + } else { + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.DECRYPT, KeymanagerConstant.EC_KEY_TYPE, + "Decrypting the Data Using ECC key."); + String ecCurveName = cryptomanagerUtil.getAlgorithmNameFromHeader(encryptedHybridData); + byte[] thumbprint = copyOfRange(encryptedHybridData, keyDelimiterIndex + keySplitter.length(), keyDelimiterIndex + keySplitter.length() + CryptomanagerConstant.THUMBPRINT_LENGTH); + byte[] encryptedDataWithIv = copyOfRange(encryptedHybridData, keyDelimiterIndex + keySplitter.length() + CryptomanagerConstant.THUMBPRINT_LENGTH, + encryptedHybridData.length); + + String certThumbprintHex = Hex.toHexString(thumbprint).toUpperCase(); + PrivateKey privateKey = (PrivateKey) cryptomanagerUtil.getEncryptedPrivateKey(cryptoRequestDto.getApplicationId(), + Optional.ofNullable(cryptoRequestDto.getReferenceId()), certThumbprintHex)[0]; + + byte[] aad = Arrays.copyOfRange(encryptedDataWithIv, 0, CryptomanagerConstant.GCM_AAD_LENGTH); + byte[] encryptedData = Arrays.copyOfRange(encryptedDataWithIv, CryptomanagerConstant.GCM_AAD_LENGTH,encryptedDataWithIv.length); + + byte[] decryptedData = ecCryptoOperation.asymmetricEcDecrypt(privateKey, encryptedData, aad, ecCurveName); + CryptomanagerResponseDto cryptoResponseDto = new CryptomanagerResponseDto(); + cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(decryptedData)); + } } private byte[] splitAadAndDecryptData(SecretKey symmetricKey, byte[] encryptedData) { diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/EcCryptoOperationImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/EcCryptoOperationImpl.java new file mode 100644 index 000000000..c90f5e60e --- /dev/null +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/EcCryptoOperationImpl.java @@ -0,0 +1,264 @@ +package io.mosip.kernel.cryptomanager.service.impl; + +import io.mosip.kernel.core.exception.NoSuchAlgorithmException; +import io.mosip.kernel.core.logger.spi.Logger; +import io.mosip.kernel.core.util.CryptoUtil; +import io.mosip.kernel.crypto.jce.constant.SecurityExceptionCodeConstant; +import io.mosip.kernel.crypto.jce.util.CryptoUtils; +import io.mosip.kernel.cryptomanager.constant.CryptomanagerConstant; +import io.mosip.kernel.cryptomanager.service.EcCryptoOperation; +import io.mosip.kernel.keymanagerservice.logger.KeymanagerLogger; +import io.mosip.kernel.core.crypto.exception.InvalidKeyException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.crypto.*; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.Objects; + +@Service +public class EcCryptoOperationImpl implements EcCryptoOperation { + + private static final String AES = "AES"; + + @Value("${mosip.kernel.data-key-splitter}") + private String keySplitter; + + @Value("${mosip.kernel.crypto.gcm-tag-length:128}") + private int tagLength; + + @Value("${mosip.kernel.crypto.symmetric-algorithm-name:AES/GCM/NoPadding}") + private String symmetricAlgorithmName; + + private static final Logger LOGGER = KeymanagerLogger.getLogger(CryptomanagerServiceImpl.class); + + private static final String reason = "CryptoManager"; + + private static final int AES_KEY_LENGTH = 32; // AES-256 + + private static final String HMAC_SHA_256 = "HmacSHA256"; + + private static final String EC_ALGORITHM = "EC"; + + private static final String ECDH = "ECDH"; + + + @Override + public byte[] asymmetricEcEncrypt(PublicKey key, byte[] data, String curveName) { + Objects.requireNonNull(key, SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage()); + CryptoUtils.verifyData(data); + return asymmetricEcEncrypt(key, data, null, null, curveName); + } + + @Override + public byte[] asymmetricEcEncrypt(PublicKey key, byte[] data, byte[] randomIV, byte[] aad, String curveName) { + byte[] output; + Cipher cipher; + SecretKey aesKey = null; + byte[] ephemeralPublicKey = null; + KeyPair ephemeralKeyPair = null; + + try { + KeyPairGenerator ephemeralKeyPairGen = KeyPairGenerator.getInstance(EC_ALGORITHM); + ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(curveName); + ephemeralKeyPairGen.initialize(ecGenParameterSpec); + ephemeralKeyPair = ephemeralKeyPairGen.generateKeyPair(); + + KeyAgreement keyAgreement = KeyAgreement.getInstance(ECDH); + keyAgreement.init(ephemeralKeyPair.getPrivate()); + keyAgreement.doPhase(key, true); + byte[] sharedSecret = keyAgreement.generateSecret(); + + if (randomIV == null || randomIV.length == 0) { + randomIV = generateIV(CryptomanagerConstant.GCM_NONCE_LENGTH); // Default IV length for AES + } + + byte[] aesKeyBytes = getHkdfKeyBytes(sharedSecret, randomIV, reason.getBytes(), AES_KEY_LENGTH); + aesKey = new SecretKeySpec(aesKeyBytes, 0, AES_KEY_LENGTH, AES); + + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(tagLength, randomIV); + cipher = Cipher.getInstance(symmetricAlgorithmName); + cipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmParameterSpec); + if (aad != null && aad.length != 0) { + cipher.updateAAD(aad); + } + + byte[] encryptedData = cipher.doFinal(data); + + byte[] encryptedDataWithIv = new byte[encryptedData.length + randomIV.length]; + ephemeralPublicKey = ephemeralKeyPair.getPublic().getEncoded(); + + System.arraycopy(encryptedData, 0, encryptedDataWithIv, 0, encryptedData.length); + System.arraycopy(randomIV, 0, encryptedDataWithIv, encryptedData.length, randomIV.length); + + byte[] keySplitterBytes = keySplitter.getBytes(); + output = new byte[encryptedDataWithIv.length + keySplitterBytes.length + ephemeralPublicKey.length]; + + System.arraycopy(encryptedDataWithIv, 0, output, 0, encryptedDataWithIv.length); + System.arraycopy(keySplitterBytes, 0, output, encryptedDataWithIv.length, keySplitterBytes.length); + System.arraycopy(ephemeralPublicKey, 0, output, encryptedDataWithIv.length + keySplitterBytes.length, ephemeralPublicKey.length); + + } catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException e) { + throw new NoSuchAlgorithmException( + SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e); + } catch (java.security.InvalidKeyException | IllegalBlockSizeException e) { + throw new InvalidKeyException( + SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage(), e); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException( + SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorMessage(), e); + } finally { + if (aesKey != null) { + destroyKey(aesKey.getEncoded()); + } + if (ephemeralPublicKey != null) { + destroyKey(ephemeralPublicKey); + } + if (ephemeralKeyPair != null) { + destroyKey(ephemeralKeyPair.getPrivate().getEncoded()); + } + if (ephemeralKeyPair.getPrivate() != null) destroyKey(ephemeralKeyPair.getPublic().getEncoded()); + } + return output; + } + + @Override + public byte[] asymmetricEcDecrypt(PrivateKey privateKey, byte[] data, byte[] aad, String algorithmName) { + Objects.requireNonNull(privateKey, SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage()); + CryptoUtils.verifyData(data); + byte[] decryptedData = null; + + try { + byte[] keySplitterBytes = keySplitter.getBytes(); + + int splitterIndex = 0; + splitterIndex = CryptoUtil.getSplitterIndex(data, splitterIndex, keySplitter); + + // Extract encrypted data and ephemeral public key bytes + byte[] encryptedData = Arrays.copyOfRange(data, 0, splitterIndex); + byte[] ephemeralPublicKeyBytes = Arrays.copyOfRange(data, splitterIndex + keySplitterBytes.length, data.length); + + // Extract IV from the end of encryptedData + int ivLength = CryptomanagerConstant.GCM_NONCE_LENGTH; + byte[] iv = Arrays.copyOfRange(encryptedData, encryptedData.length - ivLength, encryptedData.length); + byte[] cipherText = Arrays.copyOfRange(encryptedData, 0, encryptedData.length - ivLength); + + KeyFactory keyFactory = KeyFactory.getInstance(EC_ALGORITHM); + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(ephemeralPublicKeyBytes); + PublicKey ephemeralPublicKey = keyFactory.generatePublic(publicKeySpec); + + KeyAgreement keyAgreement = KeyAgreement.getInstance(ECDH); + keyAgreement.init(privateKey); + keyAgreement.doPhase(ephemeralPublicKey, true); + byte[] sharedSecret = keyAgreement.generateSecret(); + + byte[] aesKeyBytes = getHkdfKeyBytes(sharedSecret, iv, reason.getBytes(), AES_KEY_LENGTH); + SecretKey aesKey = new SecretKeySpec(aesKeyBytes, 0, AES_KEY_LENGTH, AES); + + Cipher aesCipher = Cipher.getInstance(symmetricAlgorithmName); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(tagLength, iv); + aesCipher.init(Cipher.DECRYPT_MODE, aesKey, gcmParameterSpec); + if (aad != null && aad.length != 0) { + aesCipher.updateAAD(aad); + } + decryptedData = aesCipher.doFinal(cipherText); + + } catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException e) { + throw new NoSuchAlgorithmException( + SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e); + } catch (java.security.InvalidKeyException | java.security.spec.InvalidKeySpecException | IllegalBlockSizeException e) { + throw new InvalidKeyException( + SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage(), e); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException( + SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_INVALID_PARAM_SPEC_EXCEPTION.getErrorMessage(), e); + } + return decryptedData; + } + + /** + * Generator for IV (Initialization Vector) + * + * @param blockSize blocksize of current cipher + * @return generated IV + */ + private byte[] generateIV(int blockSize) { + byte[] byteIV = new byte[blockSize]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(byteIV); + return byteIV; + } + + /** + * Destroys the key by filling it with zeros. + * + * @param key The key to be destroyed. + */ + private void destroyKey(byte[] key) { + if (Objects.nonNull(key)) { + Arrays.fill(key, (byte) 0); + } + } + + /** + * Generates HKDF key bytes using the provided secret, salt, reason, and key length. + * + * @param ikm The Input Key Material (IKM) to derive the key from. + * @param salt The salt to use in the HKDF process. + * @param reason The reason for generating the key, used as additional data. + * @param keyLength The desired length of the generated key in bytes. + * @return The derived key bytes. + */ + private byte[] getHkdfKeyBytes(byte[] ikm, byte[] salt, byte[] reason, int keyLength) { + LOGGER.info(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.WHITESPACE, CryptomanagerConstant.WHITESPACE, + "Generating HKDF key bytes."); + + try { + Mac mac = Mac.getInstance(HMAC_SHA_256); + SecretKeySpec secretKeySpec = new SecretKeySpec(salt, HMAC_SHA_256); + mac.init(secretKeySpec); + byte[] prk = mac.doFinal(ikm); + + byte[] result = new byte[keyLength]; + byte[] previousBlock = new byte[0]; + int bytegenerated = 0; + int iteration = (int) Math.ceil((double) keyLength / mac.getMacLength()); + + for (int i = 0; i < iteration; i++) { + mac.init(new SecretKeySpec(prk, HMAC_SHA_256)); + mac.update(previousBlock); + mac.update(reason); + mac.update((byte) (i + 1)); + byte[] block = mac.doFinal(); + + int bytesToCopy = Math.min(block.length, keyLength - bytegenerated); + System.arraycopy(block, 0, result, bytegenerated, bytesToCopy); + bytegenerated += bytesToCopy; + + previousBlock = block; + System.out.println("Number of iterations: " + (i + 1) + ", Bytes generated so far: " + bytegenerated); + } + return result; + + } catch (java.security.NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException( + SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e); + } catch (java.security.InvalidKeyException e) { + throw new InvalidKeyException( + SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorCode(), + SecurityExceptionCodeConstant.MOSIP_INVALID_KEY_EXCEPTION.getErrorMessage(), e); + } + } +} diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java index 234ad7292..90bd79f00 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java @@ -7,10 +7,12 @@ package io.mosip.kernel.cryptomanager.util; import java.io.IOException; -import java.security.SecureRandom; +import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; @@ -23,6 +25,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.module.afterburner.AfterburnerModule; +import io.mosip.kernel.core.keymanager.spi.ECKeyStore; +import io.mosip.kernel.core.util.DateUtils2; +import io.mosip.kernel.keymanagerservice.constant.KeyReferenceIdConsts; +import io.mosip.kernel.keymanagerservice.constant.KeymanagerErrorConstant; +import io.mosip.kernel.keymanagerservice.entity.KeyAlias; +import io.mosip.kernel.keymanagerservice.exception.NoUniqueAliasException; import io.mosip.kernel.signature.constant.SignatureConstant; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.util.encoders.Hex; @@ -98,6 +106,13 @@ public class CryptomanagerUtils { @Value("${mosip.kernel.keymanager.jwtEncrypt.validate.json:true}") private boolean confValidateJson; + /** The sign applicationid. */ + @Value("${mosip.sign.applicationid:KERNEL}") + private String signApplicationid; + + @Value("${mosip.sign-certificate-refid:SIGN}") + private String certificateSignRefID; + /** The key manager. */ @Autowired private KeymanagerService keyManager; @@ -108,6 +123,13 @@ public class CryptomanagerUtils { @Autowired private KeymanagerDBHelper dbHelper; + @Autowired + private ECKeyStore keyStore; + + /** Flag to generate and store Ed25519 key in real HSM. */ + @Value("${mosip.kernel.keymanager.ed25519.hsm.support.enabled:false}") + private boolean ed25519SupportFlag; + /** * Calls Key-Manager-Service to get public key of an application. * @@ -128,7 +150,7 @@ public Certificate getCertificate(CryptomanagerRequestDto cryptomanagerRequestDt * @param refId the ref id * @return the certificate data from key manager */ - private String getCertificateFromKeyManager(String appId, String refId) { + public String getCertificateFromKeyManager(String appId, String refId) { return keyManager.getCertificate(appId, Optional.ofNullable(refId)).getCertificate(); } @@ -273,6 +295,24 @@ public byte[] parseEncryptKeyHeader(byte[] encryptedKey){ return versionHeader; } + public String getAlgorithmNameFromHeader(byte[] encryptedData) { + int keyDelimiterIndex = 0; + keyDelimiterIndex = CryptoUtil.getSplitterIndex(encryptedData, keyDelimiterIndex, keySplitter); + byte[] algorithmBytes = Arrays.copyOfRange(encryptedData, 0, keyDelimiterIndex); + String algorithmName; + + if (Arrays.equals(algorithmBytes, CryptomanagerConstant.VERSION_EC256_R1)) { + algorithmName = CryptomanagerConstant.EC_SECP256R1; + } else if (Arrays.equals(algorithmBytes, CryptomanagerConstant.VERSION_EC256_K1)) { + algorithmName = CryptomanagerConstant.EC_SECP256K1; + } else if (Arrays.equals(algorithmBytes, CryptomanagerConstant.VERSION_EC_X25519)) { + algorithmName = CryptomanagerConstant.EC_X25519; + } else { + algorithmName = KeymanagerConstant.RSA; + } + return algorithmName; + } + public boolean isDataValid(String anyData) { return anyData != null && !anyData.trim().isEmpty(); } @@ -414,4 +454,106 @@ public boolean isJWSData(String data) { } return true; } -} + + public Object[] getEncryptedPrivateKey(String appId, Optional refId) { + + LocalDateTime localDateTime = DateUtils2.getUTCCurrentDateTime(); + Map> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime); + KeyAlias keyAlias = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS).getFirst(); + String ksAlias = keyAlias.getAlias(); + + if (!refId.isPresent() || refId.get().trim().isEmpty()) { + LOGGER.info(KeymanagerConstant.SESSIONID, KeymanagerConstant.EMPTY, KeymanagerConstant.EMPTY, + "Not valid reference Id. Getting private key from HSM."); + KeyStore.PrivateKeyEntry masterKeyEntry = keyStore.getAsymmetricKey(ksAlias); + PrivateKey masterPrivateKey = masterKeyEntry.getPrivateKey(); + Certificate masterCert = masterKeyEntry.getCertificate(); + return new Object[] {masterPrivateKey, masterCert}; + + } else if ((appId.equalsIgnoreCase(signApplicationid) && refId.isPresent() + && refId.get().equals(certificateSignRefID)) || + (refId.isPresent() && refId.get().equals(KeyReferenceIdConsts.EC_SECP256K1_SIGN.name())) || + (refId.isPresent() && refId.get().equals(KeyReferenceIdConsts.EC_SECP256R1_SIGN.name())) || + (refId.isPresent() && refId.get().equals(KeyReferenceIdConsts.ED25519_SIGN.name()) + && ed25519SupportFlag)) { + LOGGER.info(KeymanagerConstant.SESSIONID, KeymanagerConstant.EMPTY, KeymanagerConstant.EMPTY, + "Reference Id is present and it is " + refId.get() + " Signature Key ref Id. Getting private key from HSM."); + KeyStore.PrivateKeyEntry masterKeyEntry = keyStore.getAsymmetricKey(ksAlias); + PrivateKey masterPrivateKey = masterKeyEntry.getPrivateKey(); + Certificate masterCert = masterKeyEntry.getCertificate(); + return new Object[] {masterPrivateKey, masterCert}; + } else { + LOGGER.info(KeymanagerConstant.SESSIONID, KeymanagerConstant.EMPTY, KeymanagerConstant.EMPTY, + "Reference Id is present. Will get Certificate from DB store"); + + Optional dbKeyStore = dbHelper.getKeyStoreFromDB(ksAlias); + if (!dbKeyStore.isPresent()) { + LOGGER.error(KeymanagerConstant.SESSIONID, KeymanagerConstant.KEYFROMDB, dbKeyStore.toString(), + "Key in DBStore does not exist for this alias. Throwing exception"); + throw new NoUniqueAliasException(KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorCode(), + KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorMessage()); + } + String masterKeyAlias = dbKeyStore.get().getMasterAlias(); + String privateKeyObj = dbKeyStore.get().getPrivateKey(); + + if (ksAlias.equals(masterKeyAlias) || privateKeyObj.equals(KeymanagerConstant.KS_PK_NA)) { + LOGGER.error(KeymanagerConstant.SESSIONID, KeymanagerConstant.APPLICATIONID, null, + "Not Allowed to perform decryption with other domain key."); + throw new KeymanagerServiceException(KeymanagerErrorConstant.DECRYPTION_NOT_ALLOWED.getErrorCode(), + KeymanagerErrorConstant.DECRYPTION_NOT_ALLOWED.getErrorMessage()); + } + + KeyStore.PrivateKeyEntry masterKeyEntry = keyStore.getAsymmetricKey(dbKeyStore.get().getMasterAlias()); + PrivateKey masterPrivateKey = masterKeyEntry.getPrivateKey(); + PublicKey masterPublicKey = masterKeyEntry.getCertificate().getPublicKey(); + /** + * If the private key is in dbstore, then it will be first decrypted with + * application's master private key from softhsm's/HSM's keystore + */ + try { + return getObjects(dbKeyStore, masterPrivateKey, masterPublicKey); + } catch (Exception e) { + // need confirm the error message and code + LOGGER.error(KeymanagerConstant.SESSIONID, KeymanagerConstant.APPLICATIONID, null, + "Error while decrypting private key from DBStore. Throwing exception", e); + throw new KeymanagerServiceException(KeymanagerErrorConstant.NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(), + KeymanagerErrorConstant.NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage()); + } + } + } + + public Object[] getObjects(Optional dbKeyStore, PrivateKey masterPrivateKey, PublicKey masterPublicKey) { + byte[] decryptedPrivateKey = keymanagerUtil.decryptKey(CryptoUtil.decodeURLSafeBase64(dbKeyStore.get().getPrivateKey()), + masterPrivateKey, masterPublicKey); + + PublicKey publicKey = keymanagerUtil.convertToCertificate(dbKeyStore.get().getCertificateData()).getPublicKey(); + String algorithmName = publicKey.getAlgorithm(); + KeyFactory keyFactory = null; + PrivateKey privateKey = null; + try { + keyFactory = KeyFactory.getInstance(algorithmName); + privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKey)); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + Certificate certificate = keymanagerUtil.convertToCertificate(dbKeyStore.get().getCertificateData()); + return new Object[]{privateKey, certificate}; + } + + public byte[] getHeaderByte(String ecCurveName) { + byte[] headerBytes = new byte[0]; + if (ecCurveName.equalsIgnoreCase(CryptomanagerConstant.EC_SECP256R1)) { + headerBytes = CryptomanagerConstant.VERSION_EC256_R1; + } else if (ecCurveName.equalsIgnoreCase(CryptomanagerConstant.EC_SECP256K1)) { + headerBytes = CryptomanagerConstant.VERSION_EC256_K1; + } else if (ecCurveName.equalsIgnoreCase(CryptomanagerConstant.EC_X25519)) { + headerBytes = CryptomanagerConstant.VERSION_EC_X25519; + } else { + LOGGER.error(CryptomanagerConstant.SESSIONID, CryptomanagerConstant.ENCRYPT, CryptomanagerConstant.ENCRYPT, + "Unsupported EC Curve Name: " + ecCurveName); + throw new CryptoManagerSerivceException(CryptomanagerErrorCode.UNSUPPORTED_EC_CURVE.getErrorCode(), + CryptomanagerErrorCode.UNSUPPORTED_EC_CURVE.getErrorMessage() + ecCurveName); + } + return headerBytes; + } +} \ No newline at end of file diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/KeyGenerator.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/KeyGenerator.java index a3db18cc4..f228e186a 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/KeyGenerator.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/KeyGenerator.java @@ -67,6 +67,10 @@ public class KeyGenerator { @Value("${mosip.kernel.keygenerator.asymmetric.ed25519.algorithm-name:Ed25519}") private String asymmetricEDKeyAlgorithm; + /** ECC algorithm curve name */ + @Value("${mosip.kernel.keygenerator.ecc-curve-name:SECP256R1}") + private String eccCurve; + @Autowired private ECKeyStore keyStore; @@ -84,14 +88,24 @@ public SecretKey getSymmetricKey() { /** * This method generated Asymmetric key pairs * - * @return {@link KeyPair} which contain public nad private key + * @return {@link KeyPair} which contain public and private key */ public KeyPair getAsymmetricKey() { - KeyPairGenerator generator = KeyGeneratorUtils.getKeyPairGenerator(asymmetricKeyAlgorithm, asymmetricKeyLength, + KeyPairGenerator generator = KeyGeneratorUtils.getKeyPairGenerator(asymmetricKeyAlgorithm, asymmetricKeyLength, getSecureRandom()); return generator.generateKeyPair(); } + /** + * This method generated Asymmetric key pairs for ECC + * + * @return {@link KeyPair} which contain public and private key + */ + public KeyPair getECKeyPair() { + KeyPairGenerator generator = KeyGeneratorUtils.getECKeyPairGenerator(asymmetricKeyAlgorithm, eccCurve, getSecureRandom()); + return generator.generateKeyPair(); + } + public KeyPair getEd25519KeyPair() { KeyPairGenerator generator = KeyGeneratorUtils.getEdKeyPairGenerator(asymmetricEDKeyAlgorithm, getSecureRandom()); return generator.generateKeyPair(); diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/util/KeyGeneratorUtils.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/util/KeyGeneratorUtils.java index 25062736f..e49286ca9 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/util/KeyGeneratorUtils.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/util/KeyGeneratorUtils.java @@ -19,6 +19,7 @@ import io.mosip.kernel.core.exception.NoSuchAlgorithmException; import io.mosip.kernel.keygenerator.bouncycastle.constant.KeyGeneratorExceptionConstant; import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant; +import org.springframework.beans.factory.annotation.Value; /** * This is a utils class for keygenerator @@ -105,6 +106,19 @@ public static KeyPairGenerator getEdKeyPairGenerator(String algorithmName, Secur } + public static KeyPairGenerator getECKeyPairGenerator(String algorithmName, String eccCurve, SecureRandom secureRandom) { + KeyPairGenerator generator = null; + try { + generator = KeyPairGenerator.getInstance(algorithmName, provider); + generator.initialize(new ECGenParameterSpec(eccCurve), secureRandom); + return generator; + } catch (java.security.NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new NoSuchAlgorithmException( + KeyGeneratorExceptionConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(), + KeyGeneratorExceptionConstant.MOSIP_NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e); + } + } + public static PrivateKey generatePrivate(String algorithmName, byte[] privateKeyData) { try { return KeyFactory.getInstance(algorithmName, provider).generatePrivate(new PKCS8EncodedKeySpec(privateKeyData)); diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/constant/KeymanagerConstant.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/constant/KeymanagerConstant.java index 2679602ee..7fca6cec5 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/constant/KeymanagerConstant.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/constant/KeymanagerConstant.java @@ -66,4 +66,9 @@ private KeymanagerConstant() { public static final String ASYM_KEY_ED_ALGORITHM = "ASYM_KEY_ED_ALGORITHM"; + public static final String RSA_SIGN_ALGORITHM = "SHA256withRSA"; + + public static final String EC_SIGN_ALGORITHM = "SHA256withECDSA"; + + public static final String ED_SIGN_ALGORITHM = "Ed25519"; } diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/impl/pkcs/PKCS12KeyStoreImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/impl/pkcs/PKCS12KeyStoreImpl.java index 5e2a11a70..444249e5b 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/impl/pkcs/PKCS12KeyStoreImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/impl/pkcs/PKCS12KeyStoreImpl.java @@ -476,7 +476,7 @@ private KeyPair generateKeyPair(String keyType) { .collect(Collectors.toList()); if (KeymanagerConstant.RSA_KEY_TYPE.equals(keyType)) return generateRSAKeyPair(); - else if (ecCurvesList.contains(keyType)) + else if (ecCurvesList.contains(keyType.toUpperCase())) return generateECKeyPair(keyType); else if (KeymanagerConstant.ED25519_KEY_TYPE.equals(keyType)) return generateEd25519KeyPair(); diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/util/CertificateUtility.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/util/CertificateUtility.java index 82024c1d9..ebeff7a74 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/util/CertificateUtility.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanager/hsm/util/CertificateUtility.java @@ -38,6 +38,7 @@ import io.mosip.kernel.core.keymanager.exception.KeystoreProcessingException; import io.mosip.kernel.core.keymanager.model.CertificateParameters; import io.mosip.kernel.keymanager.hsm.constant.KeymanagerErrorCode; +import org.springframework.beans.factory.annotation.Value; /** * Certificate utility to generate and sign X509 Certificate @@ -106,10 +107,10 @@ public static X509Certificate generateX509Certificate(PrivateKey signPrivateKey, ExtendedCertificateParameters extendedCertParams = (ExtendedCertificateParameters) certParams; List sanDtoList = extendedCertParams.getSubjectAlternativeNames(); GeneralName[] sanArray = getCertificateSAN(sanDtoList, publicKey); - return generateX509Certificate(signPrivateKey, publicKey, certIssuer, certSubject, signAlgorithm, providerName, + return generateX509Certificate(signPrivateKey, publicKey, certIssuer, certSubject, getSignatureAlgorithm(signPrivateKey), providerName, certParams.getNotBefore(), certParams.getNotAfter(), keyUsage, basicConstraints, sanArray); }else { - return generateX509Certificate(signPrivateKey, publicKey, certIssuer, certSubject, signAlgorithm, providerName, + return generateX509Certificate(signPrivateKey, publicKey, certIssuer, certSubject, getSignatureAlgorithm(signPrivateKey), providerName, certParams.getNotBefore(), certParams.getNotAfter(), keyUsage, basicConstraints); } } @@ -120,7 +121,7 @@ private static X509Certificate generateX509Certificate(PrivateKey signPrivateKey try { BigInteger certSerialNum = new BigInteger(Long.toString(new SecureRandom().nextLong())); - ContentSigner certContentSigner = new JcaContentSignerBuilder(signAlgorithm).setProvider(providerName).build(signPrivateKey); + ContentSigner certContentSigner = new JcaContentSignerBuilder(getSignatureAlgorithm(signPrivateKey)).setProvider(providerName).build(signPrivateKey); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(certIssuer, certSerialNum, getDateFromLocalDateTime(notBefore), getDateFromLocalDateTime(notAfter), certSubject, publicKey); JcaX509ExtensionUtils certExtUtils = new JcaX509ExtensionUtils(); @@ -143,7 +144,7 @@ private static X509Certificate generateX509Certificate(PrivateKey signPrivateKey try { BigInteger certSerialNum = new BigInteger(Long.toString(new SecureRandom().nextLong())); - ContentSigner certContentSigner = new JcaContentSignerBuilder(signAlgorithm).setProvider(providerName).build(signPrivateKey); + ContentSigner certContentSigner = new JcaContentSignerBuilder(getSignatureAlgorithm(signPrivateKey)).setProvider(providerName).build(signPrivateKey); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(certIssuer, certSerialNum, getDateFromLocalDateTime(notBefore), getDateFromLocalDateTime(notAfter), certSubject, publicKey); JcaX509ExtensionUtils certExtUtils = new JcaX509ExtensionUtils(); @@ -294,4 +295,17 @@ private static GeneralName[] getCertificateSAN(List throw new RuntimeException(e); } } + + private static String getSignatureAlgorithm(PrivateKey privateKey) { + + String keyAlgorithm = privateKey.getAlgorithm(); + if (keyAlgorithm.equals(KeymanagerConstant.EC_KEY_TYPE)) + return io.mosip.kernel.keymanager.hsm.constant.KeymanagerConstant.EC_SIGN_ALGORITHM; + else if (keyAlgorithm.equals(KeymanagerConstant.ED25519_KEY_TYPE) || + keyAlgorithm.equals(KeymanagerConstant.ED25519_ALG_OID) || + keyAlgorithm.equals(KeymanagerConstant.EDDSA_KEY_TYPE)) + return io.mosip.kernel.keymanager.hsm.constant.KeymanagerConstant.ED_SIGN_ALGORITHM; + + return io.mosip.kernel.keymanager.hsm.constant.KeymanagerConstant.RSA_SIGN_ALGORITHM; + } } diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/constant/KeymanagerConstant.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/constant/KeymanagerConstant.java index b261840ab..7147fa38f 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/constant/KeymanagerConstant.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/constant/KeymanagerConstant.java @@ -1,5 +1,6 @@ package io.mosip.kernel.keymanagerservice.constant; +import java.security.PublicKey; import java.time.format.DateTimeFormatter; /** @@ -248,4 +249,8 @@ private KeymanagerConstant() { public static final String GET_CERTIFICATE_CHAIN = "Get Certificate Trust Chain"; public static final String DEFAULT_VALUE = "default"; + + public static final String EC_SECP256R1_OID = "1.2.840.10045.3.1.7"; + + public static final String EC_SECP256K1_OID = "1.3.132.0.10"; } diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/helper/SessionKeyDecryptorHelper.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/helper/SessionKeyDecryptorHelper.java index cbcbbe88b..e5b396068 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/helper/SessionKeyDecryptorHelper.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/helper/SessionKeyDecryptorHelper.java @@ -377,7 +377,7 @@ private Object[] getPrivateKey(String referenceId, KeyAlias fetchedKeyAlias) { try { byte[] decryptedPrivateKey = keymanagerUtil.decryptKey(CryptoUtil.decodeURLSafeBase64(dbKeyStore.get().getPrivateKey()), masterPrivateKey, masterPublicKey); - KeyFactory keyFactory = KeyFactory.getInstance(KeymanagerConstant.RSA); + KeyFactory keyFactory = KeyFactory.getInstance(masterPrivateKey.getAlgorithm()); PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKey)); Certificate certificate = keymanagerUtil.convertToCertificate(dbKeyStore.get().getCertificateData()); return new Object[] {privateKey, certificate}; diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java index db603daf1..0a454f3c0 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java @@ -22,6 +22,7 @@ import javax.security.auth.x500.X500Principal; +import io.mosip.kernel.core.signatureutil.spi.SignatureUtil; import io.mosip.kernel.core.util.DateUtils2; import io.mosip.kernel.keymanagerservice.dto.AllCertificatesDataResponseDto; import io.mosip.kernel.keymanagerservice.dto.CSRGenerateRequestDto; @@ -80,7 +81,6 @@ import io.mosip.kernel.keymanagerservice.service.KeymanagerService; import io.mosip.kernel.keymanagerservice.util.KeymanagerUtil; import io.mosip.kernel.keymanagerservice.validator.ECKeyPairGenRequestValidator; -import io.mosip.kernel.signature.util.SignatureUtil; /** * This class provides the implementation for the methods of KeymanagerService @@ -123,6 +123,14 @@ public class KeymanagerServiceImpl implements KeymanagerService { @Value("${mosip.kernel.keymanager.ed25519.hsm.support.enabled:false}") private boolean ed25519SupportFlag; + /** Master Key generation algorithm */ + @Value("${mosip.kernel.keygenerator.asymmetric-algorithm-name:RSA}") + private String masterKeyAlgorithm; + + /** ECC algorithm curve name */ + @Value("${mosip.kernel.keygenerator.ecc-curve-name:SECP256R1}") + private String eccCurve; + /** * Keystore instance to handles and store cryptographic keys. */ @@ -162,7 +170,7 @@ public class KeymanagerServiceImpl implements KeymanagerService { @Autowired SubjectAlternativeNamesHelper sanHelper; - private static Map ecRefIdsAlgoNamesMap = new HashMap<>(); + private static final Map ecRefIdsAlgoNamesMap = new HashMap<>(); static { ecRefIdsAlgoNamesMap.put(KeyReferenceIdConsts.EC_SECP256K1_SIGN.name(), ECCurves.SECP256K1.name()); @@ -367,17 +375,23 @@ private CertificateInfo getCertificateFromDBStore(String applic String encryptedPrivateKey; alias = UUID.randomUUID().toString(); KeyPair keypair = null; + + CertificateInfo certInfo; if (referenceId.equals(KeyReferenceIdConsts.ED25519_SIGN.name()) && isSignKey) { keypair = keyGenerator.getEd25519KeyPair(); + certInfo = getCertificateFromHSM(applicationId, timeStamp, KeymanagerConstant.EMPTY); + } else if (!masterKeyAlgorithm.equals(KeymanagerConstant.RSA)) { + keypair = keyGenerator.getECKeyPair(); + certInfo = getCertificateFromHSM(applicationId, timeStamp, KeymanagerConstant.EMPTY); } else { keypair = keyGenerator.getAsymmetricKey(); + certInfo = getCertificateFromHSM(applicationId, timeStamp, KeymanagerConstant.EMPTY); } PrivateKey privateKey = keypair.getPrivate(); /** * Will get application's master key information from HSM. On first request for * an applicationId and duration, will create a new keypair. */ - CertificateInfo certInfo = getCertificateFromHSM(applicationId, timeStamp, KeymanagerConstant.EMPTY); X509Certificate hsmX509Cert = certInfo.getCertificate(); PublicKey masterPublicKey = hsmX509Cert.getPublicKey(); @@ -724,8 +738,13 @@ private CertificateInfo generateAndStoreAsymmetricKey(String al genAlias = certificateInfo.getAlias(); } return new CertificateInfo<>(genAlias, x509Cert); - } - keyStore.generateAndStoreAsymmetricKey(alias, rootKeyAlias, certParams); + } + + if (!masterKeyAlgorithm.trim().equals(KeymanagerConstant.RSA)) { + keyStore.generateAndStoreAsymmetricKey(alias, rootKeyAlias, certParams, eccCurve); + } else { + keyStore.generateAndStoreAsymmetricKey(alias, rootKeyAlias, certParams); + } x509Cert = (X509Certificate) keyStore.getCertificate(alias); storeAsymmetricKey(alias, applicationId, refId, keyAliasMap, x509Cert, generationDateTime, expiryDateTime); return new CertificateInfo<>(genAlias, x509Cert); @@ -967,7 +986,7 @@ private Object[] getKeyDetails(Optional allowedAppIds; + @Value("${mosip.kernel.keygenerator.ecc-curve-name:SECP256R1}") + private String ecCurveName; + // Default to 1440 (24 hours) - can be configured via application properties @Value("${mosip.kernel.keymgr.truststore.cache.expiry.inMins:1440}") private long trustAnchorsCacheExpiryMinutes; @@ -213,6 +219,9 @@ public class KeymanagerUtil { @Autowired private CryptoCoreSpec cryptoCore; + @Autowired + private EcCryptoOperation ecCryptoOperation; + @Autowired SubjectAlternativeNamesHelper sanService; @@ -304,10 +313,18 @@ public T setMetaData(T entity) { * @return encrypted key */ public byte[] encryptKey(PrivateKey privateKey, PublicKey masterKey) { - SecretKey symmetricKey = keyGenerator.getSymmetricKey(); - byte[] encryptedPrivateKey = cryptoCore.symmetricEncrypt(symmetricKey, privateKey.getEncoded(), null); - byte[] encryptedSymmetricKey = cryptoCore.asymmetricEncrypt(masterKey, symmetricKey.getEncoded()); - return CryptoUtil.combineByteArray(encryptedPrivateKey, encryptedSymmetricKey, keySplitter); + byte[] encryptedSymmetricKey; + byte[] encryptedKey = null; + + if (masterKey.getAlgorithm().equalsIgnoreCase(KeymanagerConstant.RSA)) { + SecretKey symmetricKey = keyGenerator.getSymmetricKey(); + byte[] encryptedPrivateKey = cryptoCore.symmetricEncrypt(symmetricKey, privateKey.getEncoded(), null); + encryptedSymmetricKey = cryptoCore.asymmetricEncrypt(masterKey, symmetricKey.getEncoded()); + encryptedKey = CryptoUtil.combineByteArray(encryptedPrivateKey, encryptedSymmetricKey, keySplitter); + } else { + encryptedKey = ecCryptoOperation.asymmetricEcEncrypt(masterKey, privateKey.getEncoded(), ecCurveName); + } + return encryptedKey; } /** @@ -323,25 +340,29 @@ public byte[] decryptKey(byte[] key, PrivateKey privateKey, PublicKey publicKey) public byte[] decryptKey(byte[] key, PrivateKey privateKey, PublicKey publicKey, String keystoreType) { - final int keySplitterLength = keySplitter.length(); - final int keyDelimiterIndex = CryptoUtil.getSplitterIndex(key, 0, keySplitter); - if (keyDelimiterIndex < 0 || keyDelimiterIndex + keySplitterLength >= key.length) { - throw new IllegalArgumentException("Splitter not found or invalid key format"); - } - - // Split encrypted key and encrypted data - byte[] encryptedKey = new byte[keyDelimiterIndex]; - System.arraycopy(key, 0, encryptedKey, 0, keyDelimiterIndex); - - int encryptedDataLen = key.length - (keyDelimiterIndex + keySplitterLength); - byte[] encryptedData = new byte[encryptedDataLen]; - System.arraycopy(key, keyDelimiterIndex + keySplitterLength, encryptedData, 0, encryptedDataLen); - // Decrypt asymmetric key - byte[] decryptedSymmetricKey = cryptoCore.asymmetricDecrypt(privateKey, publicKey, encryptedKey, keystoreType); - SecretKey symmetricKey = new SecretKeySpec(decryptedSymmetricKey, symmetricAlgorithmName); + if (privateKey.getAlgorithm().equalsIgnoreCase(KeymanagerConstant.RSA)) { + final int keySplitterLength = keySplitter.length(); + final int keyDelimiterIndex = CryptoUtil.getSplitterIndex(key, 0, keySplitter); + if (keyDelimiterIndex < 0 || keyDelimiterIndex + keySplitterLength >= key.length) { + throw new IllegalArgumentException("Splitter not found or invalid key format"); + } - // Symmetric decryption (AAD = null) - return cryptoCore.symmetricDecrypt(symmetricKey, encryptedData, null); + // Split encrypted key and encrypted data + byte[] encryptedKey = new byte[keyDelimiterIndex]; + System.arraycopy(key, 0, encryptedKey, 0, keyDelimiterIndex); + + int encryptedDataLen = key.length - (keyDelimiterIndex + keySplitterLength); + byte[] encryptedData = new byte[encryptedDataLen]; + System.arraycopy(key, keyDelimiterIndex + keySplitterLength, encryptedData, 0, encryptedDataLen); + // Decrypt asymmetric key + byte[] decryptedSymmetricKey = cryptoCore.asymmetricDecrypt(privateKey, publicKey, encryptedKey, keystoreType); + SecretKey symmetricKey = new SecretKeySpec(decryptedSymmetricKey, symmetricAlgorithmName); + + // Symmetric decryption (AAD = null) + return cryptoCore.symmetricDecrypt(symmetricKey, encryptedData, null); + } else { + return ecCryptoOperation.asymmetricEcDecrypt(privateKey, key, null, getEcCurveName(publicKey)); + } } /** @@ -785,4 +806,21 @@ public void purgeKeyAliasTrustAnchorsCache() { "Purging Key alias Trust Anchors Cache because new key generated or new certificate uploaded."); keyAliasTrustAnchorsCache.expireAt("default", Expiry.NOW); } + + public String getEcCurveName(PublicKey publicKey) { + SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) subjectPublicKeyInfo.getAlgorithm().getParameters(); + String curveName; + if (KeymanagerConstant.EC_SECP256R1_OID.equals(oid.getId())) { + curveName = ECCurves.SECP256R1.name(); + } else if (KeymanagerConstant.EC_SECP256K1_OID.equals(oid.getId())) { + curveName = ECCurves.SECP256K1.name(); + } else { + throw new io.mosip.kernel.core.exception.NoSuchAlgorithmException( + KeymanagerErrorConstant.NOT_SUPPORTED_CURVE_VALUE.getErrorCode(), + KeymanagerErrorConstant.NOT_SUPPORTED_CURVE_VALUE.getErrorMessage() + ); + } + return curveName; + } } diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymigrate/service/impl/KeyMigratorServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymigrate/service/impl/KeyMigratorServiceImpl.java index 310034ed7..e75f64f12 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymigrate/service/impl/KeyMigratorServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymigrate/service/impl/KeyMigratorServiceImpl.java @@ -88,6 +88,13 @@ public class KeyMigratorServiceImpl implements KeyMigratorService { @Value("${mosip.kernel.zkcrypto.wrap.algorithm-name:AES/ECB/NoPadding}") private String aesECBTransformation; + /** Master Key generation algorithm */ + @Value("${mosip.kernel.keygenerator.asymmetric-algorithm-name:RSA}") + private String keyAlgorithm; + + @Value("${mosip.kernel.keygenerator.ecc-curve-name:SECP256R1}") + private String ecCurveName; + /** * KeymanagerDBHelper instance to handle all DB operations */ @@ -283,7 +290,12 @@ public ZKKeyMigrateCertficateResponseDto getZKTempCertificate() { LocalDateTime expiryDateTime = localDateTimeStamp.plusDays(1); CertificateParameters certParams = keymanagerUtil.getCertificateParameters(KeyMigratorConstants.ZK_CERT_COMMON_NAME, localDateTimeStamp, expiryDateTime); - keyStore.generateAndStoreAsymmetricKey(alias, null, certParams); + + if (keyAlgorithm.equalsIgnoreCase(KeymanagerConstant.RSA)) { + keyStore.generateAndStoreAsymmetricKey(alias, null, certParams); + } else { + keyStore.generateAndStoreAsymmetricKey(alias, null, certParams, ecCurveName); + } X509Certificate x509Cert = (X509Certificate) keyStore.getCertificate(alias); String certThumbprint = cryptomanagerUtil.getCertificateThumbprintInHex(x509Cert); // Using certThumbprint not generation time because in case more than one migration master key may be diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java index cd117593a..86264544a 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java @@ -29,6 +29,7 @@ import io.mosip.kernel.signature.constant.SignatureProviderEnum; import io.mosip.kernel.signature.dto.*; import io.mosip.kernel.signature.service.SignatureServicev2; +import io.mosip.kernel.keymanagerservice.constant.ECCurves; import org.apache.commons.codec.binary.Base64; import org.jose4j.jca.ProviderContext; import org.jose4j.jwa.AlgorithmFactory; @@ -124,6 +125,9 @@ public class SignatureServiceImpl implements SignatureService, SignatureServicev @Value("${mosip.kernel.keymanager.signature.kid.prepend:}") private String kidPrepend; + @Value("${mosip.sign-certificate-refid:SIGN}") + private String certificateSignRefID; + /** * Utility to generate Metadata */ @@ -160,11 +164,12 @@ public class SignatureServiceImpl implements SignatureService, SignatureServicev private static Map JWT_SIGNATURE_ALGO_IDENT = new HashMap<>(); static { - JWT_SIGNATURE_ALGO_IDENT.put(SignatureConstant.BLANK, AlgorithmIdentifiers.RSA_USING_SHA256); - JWT_SIGNATURE_ALGO_IDENT.put(SignatureConstant.REF_ID_SIGN_CONST, AlgorithmIdentifiers.RSA_USING_SHA256); + JWT_SIGNATURE_ALGO_IDENT.put(KeymanagerConstant.RSA, AlgorithmIdentifiers.RSA_USING_SHA256); JWT_SIGNATURE_ALGO_IDENT.put(KeyReferenceIdConsts.EC_SECP256K1_SIGN.name(), AlgorithmIdentifiers.ECDSA_USING_SECP256K1_CURVE_AND_SHA256); JWT_SIGNATURE_ALGO_IDENT.put(KeyReferenceIdConsts.EC_SECP256R1_SIGN.name(), AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256); JWT_SIGNATURE_ALGO_IDENT.put(KeyReferenceIdConsts.ED25519_SIGN.name(), AlgorithmIdentifiers.EDDSA); + JWT_SIGNATURE_ALGO_IDENT.put(ECCurves.SECP256R1.name(), AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256); + JWT_SIGNATURE_ALGO_IDENT.put(ECCurves.SECP256K1.name(), AlgorithmIdentifiers.ECDSA_USING_SECP256K1_CURVE_AND_SHA256); } // ---- FAST PATH CACHES ---- diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java index a7d5ef5d7..cb532bf46 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java @@ -6,6 +6,7 @@ import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; +import java.security.PublicKey; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; @@ -28,6 +29,9 @@ import com.nimbusds.jose.util.Base64; import com.nimbusds.jose.util.Base64URL; +import io.mosip.kernel.keymanagerservice.constant.ECCurves; +import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant; +import io.mosip.kernel.keymanagerservice.constant.KeymanagerErrorConstant; import io.mosip.kernel.keymanagerservice.constant.KeymanagerErrorConstant; import io.mosip.kernel.keymanagerservice.exception.KeymanagerServiceException; import io.mosip.kernel.keymanagerservice.util.KeymanagerUtil; @@ -53,6 +57,11 @@ import java.util.stream.Collectors; import java.util.Arrays; import com.nimbusds.jose.JOSEObjectType; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.jose4j.jws.AlgorithmIdentifiers; + +import java.lang.*; /** * Utility class for Signature Service @@ -207,7 +216,7 @@ public static String convertHexToBase64(String anyHexString) { } catch (DecoderException | NoSuchAlgorithmException e) { // ignore this exception. LOGGER.warn(SignatureConstant.SESSIONID, SignatureConstant.JWS_SIGN, SignatureConstant.BLANK, - "Warning thrown when converting hex data to base64 encoded data."); + "Warning thrown when converting hex data to base64 encoded data."); // not throwing exception, as this function is added to include kid in jwt signature. // in case any error in conversion kid will not be added in jwt header. } @@ -604,4 +613,26 @@ public List getCertificateTrustChain(X509Certificate x509 return certificateChain; } + + public static String getJwtSignAlgorithm(X509Certificate x509Certificate) { + PublicKey publicKey = x509Certificate.getPublicKey(); + String algorithm = publicKey.getAlgorithm(); + + if (KeymanagerConstant.EC_KEY_TYPE.equalsIgnoreCase(algorithm)) { + SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + ASN1ObjectIdentifier curveOid = (ASN1ObjectIdentifier) subjectPublicKeyInfo.getAlgorithm().getParameters(); + + return mapCurveOidToCurveName(curveOid.getId()); + } + return AlgorithmIdentifiers.RSA_USING_SHA256; + } + + private static String mapCurveOidToCurveName(String oid) { + return switch (oid) { + case KeymanagerConstant.EC_SECP256R1_OID -> AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256; + case KeymanagerConstant.EC_SECP256K1_OID -> AlgorithmIdentifiers.ECDSA_USING_SECP256K1_CURVE_AND_SHA256; + default -> throw new io.mosip.kernel.core.exception.NoSuchAlgorithmException(KeymanagerErrorConstant.NOT_SUPPORTED_CURVE_VALUE.getErrorCode(), + KeymanagerErrorConstant.NOT_SUPPORTED_CURVE_VALUE.getErrorMessage()); + }; + } } \ No newline at end of file diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java index 8ca9db1a1..1b95e904b 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java @@ -33,6 +33,9 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import io.mosip.kernel.core.util.DateUtils2; +import io.mosip.kernel.cryptomanager.service.EcCryptoOperation; +import io.mosip.kernel.keymanager.hsm.util.CertificateUtility; +import io.mosip.kernel.keymanagerservice.repository.KeyAliasRepository; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; @@ -134,6 +137,12 @@ public class ZKCryptoManagerServiceImpl implements ZKCryptoManagerService, Initi @Autowired CryptomanagerUtils cryptomanagerUtil; + @Autowired + EcCryptoOperation ecCryptoCore; + + @Autowired + KeyAliasRepository keyAliasRepository; + @Autowired private CryptoCoreSpec cryptoCore; @@ -453,7 +462,10 @@ private String encryptRandomKey(Key secretRandomKey) { String certificateData = dbKeyStore.get().getCertificateData(); X509Certificate x509Cert = (X509Certificate) keymanagerUtil.convertToCertificate(certificateData); PublicKey publicKey = x509Cert.getPublicKey(); - byte[] encryptedRandomKey = cryptoCore.asymmetricEncrypt(publicKey, secretRandomKey.getEncoded()); + + byte[] encryptedRandomKey = publicKey.getAlgorithm().equalsIgnoreCase(KeymanagerConstant.RSA) ? cryptoCore.asymmetricEncrypt(publicKey, secretRandomKey.getEncoded()) : + ecCryptoCore.asymmetricEcEncrypt(publicKey, secretRandomKey.getEncoded(), keymanagerUtil.getEcCurveName(publicKey)); + byte[] certThumbprint = cryptomanagerUtil.getCertificateThumbprint(x509Cert); byte[] concatedData = cryptomanagerUtil.concatCertThumbprint(certThumbprint, encryptedRandomKey); encryptedRandomKeyList.add(CryptoUtil.encodeToURLSafeBase64(concatedData)); @@ -477,29 +489,42 @@ public ReEncryptRandomKeyResponseDto zkReEncryptRandomKey(String encryptedKey){ Map> keyAliasMap = dbHelper.getKeyAliases(pubKeyApplicationId, pubKeyReferenceId, localDateTimeStamp); keyAliases = keyAliasMap.get(KeymanagerConstant.KEYALIAS); } + + String kyAlias = null; String encRandomKey = null; + byte[] encRandomKeyBytes = null; for (String encKey : encryptedKeyArr) { byte[] encKeyBytes = CryptoUtil.decodeURLSafeBase64(encKey); byte[] certThumbprint = Arrays.copyOfRange(encKeyBytes, 0, CryptomanagerConstant.THUMBPRINT_LENGTH); + encRandomKeyBytes = Arrays.copyOfRange(encKeyBytes, CryptomanagerConstant.THUMBPRINT_LENGTH, encKeyBytes.length); String certThumbprintHex = Hex.toHexString(certThumbprint).toUpperCase(); Optional keyAlias = keyAliases.stream().filter(alias -> alias.getCertThumbprint().equals(certThumbprintHex)) .findFirst(); - + kyAlias = keyAlias.map(KeyAlias::getAlias).orElse(null); if (!keyAlias.isPresent()) { continue; } encRandomKey = encKey; break; } + + Optional dbKeyStore = keyStoreRepository.findByAlias(kyAlias); + Optional keyAliasObj = keyAliasRepository.findById(Objects.requireNonNull(kyAlias)); + String certificateData = dbKeyStore.get().getCertificateData(); + X509Certificate x509Cert = (X509Certificate) keymanagerUtil.convertToCertificate(certificateData); + if (Objects.isNull(encRandomKey)) { LOGGER.error(ZKCryptoManagerConstants.SESSIONID, ZKCryptoManagerConstants.RE_ENCRYPT_RANDOM_KEY, ZKCryptoManagerConstants.RE_ENCRYPT_RANDOM_KEY, "Thumbprint matching key not found in DB."); throw new ZKCryptoException(ZKCryptoErrorConstants.INVALID_ENCRYPTED_RANDOM_KEY.getErrorCode(), ZKCryptoErrorConstants.INVALID_ENCRYPTED_RANDOM_KEY.getErrorMessage()); } - SymmetricKeyRequestDto symmetricKeyRequestDto = new SymmetricKeyRequestDto( - pubKeyApplicationId, localDateTimeStamp, pubKeyReferenceId, encRandomKey, true); - String randomKey = keyManagerService.decryptSymmetricKey(symmetricKeyRequestDto).getSymmetricKey(); + + PrivateKey privateKey = (PrivateKey) cryptomanagerUtil.getEncryptedPrivateKey(keyAliasObj.get().getApplicationId(), Optional.ofNullable(keyAliasObj.get().getReferenceId()))[0]; + SymmetricKeyRequestDto symmetricKeyRequestDto = new SymmetricKeyRequestDto(pubKeyApplicationId, localDateTimeStamp, pubKeyReferenceId, encRandomKey, true); + + String randomKey = x509Cert.getPublicKey().getAlgorithm().equalsIgnoreCase(KeymanagerConstant.RSA) ? keyManagerService.decryptSymmetricKey(symmetricKeyRequestDto).getSymmetricKey() : + CryptoUtil.encodeToURLSafeBase64(ecCryptoCore.asymmetricEcDecrypt(privateKey, encRandomKeyBytes, null, keymanagerUtil.getEcCurveName(x509Cert.getPublicKey()))); String encryptedRandomKey = getEncryptedRandomKey(Base64.getEncoder().encodeToString(CryptoUtil.decodeURLSafeBase64(randomKey))); ReEncryptRandomKeyResponseDto responseDto = new ReEncryptRandomKeyResponseDto(); responseDto.setEncryptedKey(encryptedRandomKey); diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java new file mode 100644 index 000000000..c4adc1c81 --- /dev/null +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java @@ -0,0 +1,26 @@ +package io.mosip.kernel.cryptomanager.test.service; + +import io.mosip.kernel.clientcrypto.test.ClientCryptoTestBootApplication; +import io.mosip.kernel.cryptomanager.service.impl.EcCryptoOperationImpl; +import io.mosip.kernel.keymanagerservice.test.KeymanagerTestBootApplication; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest(classes = { KeymanagerTestBootApplication.class }) +@RunWith(SpringRunner.class) +public class EcCryptoOperationTest { + + @Autowired + private EcCryptoOperationImpl service; + + private final String privateKey = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCC6m5SEHQms8YoFUfABl3P918oNQwIJeGDukOeNV6e8Hw=="; + public final String publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvMnarFay31WXcB/xsxBEOccl6+qJ88ctrzF9Rj9VqrLXk0Camh5x04cuA2cI9V8UWx9EhvPV7Wg4oS2aGs29Kg=="; + + @Test + public void testAsymmetricEcEncrypt() { + + } +} From d731dcb9e907787269400d2e81558377ba0eec65 Mon Sep 17 00:00:00 2001 From: nagendra0721 Date: Thu, 18 Jun 2026 00:14:41 +0530 Subject: [PATCH 2/5] #523: Enable keymanager to support ECC algorithm during encryption and decryption Signed-off-by: nagendra0721 --- .../service/impl/KeymanagerServiceImpl.java | 6 +++++- .../keymanagerservice/util/KeymanagerUtil.java | 1 - .../service/impl/SignatureServiceImpl.java | 13 +++++++++---- .../mosip/kernel/signature/util/SignatureUtil.java | 1 - .../service/impl/ZKCryptoManagerServiceImpl.java | 12 ++++++------ 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java index 0a454f3c0..58abcbdb9 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/service/impl/KeymanagerServiceImpl.java @@ -255,7 +255,11 @@ private ImmutablePair generateKeyPairInHSM(String alias return ImmutablePair.of(uniqueIdentifier, x509Certificate); } } else { - keyStore.generateAndStoreAsymmetricKey(alias, rootKeyAlias, certParams); + if (!masterKeyAlgorithm.trim().equals(KeymanagerConstant.RSA)) { + keyStore.generateAndStoreAsymmetricKey(alias, rootKeyAlias, certParams, eccCurve); + } else { + keyStore.generateAndStoreAsymmetricKey(alias, rootKeyAlias, certParams); + } } X509Certificate x509Cert = (X509Certificate) keyStore.getCertificate(alias); String certThumbprint = cryptomanagerUtil.getCertificateThumbprintInHex(x509Cert); diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/util/KeymanagerUtil.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/util/KeymanagerUtil.java index 2b23631ad..8b3ebc587 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/util/KeymanagerUtil.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keymanagerservice/util/KeymanagerUtil.java @@ -52,7 +52,6 @@ import io.mosip.kernel.keymanagerservice.service.KeymanagerService; import io.mosip.kernel.partnercertservice.constant.PartnerCertManagerConstants; import jakarta.annotation.PostConstruct; -import io.mosip.kernel.core.keymanager.spi.KeyStore; import io.mosip.kernel.cryptomanager.service.EcCryptoOperation; import io.mosip.kernel.keymanagerservice.constant.ECCurves; import org.apache.commons.codec.binary.Base64; diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java index 86264544a..064a02f67 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/service/impl/SignatureServiceImpl.java @@ -410,10 +410,15 @@ private String sign(String dataToSign, SignatureCertificate certificateResponse, } final String keyId = SignatureUtil.convertHexToBase64(certificateResponse.getUniqueIdentifier()); - // Alg selection from referenceId (same defaults) - String algoString = JWT_SIGNATURE_ALGO_IDENT.get(referenceId); - if (algoString == null || algoString.isBlank()) { - algoString = AlgorithmIdentifiers.RSA_USING_SHA256; + // Alg selection: for default SIGN/empty refID derive from actual cert (handles EC sign certs) + String algoString; + if (referenceId.equals(KeymanagerConstant.EMPTY) || referenceId.equals(certificateSignRefID)) { + algoString = SignatureUtil.getJwtSignAlgorithm(x509Certificate); + } else { + algoString = JWT_SIGNATURE_ALGO_IDENT.get(referenceId); + if (algoString == null || algoString.isBlank()) { + algoString = AlgorithmIdentifiers.RSA_USING_SHA256; + } } // --- Header caching: build a stable cache key for this exact header shape --- diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java index cb532bf46..4ae8e012a 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/signature/util/SignatureUtil.java @@ -32,7 +32,6 @@ import io.mosip.kernel.keymanagerservice.constant.ECCurves; import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant; import io.mosip.kernel.keymanagerservice.constant.KeymanagerErrorConstant; -import io.mosip.kernel.keymanagerservice.constant.KeymanagerErrorConstant; import io.mosip.kernel.keymanagerservice.exception.KeymanagerServiceException; import io.mosip.kernel.keymanagerservice.util.KeymanagerUtil; import io.mosip.kernel.partnercertservice.service.spi.PartnerCertificateManagerService; diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java index 1b95e904b..688203397 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/zkcryptoservice/service/impl/ZKCryptoManagerServiceImpl.java @@ -508,18 +508,18 @@ public ReEncryptRandomKeyResponseDto zkReEncryptRandomKey(String encryptedKey){ break; } - Optional dbKeyStore = keyStoreRepository.findByAlias(kyAlias); - Optional keyAliasObj = keyAliasRepository.findById(Objects.requireNonNull(kyAlias)); - String certificateData = dbKeyStore.get().getCertificateData(); - X509Certificate x509Cert = (X509Certificate) keymanagerUtil.convertToCertificate(certificateData); - if (Objects.isNull(encRandomKey)) { - LOGGER.error(ZKCryptoManagerConstants.SESSIONID, ZKCryptoManagerConstants.RE_ENCRYPT_RANDOM_KEY, + LOGGER.error(ZKCryptoManagerConstants.SESSIONID, ZKCryptoManagerConstants.RE_ENCRYPT_RANDOM_KEY, ZKCryptoManagerConstants.RE_ENCRYPT_RANDOM_KEY, "Thumbprint matching key not found in DB."); throw new ZKCryptoException(ZKCryptoErrorConstants.INVALID_ENCRYPTED_RANDOM_KEY.getErrorCode(), ZKCryptoErrorConstants.INVALID_ENCRYPTED_RANDOM_KEY.getErrorMessage()); } + Optional dbKeyStore = keyStoreRepository.findByAlias(kyAlias); + Optional keyAliasObj = keyAliasRepository.findById(Objects.requireNonNull(kyAlias)); + String certificateData = dbKeyStore.get().getCertificateData(); + X509Certificate x509Cert = (X509Certificate) keymanagerUtil.convertToCertificate(certificateData); + PrivateKey privateKey = (PrivateKey) cryptomanagerUtil.getEncryptedPrivateKey(keyAliasObj.get().getApplicationId(), Optional.ofNullable(keyAliasObj.get().getReferenceId()))[0]; SymmetricKeyRequestDto symmetricKeyRequestDto = new SymmetricKeyRequestDto(pubKeyApplicationId, localDateTimeStamp, pubKeyReferenceId, encRandomKey, true); From c67e63c47273013fe3e9188b709d21876805b8b5 Mon Sep 17 00:00:00 2001 From: nagendra0721 Date: Fri, 20 Jun 2025 19:13:27 +0530 Subject: [PATCH 3/5] MOSIP-36428: ECC Algorithm Support for encryption and decryption Signed-off-by: nagendra0721 --- .../test/service/EcCryptoOperationTest.java | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java deleted file mode 100644 index c4adc1c81..000000000 --- a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/EcCryptoOperationTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.mosip.kernel.cryptomanager.test.service; - -import io.mosip.kernel.clientcrypto.test.ClientCryptoTestBootApplication; -import io.mosip.kernel.cryptomanager.service.impl.EcCryptoOperationImpl; -import io.mosip.kernel.keymanagerservice.test.KeymanagerTestBootApplication; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@SpringBootTest(classes = { KeymanagerTestBootApplication.class }) -@RunWith(SpringRunner.class) -public class EcCryptoOperationTest { - - @Autowired - private EcCryptoOperationImpl service; - - private final String privateKey = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCC6m5SEHQms8YoFUfABl3P918oNQwIJeGDukOeNV6e8Hw=="; - public final String publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvMnarFay31WXcB/xsxBEOccl6+qJ88ctrzF9Rj9VqrLXk0Camh5x04cuA2cI9V8UWx9EhvPV7Wg4oS2aGs29Kg=="; - - @Test - public void testAsymmetricEcEncrypt() { - - } -} From 869d8f0371a9377bf26d402754cb596ceca44f86 Mon Sep 17 00:00:00 2001 From: nagendra0721 Date: Mon, 23 Jun 2025 12:37:22 +0530 Subject: [PATCH 4/5] MOSIP-36428: failed test case modified Signed-off-by: nagendra0721 --- .../test/integration/CryptographicServiceIntegrationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/integration/CryptographicServiceIntegrationTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/integration/CryptographicServiceIntegrationTest.java index e1e4dcd60..c5db6b36f 100644 --- a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/integration/CryptographicServiceIntegrationTest.java +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/integration/CryptographicServiceIntegrationTest.java @@ -125,6 +125,7 @@ public void setUp() throws NoSuchMethodException, InvocationTargetException, Ill requestWithPinWrapper.setId(ID); requestWithPinWrapper.setVersion(VERSION); requestWithPinWrapper.setRequesttime(LocalDateTime.now(ZoneId.of("UTC"))); + when(cryptomanagerUtil.getAlgorithmNameFromHeader(Mockito.any())).thenReturn("RSA"); } @WithUserDetails("reg-processor") @@ -191,7 +192,7 @@ public void testDecrypt() throws Exception { requestDto.setTimeStamp(timeStamp); SymmetricKeyRequestDto symmetricKeyRequestDto = new SymmetricKeyRequestDto(appid, timeStamp, refid, data, true); when(keyManagerService.decryptSymmetricKey(Mockito.any())).thenReturn(symmetricKeyResponseDto); - when(cryptomanagerUtil.parseEncryptKeyHeader(Mockito.any())).thenReturn("".getBytes()); + when(cryptomanagerUtil.parseEncryptKeyHeader(Mockito.any())).thenReturn("RSA".getBytes()); when(cryptomanagerUtil.decodeBase64Data(data)) .thenReturn("MOCKENCRYPTEDKEY#KEY_SPLITTER#MOCKENCRYPTEDDATA".getBytes()); when(cryptomanagerUtil.hasKeyAccess(Mockito.anyString())).thenReturn(true); From 65fd5028e7c32d2218f097a5199dc0e37e875bf8 Mon Sep 17 00:00:00 2001 From: nagendra0721 Date: Wed, 25 Jun 2025 16:28:00 +0530 Subject: [PATCH 5/5] MOSIP-36428: ec support related changes in key migrator Signed-off-by: nagendra0721 --- .../impl/CryptomanagerServiceImpl.java | 5 ++- .../util/CryptomanagerUtils.java | 38 +++++++++------- .../util/KeymanagerUtil.java | 2 +- .../service/impl/KeyMigratorServiceImpl.java | 44 ++++++++++++++----- .../impl/ZKCryptoManagerServiceImpl.java | 5 ++- .../kernel/migrate/impl/BaseKeysMigrator.java | 11 ++++- 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java index ae689a902..0393386b1 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/CryptomanagerServiceImpl.java @@ -204,6 +204,7 @@ public CryptomanagerResponseDto encrypt(CryptomanagerRequestDto cryptoRequestDto Certificate certificate = cryptomanagerUtil.getCertificate(cryptoRequestDto); PublicKey publicKey = certificate.getPublicKey(); + byte[] certThumbprint = cryptomanagerUtil.getCertificateThumbprint(certificate); CryptomanagerResponseDto cryptoResponseDto = new CryptomanagerResponseDto(); @@ -245,7 +246,6 @@ public CryptomanagerResponseDto encrypt(CryptomanagerRequestDto cryptoRequestDto return cryptoResponseDto; } */ //--------------------- - byte[] certThumbprint = cryptomanagerUtil.getCertificateThumbprint(certificate); byte[] concatedData = cryptomanagerUtil.concatCertThumbprint(certThumbprint, encryptedSymmetricKey); byte[] finalEncKeyBytes = cryptomanagerUtil.concatByteArrays(headerBytes, concatedData); cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(CryptoUtil.combineByteArray(encryptedData, @@ -266,7 +266,8 @@ public CryptomanagerResponseDto encrypt(CryptomanagerRequestDto cryptoRequestDto byte[] headerBytes = cryptomanagerUtil.getHeaderByte(ecCurveName); - byte[] finalEncKeyBytes = CryptoUtil.combineByteArray(encryptedDataWithIv, headerBytes, keySplitter); + byte[] concatedData = cryptomanagerUtil.concatCertThumbprint(certThumbprint, encryptedDataWithIv); + byte[] finalEncKeyBytes = CryptoUtil.combineByteArray(concatedData, headerBytes, keySplitter); cryptoResponseDto.setData(CryptoUtil.encodeToURLSafeBase64(finalEncKeyBytes)); } return cryptoResponseDto; diff --git a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java index 90bd79f00..028546157 100644 --- a/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java +++ b/kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java @@ -32,6 +32,7 @@ import io.mosip.kernel.keymanagerservice.entity.KeyAlias; import io.mosip.kernel.keymanagerservice.exception.NoUniqueAliasException; import io.mosip.kernel.signature.constant.SignatureConstant; +import io.mosip.kernel.keymanagerservice.helper.PrivateKeyDecryptorHelper; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; @@ -126,6 +127,9 @@ public class CryptomanagerUtils { @Autowired private ECKeyStore keyStore; + @Autowired + private PrivateKeyDecryptorHelper privateKeyDecryptorHelper; + /** Flag to generate and store Ed25519 key in real HSM. */ @Value("${mosip.kernel.keymanager.ed25519.hsm.support.enabled:false}") private boolean ed25519SupportFlag; @@ -254,12 +258,12 @@ public String getCertificateThumbprintInHex(Certificate cert) { return Hex.toHexString(getCertificateThumbprint(cert)).toUpperCase(); } - public byte[] concatCertThumbprint(byte[] certThumbprint, byte[] encryptedKey){ - byte[] finalData = new byte[CryptomanagerConstant.THUMBPRINT_LENGTH + encryptedKey.length]; - System.arraycopy(certThumbprint, 0, finalData, 0, certThumbprint.length); - System.arraycopy(encryptedKey, 0, finalData, certThumbprint.length, encryptedKey.length); - return finalData; - } + public byte[] concatCertThumbprint(byte[] certThumbprint, byte[] encryptedKey){ + byte[] finalData = new byte[CryptomanagerConstant.THUMBPRINT_LENGTH + encryptedKey.length]; + System.arraycopy(certThumbprint, 0, finalData, 0, certThumbprint.length); + System.arraycopy(encryptedKey, 0, finalData, certThumbprint.length, encryptedKey.length); + return finalData; + } public byte[] generateRandomBytes(int size) { byte[] randomBytes = new byte[size]; @@ -455,7 +459,7 @@ public boolean isJWSData(String data) { return true; } - public Object[] getEncryptedPrivateKey(String appId, Optional refId) { + public Object[] getEncryptedPrivateKey(String appId, Optional refId, String certThumbprint) { LocalDateTime localDateTime = DateUtils2.getUTCCurrentDateTime(); Map> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime); @@ -486,15 +490,17 @@ public Object[] getEncryptedPrivateKey(String appId, Optional refId) { LOGGER.info(KeymanagerConstant.SESSIONID, KeymanagerConstant.EMPTY, KeymanagerConstant.EMPTY, "Reference Id is present. Will get Certificate from DB store"); - Optional dbKeyStore = dbHelper.getKeyStoreFromDB(ksAlias); - if (!dbKeyStore.isPresent()) { + String referenceId = refId.get(); + io.mosip.kernel.keymanagerservice.entity.KeyStore dbKeyStore = privateKeyDecryptorHelper.getDBKeyStoreData(certThumbprint, + appId, referenceId); + if (dbKeyStore.getAlias().isEmpty()) { LOGGER.error(KeymanagerConstant.SESSIONID, KeymanagerConstant.KEYFROMDB, dbKeyStore.toString(), "Key in DBStore does not exist for this alias. Throwing exception"); throw new NoUniqueAliasException(KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorCode(), KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorMessage()); } - String masterKeyAlias = dbKeyStore.get().getMasterAlias(); - String privateKeyObj = dbKeyStore.get().getPrivateKey(); + String masterKeyAlias = dbKeyStore.getMasterAlias(); + String privateKeyObj = dbKeyStore.getPrivateKey(); if (ksAlias.equals(masterKeyAlias) || privateKeyObj.equals(KeymanagerConstant.KS_PK_NA)) { LOGGER.error(KeymanagerConstant.SESSIONID, KeymanagerConstant.APPLICATIONID, null, @@ -503,7 +509,7 @@ public Object[] getEncryptedPrivateKey(String appId, Optional refId) { KeymanagerErrorConstant.DECRYPTION_NOT_ALLOWED.getErrorMessage()); } - KeyStore.PrivateKeyEntry masterKeyEntry = keyStore.getAsymmetricKey(dbKeyStore.get().getMasterAlias()); + KeyStore.PrivateKeyEntry masterKeyEntry = keyStore.getAsymmetricKey(dbKeyStore.getMasterAlias()); PrivateKey masterPrivateKey = masterKeyEntry.getPrivateKey(); PublicKey masterPublicKey = masterKeyEntry.getCertificate().getPublicKey(); /** @@ -522,11 +528,11 @@ public Object[] getEncryptedPrivateKey(String appId, Optional refId) { } } - public Object[] getObjects(Optional dbKeyStore, PrivateKey masterPrivateKey, PublicKey masterPublicKey) { - byte[] decryptedPrivateKey = keymanagerUtil.decryptKey(CryptoUtil.decodeURLSafeBase64(dbKeyStore.get().getPrivateKey()), + public Object[] getObjects(io.mosip.kernel.keymanagerservice.entity.KeyStore dbKeyStore, PrivateKey masterPrivateKey, PublicKey masterPublicKey) { + byte[] decryptedPrivateKey = keymanagerUtil.decryptKey(CryptoUtil.decodeURLSafeBase64(dbKeyStore.getPrivateKey()), masterPrivateKey, masterPublicKey); - PublicKey publicKey = keymanagerUtil.convertToCertificate(dbKeyStore.get().getCertificateData()).getPublicKey(); + PublicKey publicKey = keymanagerUtil.convertToCertificate(dbKeyStore.getCertificateData()).getPublicKey(); String algorithmName = publicKey.getAlgorithm(); KeyFactory keyFactory = null; PrivateKey privateKey = null; @@ -536,7 +542,7 @@ public Object[] getObjects(Optional keyAlias = keyAliases.stream().filter(alias -> alias.getCertThumbprint().equals(certThumbprintHex)) .findFirst(); kyAlias = keyAlias.map(KeyAlias::getAlias).orElse(null); + certificateThumbprint = certThumbprintHex; if (!keyAlias.isPresent()) { continue; } @@ -520,7 +523,7 @@ public ReEncryptRandomKeyResponseDto zkReEncryptRandomKey(String encryptedKey){ String certificateData = dbKeyStore.get().getCertificateData(); X509Certificate x509Cert = (X509Certificate) keymanagerUtil.convertToCertificate(certificateData); - PrivateKey privateKey = (PrivateKey) cryptomanagerUtil.getEncryptedPrivateKey(keyAliasObj.get().getApplicationId(), Optional.ofNullable(keyAliasObj.get().getReferenceId()))[0]; + PrivateKey privateKey = (PrivateKey) cryptomanagerUtil.getEncryptedPrivateKey(keyAliasObj.get().getApplicationId(), Optional.ofNullable(keyAliasObj.get().getReferenceId()), certificateThumbprint)[0]; SymmetricKeyRequestDto symmetricKeyRequestDto = new SymmetricKeyRequestDto(pubKeyApplicationId, localDateTimeStamp, pubKeyReferenceId, encRandomKey, true); String randomKey = x509Cert.getPublicKey().getAlgorithm().equalsIgnoreCase(KeymanagerConstant.RSA) ? keyManagerService.decryptSymmetricKey(symmetricKeyRequestDto).getSymmetricKey() : diff --git a/kernel/keys-migrator/src/main/java/io/mosip/kernel/migrate/impl/BaseKeysMigrator.java b/kernel/keys-migrator/src/main/java/io/mosip/kernel/migrate/impl/BaseKeysMigrator.java index 85cb26cfb..7688c8502 100755 --- a/kernel/keys-migrator/src/main/java/io/mosip/kernel/migrate/impl/BaseKeysMigrator.java +++ b/kernel/keys-migrator/src/main/java/io/mosip/kernel/migrate/impl/BaseKeysMigrator.java @@ -21,6 +21,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import io.mosip.kernel.cryptomanager.service.EcCryptoOperation; import jakarta.annotation.PostConstruct; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -163,6 +164,9 @@ public class BaseKeysMigrator { @Autowired CryptomanagerUtils cryptomanagerUtil; + @Autowired + EcCryptoOperation ecCrypto; + @Autowired private CryptoCoreSpec cryptoCore; @@ -249,7 +253,9 @@ private void reEncryptAndUpload(PrivateKeyEntry masterKeyEntry, String masterKey try { byte[] decryptedPrivateKey = keymanagerUtil.decryptKey(CryptoUtil.decodeBase64(baseKey.getPrivateKey()), masterKey, masterPublicKey); - KeyFactory keyFactory = KeyFactory.getInstance(KeymanagerConstant.RSA); + PublicKey baseKeyPublicKey = keymanagerUtil.convertToCertificate(baseKey.getCertificateData()).getPublicKey(); + + KeyFactory keyFactory = KeyFactory.getInstance(baseKeyPublicKey.getAlgorithm()); PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKey)); String encryptedPrivateKey = CryptoUtil.encodeBase64(keymanagerUtil.encryptKey(privateKey, newKeyMgrPubKey)); Optional keyAliasObj = keyAliasRepository.findById(baseKeyUuid); @@ -353,7 +359,8 @@ private void migrateZKRandomKeys() { int keyIndex = zkKey.getId(); String encryptedKey = zkKey.getKey(); byte[] decryptedZKKey = decryptRandomKey(encryptedKey, zkMasterKey); - byte[] encryptedRandomKey = cryptoCore.asymmetricEncrypt(zkPublicKey, decryptedZKKey); + byte[] encryptedRandomKey = zkPublicKey.getAlgorithm().equalsIgnoreCase(KeymanagerConstant.RSA) ? cryptoCore.asymmetricEncrypt(zkPublicKey, decryptedZKKey) : + ecCrypto.asymmetricEcEncrypt(zkPublicKey, decryptedZKKey, keymanagerUtil.getEcCurveName(zkPublicKey)); String encodedKey = CryptoUtil.encodeBase64(encryptedRandomKey); ZKKeyDataDto keyDataDto = new ZKKeyDataDto(); keyDataDto.setKeyIndex(keyIndex);