-
Notifications
You must be signed in to change notification settings - Fork 90
#523: Enable keymanager to support ECC algorithm during encryption and decryption #578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
331add5
d731dcb
c67e63c
869d8f0
65fd502
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 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); | ||
| } |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+99
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Binary payload framing with The encrypt path inserts a text delimiter into raw binary, and decrypt finds it by index scan. If ciphertext/IV bytes contain the same sequence, parsing breaks nondeterministically (wrong split, invalid key parse, decryption failures). Protocol-hardening direction- // output = encryptedDataWithIv + keySplitter + ephemeralPublicKey
+ // output = [4-byte encLen][encDataWithIv][4-byte ephKeyLen][ephPublicKey]
+ // Use explicit length-prefix framing to avoid delimiter collisions in binary payloads.Also applies to: 142-147 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| } 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()); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+125
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid NPE in Line 128 dereferences Proposed fix- if (ephemeralKeyPair != null) {
- destroyKey(ephemeralKeyPair.getPrivate().getEncoded());
- }
- if (ephemeralKeyPair.getPrivate() != null) destroyKey(ephemeralKeyPair.getPublic().getEncoded());
+ if (ephemeralKeyPair != null) {
+ if (ephemeralKeyPair.getPrivate() != null) {
+ destroyKey(ephemeralKeyPair.getPrivate().getEncoded());
+ }
+ if (ephemeralKeyPair.getPublic() != null) {
+ destroyKey(ephemeralKeyPair.getPublic().getEncoded());
+ }
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| 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); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: mosip/keymanager
Length of output: 6610
🌐 Web query:
Does Java JCA support X25519 key generation/agreement throughKeyPairGenerator.getInstance("EC")+ECGenParameterSpec("X25519"), or shouldX25519/XDHalgorithms be used explicitly?💡 Result:
In the Java Cryptography Architecture (JCA), X25519 key generation should be performed using the "XDH" algorithm, not the "EC" algorithm [1][2]. While some specialized providers (such as the Android Keystore) may support X25519 through the "EC" algorithm for specific platform integrations [3], standard Java SE implementations require the use of "XDH" to comply with the API introduced in JEP 324 [1]. To generate an X25519 key pair, you should use the following approach: 1. Obtain a KeyPairGenerator instance for "XDH" [1]. 2. Initialize it using a NamedParameterSpec set to "X25519" [1][4]. Example: KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH"); NamedParameterSpec paramSpec = new NamedParameterSpec("X25519"); kpg.initialize(paramSpec); KeyPair kp = kpg.generateKeyPair; Using KeyPairGenerator.getInstance("X25519") is also a supported shorthand in modern Java versions [1]. The "EC" algorithm and ECGenParameterSpec are intended for traditional Elliptic Curve cryptography (such as NIST curves) and should not be used for X25519 key agreement in standard JCA code [1][2][5].
Citations:
🏁 Script executed:
Repository: mosip/keymanager
Length of output: 8083
🏁 Script executed:
Repository: mosip/keymanager
Length of output: 174
🏁 Script executed:
# Get full file to understand context better wc -l kernel/kernel-keymanager-service/src/main/java/io/mosip/kernel/cryptomanager/service/impl/EcCryptoOperationImpl.javaRepository: mosip/keymanager
Length of output: 181
Remove unused
algorithmNameparameter and fix hard-coded EC/ECDH algorithm usage to support X25519.The
algorithmNameparameter is unused inasymmetricEcDecrypt, and more critically,KeyPairGenerator.getInstance(EC_ALGORITHM),KeyAgreement.getInstance(ECDH), andKeyFactory.getInstance(EC_ALGORITHM)are hard-coded throughout the class. X25519 support is broken because Java JCA requires "XDH" algorithm for X25519 key generation and agreement, not "EC". The codebase has X25519 routing infrastructure with VERSION_EC_X25519 headers, but this implementation cannot honor it due to the hard-coded algorithm constraints.This also applies to lines 134 (method signature), 154-160 (decryption's hard-coded algorithm instantiations), and the corresponding encryption methods that similarly ignore curve selection.
🧰 Tools
🪛 ast-grep (0.43.0)
[warning] 66-66: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: KeyPairGenerator.getInstance(EC_ALGORITHM)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures
(desede-is-deprecated-java)
[warning] 66-66: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: KeyPairGenerator.getInstance(EC_ALGORITHM)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures
(use-of-aes-ecb-java)
🤖 Prompt for AI Agents