Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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,10 @@ 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."),

JWE_ENCRYPTION_NOT_SUPPORTED("KER-CRY-017", "JWE encryption is not supported for the provided (%S) public key."),

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,13 @@ public class CryptomanagerUtils {
@Value("${mosip.kernel.keymanager.jwtEncrypt.validate.json:true}")
private boolean confValidateJson;

@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;

/** The key manager. */
@Autowired
private KeymanagerService keyManager;
Expand All @@ -103,6 +118,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 +144,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 +343,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 +421,128 @@ 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);
List<KeyAlias> curkeyAliasList = keyAliasMap.getOrDefault(KeymanagerConstant.CURRENTKEYALIAS, Collections.emptyList());
List<KeyAlias> keyAliasList = keyAliasMap.getOrDefault(KeymanagerConstant.KEYALIAS, Collections.emptyList());
String ksAlias = curkeyAliasList.isEmpty() ? keyAliasList.getFirst().getAlias() : curkeyAliasList.getFirst().getAlias();

if (!refId.isPresent() || refId.get().trim().isEmpty()) {
LOGGER.info(KeymanagerConstant.SESSIONID, KeymanagerConstant.EMPTY, KeymanagerConstant.EMPTY,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
"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");

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 CryptoManagerSerivceException(CryptomanagerErrorCode.UNSUPPORTED_EC_CURVE.getErrorCode(),
CryptomanagerErrorCode.UNSUPPORTED_EC_CURVE.getErrorMessage() + e.getMessage());
}
Certificate certificate = keymanagerUtil.convertToCertificate(dbKeyStore.getCertificateData());
return new Object[]{privateKey, certificate};
}

public byte[] getHeaderByte(String ecCurveName) {
byte[] headerBytes;
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 @@ -10,6 +10,7 @@

import javax.crypto.SecretKey;

import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -67,6 +68,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,10 +89,10 @@ 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(KeymanagerConstant.RSA, asymmetricKeyLength,
getSecureRandom());
return generator.generateKeyPair();
}
Expand Down Expand Up @@ -119,4 +124,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(KeymanagerConstant.EC_KEY_TYPE, eccCurve, getSecureRandom());
return generator.generateKeyPair();
}

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