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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ 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";
}

Original file line number Diff line number Diff line change
Expand Up @@ -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");


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.mosip.kernel.cryptomanager.service;

import java.security.PrivateKey;
import java.security.PublicKey;

public interface EcCryptomanagerService {

/**
*
* 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);
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
package io.mosip.kernel.cryptomanager.util;

import java.io.IOException;
import java.security.PublicKey;
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.*;
Expand All @@ -24,6 +25,13 @@
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.DateUtils;
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.keymanagerservice.helper.PrivateKeyDecryptorHelper;
import io.mosip.kernel.signature.constant.SignatureConstant;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.util.encoders.Hex;
Expand Down Expand Up @@ -93,6 +101,17 @@ 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;

/** Flag to generate and store Ed25519 key in real HSM. */
@Value("${mosip.kernel.keymanager.ed25519.hsm.support.enabled:false}")
private boolean ed25519SupportFlag;

Comment on lines +104 to +114
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate configuration fields.

signApplicationid (Line 106) duplicates signApplicationId (Line 96). This creates confusion and maintenance burden. Use a single field.

♻️ Proposed fix
-    /** The sign applicationid. */
-    `@Value`("${mosip.sign.applicationid:KERNEL}")
-    private String signApplicationid;
-
-    `@Value`("${mosip.sign-certificate-refid:SIGN}")
-    private String certificateSignRefID;

Then update usages on lines 462-463 to use signApplicationId and signRefId instead.

🤖 Prompt for AI Agents
In
`@kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java`
around lines 104 - 114, Remove the duplicate field signApplicationid and
consolidate configuration to the existing signApplicationId and signRefId names:
keep the `@Value` annotations as needed but rename/remove certificateSignRefID to
signRefId (or vice versa) so only signApplicationId and signRefId exist; update
all usages that currently reference signApplicationid and certificateSignRefID
(e.g., in methods that build signing context or calls around
signApplicationId/signRefId) to use the consolidated signApplicationId and
signRefId fields, and remove the redundant field declaration to avoid confusion.

/** The key manager. */
@Autowired
private KeymanagerService keyManager;
Expand All @@ -103,6 +122,12 @@ public class CryptomanagerUtils {
@Autowired
private KeymanagerDBHelper dbHelper;

@Autowired
private ECKeyStore keyStore;

@Autowired
private PrivateKeyDecryptorHelper privateKeyDecryptorHelper;

/**
* Calls Key-Manager-Service to get public key of an application.
*
Expand All @@ -123,7 +148,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();
}

Expand Down Expand Up @@ -322,13 +347,16 @@ public Certificate getCertificate(String applicationId, String referenceId) {
public void validateEncKeySize(Certificate encCert) {

if (validateKeySize) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) encCert.getPublicKey();
if (rsaPublicKey.getModulus().bitLength() != 2048) {
LOGGER.error(CryptomanagerConstant.SESSIONID, this.getClass().getSimpleName(), CryptomanagerConstant.JWT_ENCRYPT,
"Not Allowed to preform encryption with Key size not equal to 2048 bit.");
throw new CryptoManagerSerivceException(CryptomanagerErrorCode.ENCRYPT_NOT_ALLOWED_ERROR.getErrorCode(),
CryptomanagerErrorCode.ENCRYPT_NOT_ALLOWED_ERROR.getErrorMessage());
}
String algorithmName = encCert.getPublicKey().getAlgorithm();
if (algorithmName.equalsIgnoreCase(KeymanagerConstant.RSA)) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) encCert.getPublicKey();
if (rsaPublicKey.getModulus().bitLength() != 2048) {
LOGGER.error(CryptomanagerConstant.SESSIONID, this.getClass().getSimpleName(), CryptomanagerConstant.JWT_ENCRYPT,
"Not Allowed to preform encryption with Key size not equal to 2048 bit.");
throw new CryptoManagerSerivceException(CryptomanagerErrorCode.ENCRYPT_NOT_ALLOWED_ERROR.getErrorCode(),
CryptomanagerErrorCode.ENCRYPT_NOT_ALLOWED_ERROR.getErrorMessage());
}
}
}
}

Expand Down Expand Up @@ -397,4 +425,126 @@ public boolean isJWSData(String data) {
}
return true;
}

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 Object[] getEncryptedPrivateKey(String appId, Optional<String> refId, String certThumbprint) {

LocalDateTime localDateTime = DateUtils.getUTCCurrentDateTime();
Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime);
KeyAlias keyAlias = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS).getFirst();
Comment on lines +447 to +451
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potential NullPointerException.

Line 451 calls keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS).getFirst() without null-checking. If CURRENTKEYALIAS list is empty, getFirst() will throw NoSuchElementException.

🐛 Proposed fix
     public Object[] getEncryptedPrivateKey(String appId, Optional<String> refId, String certThumbprint) {

         LocalDateTime localDateTime = DateUtils.getUTCCurrentDateTime();
         Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime);
-        KeyAlias keyAlias = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS).getFirst();
+        List<KeyAlias> currentKeyAliasList = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS);
+        if (currentKeyAliasList == null || currentKeyAliasList.isEmpty()) {
+            throw new NoUniqueAliasException(KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorCode(),
+                    KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorMessage());
+        }
+        KeyAlias keyAlias = currentKeyAliasList.getFirst();
         String ksAlias = keyAlias.getAlias();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Object[] getEncryptedPrivateKey(String appId, Optional<String> refId, String certThumbprint) {
LocalDateTime localDateTime = DateUtils.getUTCCurrentDateTime();
Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime);
KeyAlias keyAlias = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS).getFirst();
public Object[] getEncryptedPrivateKey(String appId, Optional<String> refId, String certThumbprint) {
LocalDateTime localDateTime = DateUtils.getUTCCurrentDateTime();
Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime);
List<KeyAlias> currentKeyAliasList = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS);
if (currentKeyAliasList == null || currentKeyAliasList.isEmpty()) {
throw new NoUniqueAliasException(KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorCode(),
KeymanagerErrorConstant.NO_UNIQUE_ALIAS.getErrorMessage());
}
KeyAlias keyAlias = currentKeyAliasList.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};

Comment on lines +454 to +461
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potential NPE on Optional access.

Line 454 checks !refId.isPresent() but line 450 already calls refId.get() unconditionally. If refId is empty, line 450 throws NoSuchElementException before this check.

🐛 Proposed fix
     public Object[] getEncryptedPrivateKey(String appId, Optional<String> refId, String certThumbprint) {
+        if (!refId.isPresent() || refId.get().trim().isEmpty()) {
+            LOGGER.info(KeymanagerConstant.SESSIONID, KeymanagerConstant.EMPTY, KeymanagerConstant.EMPTY,
+                    "Not valid reference Id. Getting private key from HSM.");
+            // Handle HSM retrieval for empty refId case
+            LocalDateTime localDateTime = DateUtils.getUTCCurrentDateTime();
+            Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, KeymanagerConstant.EMPTY, localDateTime);
+            // ... rest of HSM logic
+        }

         LocalDateTime localDateTime = DateUtils.getUTCCurrentDateTime();
-        Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, refId.get(), localDateTime);
+        Map<String, List<KeyAlias>> keyAliasMap = dbHelper.getKeyAliases(appId, refId.orElse(KeymanagerConstant.EMPTY), localDateTime);
🤖 Prompt for AI Agents
In
`@kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/util/CryptomanagerUtils.java`
around lines 454 - 461, The code calls refId.get() before checking presence
which can throw NoSuchElementException; modify CryptomanagerUtils to avoid
unguarded get(): either first check refId.isPresent() and only call
refId.get().trim() inside that branch, or replace uses with
refId.map(String::trim).orElse("") (or refId.orElse("").trim()) and then test if
the resulting string is empty; update the block that currently reads
refId.get().trim() and the subsequent if (!refId.isPresent() ||
refId.get().trim().isEmpty()) to use the safe value so the fallback to HSM
(keyStore.getAsymmetricKey(ksAlias) and the return new Object[]
{masterPrivateKey, masterCert}) only executes when the refId is actually
absent/blank.

} 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");

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.getMasterAlias();
String privateKeyObj = dbKeyStore.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.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(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.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);
}
Comment on lines +524 to +529
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid throwing generic RuntimeException.

Wrapping InvalidKeySpecException and NoSuchAlgorithmException in a generic RuntimeException loses error context and makes debugging difficult. Use a domain-specific exception.

🐛 Proposed fix
         try {
             keyFactory = KeyFactory.getInstance(algorithmName);
             privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKey));
         } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
-            throw new RuntimeException(e);
+            throw new KeymanagerServiceException(KeymanagerErrorConstant.CRYPTO_EXCEPTION.getErrorCode(),
+                    KeymanagerErrorConstant.CRYPTO_EXCEPTION.getErrorMessage() + e.getMessage(), e);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
keyFactory = KeyFactory.getInstance(algorithmName);
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKey));
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
try {
keyFactory = KeyFactory.getInstance(algorithmName);
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKey));
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
throw new KeymanagerServiceException(KeymanagerErrorConstant.CRYPTO_EXCEPTION.getErrorCode(),
KeymanagerErrorConstant.CRYPTO_EXCEPTION.getErrorMessage() + e.getMessage(), e);
}

Certificate certificate = keymanagerUtil.convertToCertificate(dbKeyStore.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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -84,7 +88,7 @@ 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,
Expand Down Expand Up @@ -119,4 +123,23 @@ private SecureRandom getSecureRandom() {
return secureRandom;
}

/**
* 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();
}
Comment on lines +126 to +134
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find the KeyGenerator file and examine its full context
fd "KeyGenerator.java" -t f --path "*keygenerator*"

Repository: mosip/keymanager

Length of output: 293


🏁 Script executed:

# Search for asymmetricKeyAlgorithm configuration in properties/yaml files
rg "asymmetric-algorithm-name|asymmetricKeyAlgorithm" -t properties -t yaml -t yml -A 2 -B 2

Repository: mosip/keymanager

Length of output: 94


🏁 Script executed:

# Find KeyGeneratorUtils to understand how it handles the algorithm parameter
fd "KeyGeneratorUtils.java" -t f

Repository: mosip/keymanager

Length of output: 176


🏁 Script executed:

# Find KeyGenerator file correctly
find . -name "KeyGenerator.java" -type f | grep keygenerator

Repository: mosip/keymanager

Length of output: 168


🏁 Script executed:

# Read KeyGeneratorUtils to understand the getECKeyPairGenerator method
cat -n kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/util/KeyGeneratorUtils.java

Repository: mosip/keymanager

Length of output: 7465


🏁 Script executed:

# Search for configuration files with algorithm properties using correct syntax
rg "asymmetric.*algorithm|ecKeyAlgorithm" -g "*.properties" -g "*.yml" -g "*.yaml" -A 2

Repository: mosip/keymanager

Length of output: 3721


🏁 Script executed:

# Read the KeyGenerator.java file to understand the full context
cat -n ./kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/KeyGenerator.java

Repository: mosip/keymanager

Length of output: 5369


🏁 Script executed:

# Search for usages of getECKeyPair method
rg "getECKeyPair" -B 3 -A 3

Repository: mosip/keymanager

Length of output: 3577


🏁 Script executed:

# Search for tests related to getECKeyPair to see if this code path is tested
find . -name "*.java" -type f -path "*/test/*" | xargs rg "getECKeyPair|KeyGenerator" | head -20

Repository: mosip/keymanager

Length of output: 3677


🏁 Script executed:

# Check if there are any tests for KeyGenerator class
find . -name "*KeyGeneratorTest*.java" -o -name "*KeyGenerator*Test.java"

Repository: mosip/keymanager

Length of output: 304


🏁 Script executed:

# Read the KeyGeneratorTest to see if getECKeyPair is tested
cat -n ./kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/keygenerator/bouncycastle/test/KeyGeneratorTest.java

Repository: mosip/keymanager

Length of output: 4652


🏁 Script executed:

# Verify that KeyPairGenerator.getInstance("RSA") with ECGenParameterSpec would fail
# by checking Java security specifications - let's also check if there's any wrapper logic
rg "getECKeyPairGenerator" -B 5 -A 10

Repository: mosip/keymanager

Length of output: 5163


🏁 Script executed:

# Check if there's any special handling in BouncyCastle for this scenario
rg "getInstance.*RSA|getInstance.*EC" kernel/kernel-keymanager-service/src/main/java -A 2

Repository: mosip/keymanager

Length of output: 7971


Fix EC key generation: use dedicated algorithm property instead of RSA-configured asymmetricKeyAlgorithm.

The method passes asymmetricKeyAlgorithm (configured as "RSA") to KeyGeneratorUtils.getECKeyPairGenerator(), which attempts to create an RSA KeyPairGenerator and initialize it with ECGenParameterSpec. This will throw InvalidAlgorithmParameterException at runtime since RSA generators cannot be initialized with EC parameters.

Add a dedicated ecKeyAlgorithm property (defaulting to "EC") and use it instead, following the pattern already used in PKCS12KeyStoreImpl and PKCS11KeyStoreImpl.

Proposed fix
+    /** ECC algorithm name */
+    `@Value`("${mosip.kernel.keygenerator.ec-algorithm-name:EC}")
+    private String ecKeyAlgorithm;
+
     /**
      * 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());
+        KeyPairGenerator generator = KeyGeneratorUtils.getECKeyPairGenerator(ecKeyAlgorithm, eccCurve, getSecureRandom());
         return generator.generateKeyPair();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* 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();
}
/** ECC algorithm name */
`@Value`("${mosip.kernel.keygenerator.ec-algorithm-name:EC}")
private String ecKeyAlgorithm;
/**
* This method generated Asymmetric key pairs for ECC
*
* `@return` {`@link` KeyPair} which contain public and private key
*/
public KeyPair getECKeyPair() {
KeyPairGenerator generator = KeyGeneratorUtils.getECKeyPairGenerator(ecKeyAlgorithm, eccCurve, getSecureRandom());
return generator.generateKeyPair();
}
🤖 Prompt for AI Agents
In
`@kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/keygenerator/bouncycastle/KeyGenerator.java`
around lines 126 - 134, The getECKeyPair method currently calls
KeyGeneratorUtils.getECKeyPairGenerator(asymmetricKeyAlgorithm, eccCurve,
getSecureRandom()) but asymmetricKeyAlgorithm is RSA-configured and causes
InvalidAlgorithmParameterException; add a new ecKeyAlgorithm field/property
defaulting to "EC" (mirroring PKCS12KeyStoreImpl/PKCS11KeyStoreImpl patterns)
and change getECKeyPair to pass ecKeyAlgorithm instead of asymmetricKeyAlgorithm
to KeyGeneratorUtils.getECKeyPairGenerator so an EC KeyPairGenerator is created
and initialized with ECGenParameterSpec.


/**
* This method generated Asymmetric key pairs for X25519
*
* @return {@link java.security.KeyPair} which contain public and private
*/
public KeyPair getX25519KeyPair() {
KeyPairGenerator generator = KeyGeneratorUtils.getX25519KeyPairGenerator(getSecureRandom());
return generator.generateKeyPair();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.*;

import javax.crypto.KeyGenerator;

Expand Down Expand Up @@ -137,4 +134,31 @@ private static BouncyCastleProvider init() {
Security.addProvider(provider);
return provider;
}

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 KeyPairGenerator getX25519KeyPairGenerator(SecureRandom secureRandom) {
KeyPairGenerator generator = null;
try {
generator = KeyPairGenerator.getInstance(KeymanagerConstant.X25519_KEY_TYPE, provider);
NamedParameterSpec namedParameterSpec = new NamedParameterSpec(KeymanagerConstant.X25519_KEY_TYPE);
generator.initialize(namedParameterSpec, 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@ 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";

}
Loading
Loading