Skip to content
Merged
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
23 changes: 22 additions & 1 deletion base/kra/shared/conf/CS.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,14 @@ kra.storageUnit.wrapping.0.payloadEncryptionIV=AQEBAQEBAQE=
kra.storageUnit.wrapping.0.payloadWrapAlgorithm=DES3/CBC/Pad

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Question, are we going to want an upgrade script for existing instances where these changes are being installed upon?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the initial release will not support any upgrade, especially from non-pqc. We can certainly create a ticket for that later.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sounds good.

kra.storageUnit.wrapping.0.payloadWrapIV=AQEBAQEBAQE=
kra.storageUnit.wrapping.0.sessionKeyType=DESede
kra.storageUnit.wrapping.1.sessionKeyLength=128
kra.storageUnit.wrapping._000=##
kra.storageUnit.wrapping._001=## wrapping.1 settings apply to RSA, EC, and ML-KEM storage certs
kra.storageUnit.wrapping._002=## For ML-KEM: sessionKeyWrapAlgorithm and sessionKeyKeyGenAlgorithm
kra.storageUnit.wrapping._003=## are not used (ML-KEM uses encapsulation, not wrapping/generation)
kra.storageUnit.wrapping._004=## payloadEncryption* fields only used when kra.allowEncDecrypt.archival=true
kra.storageUnit.wrapping._005=## (ML-KEM does not support allowEncDecrypt.archival)
kra.storageUnit.wrapping._006=##
kra.storageUnit.wrapping.1.sessionKeyLength=256
kra.storageUnit.wrapping.1.sessionKeyWrapAlgorithm=RSA
kra.storageUnit.wrapping.1.payloadEncryptionPadding=PKCS5Padding
kra.storageUnit.wrapping.1.sessionKeyKeyGenAlgorithm=AES
Expand All @@ -155,6 +162,20 @@ kra.storageUnit.wrapping.1.sessionKeyType=AES
kra.storageUnit.wrapping.choice=1
kra.storageUnit.nickName=
kra.transportUnit.nickName=
kra._000=##
kra._001=## Key Wrapping and PKCS#12 Security Settings
kra._002=##
kra._003=## For improved security, the following settings are recommended:
kra._004=##
kra._005=## 1. Use OAEP for RSA session key wrapping (applies to RSA storage certs only)
kra._006=## ML-KEM storage certs use encapsulation, not RSA wrapping
keyWrap.useOAEP=true
kra._007=##
kra._008=## 2. Use AES-256-KWP for PKCS#12 encryption (applies to all storage certs)
kra._009=## Legacy mode uses DES3, which is less secure
kra.legacyPKCS12=false
kra.nonLegacyAlg=AES/None/PKCS5Padding/Kwp/256
kra._010=##
log._000=##
log._001=## Logging
log._002=##
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ public boolean serviceRequest(Request request) throws EBaseException {
}

try {
record.setWrappingParams(params, allowEncDecrypt_archival);
String storageKeyAlg = storageUnit.getPublicKey().getAlgorithm();
record.setWrappingParams(params, allowEncDecrypt_archival, storageKeyAlg);
Comment thread
ladycfu marked this conversation as resolved.
} catch (Exception e) {
errmsg = "Unable to store wrapping parameters: " + e.getMessage();
logger.error("AsymKeyGenService: " + errmsg);
Expand Down
32 changes: 28 additions & 4 deletions base/kra/src/main/java/com/netscape/kra/EnrollmentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.util.Arrays;
Expand Down Expand Up @@ -72,6 +73,7 @@
import com.netscape.cmscore.request.Request;
import com.netscape.cmscore.security.JssSubsystem;
import com.netscape.cmscore.util.StatsSubsystem;
import com.netscape.cmsutil.crypto.CryptoUtil;

/**
* A class represents archival request processor. It
Expand Down Expand Up @@ -407,9 +409,12 @@ this is done below with unwrap()

//
// privateKeyData ::= SEQUENCE {
// sessionKey OCTET_STRING,
// encKey OCTET_STRING,
// }
// sessionKey OCTET_STRING, // RSA/EC storage: wrapped session key
// // ML-KEM storage: KEM ciphertext
// encKey OCTET_STRING, // User private key wrapped with session key/shared secret
// }
//
// Note: allowEncDecrypt_archival is not supported for ML-KEM storage at this time.
//
if (statsSub != null) {
statsSub.startTiming("encrypt_user_key");
Expand All @@ -421,6 +426,12 @@ this is done below with unwrap()
params = mStorageUnit.getWrappingParams(allowEncDecrypt_archival);

if (allowEncDecrypt_archival == true) {
String storageAlg = mStorageUnit.getPublicKey().getAlgorithm();
if (CryptoUtil.isAlgorithmMLKEM(storageAlg)) {
String msg = "allowEncDecrypt_archival not supported with ML-KEM storage certificate";
logger.error("EnrollmentService: " + msg);
throw new EKRAException(msg);
}
logger.info("EnrollmentService: Encrypting internal private key");
privateKeyData = mStorageUnit.encryptInternalPrivate(unwrapped, params);
} else {
Expand Down Expand Up @@ -508,6 +519,18 @@ this is done below with unwrap()
rec.set(KeyRecord.ATTR_META_INFO, metaInfo);
// key size does not apply to EC
rec.setKeySize(-1);
} else if (CryptoUtil.isAlgorithmMLKEM(keyAlg)) {
// ML-KEM keys: extract strength parameter
try {
int strength = CryptoUtil.getMLKEMStrength(keyAlg);
rec.setKeySize(Integer.valueOf(strength));

logger.debug("EnrollmentService: ML-KEM key archived - algorithm: " + keyAlg + ", strength: " + strength);

} catch (NoSuchAlgorithmException e) {
logger.warn("EnrollmentService: Unable to determine ML-KEM strength: " + e.getMessage(), e);
rec.setKeySize(-1);
}
}

// if record already has a serial number, yell out.
Expand Down Expand Up @@ -535,7 +558,8 @@ this is done below with unwrap()
}

try {
rec.setWrappingParams(params, allowEncDecrypt_archival);
String storageKeyAlg = mStorageUnit.getPublicKey().getAlgorithm();
rec.setWrappingParams(params, allowEncDecrypt_archival, storageKeyAlg);
} catch (Exception e) {
logger.error("EnrollmentService: Unable to store wrapping parameters: " + e.getMessage(), e);
// TODO(alee) Set correct audit message here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ public boolean serviceRequest(Request request)
throw new Exception("Unable to generate next serial number");
}

rec.setWrappingParams(params, allowEncDecrypt_archival);
String storageKeyAlg = mStorageUnit.getPublicKey().getAlgorithm();
rec.setWrappingParams(params, allowEncDecrypt_archival, storageKeyAlg);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

logger.debug("NetkeyKeygenService: before addKeyRecord");
rec.set(KeyRecord.ATTR_ID, serialNo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ public boolean archive(Request request)
}

try {
rec.setWrappingParams(params, doEncrypt);
String storageKeyAlg = storageUnit.getPublicKey().getAlgorithm();
rec.setWrappingParams(params, doEncrypt, storageKeyAlg);
Comment thread
ladycfu marked this conversation as resolved.
} catch (Exception e) {
logger.error("Unable to store wrapping parameters: " + e.getMessage(), e);

Expand Down
135 changes: 97 additions & 38 deletions base/kra/src/main/java/com/netscape/kra/StorageKeyUnit.java
Original file line number Diff line number Diff line change
Expand Up @@ -1115,33 +1115,62 @@ public byte[] encryptInternalPrivate(byte priKey[], WrappingParams params) throw
logger.debug("StorageKeyUnit.encryptInternalPrivate");
CryptoToken internalToken = getInternalToken();

// (1) generate session key
SymmetricKey sk = CryptoUtil.generateKey(
internalToken,
params.getSkKeyGenAlgorithm(),
params.getSkLength(),
null,
false);
PublicKey storagePubKey = getPublicKey();
String storageAlg = storagePubKey.getAlgorithm();

SymmetricKey sk;
byte[] session;

// Check if storage cert is ML-KEM (PQC)
if (CryptoUtil.isAlgorithmMLKEM(storageAlg)) {
logger.debug("StorageKeyUnit.encryptInternalPrivate: Using ML-KEM storage cert");

// ML-KEM requires the public key to be imported as a
// PKCS#11 object on the token for PK11_Encapsulate
internalToken.importPublicKey(storagePubKey, false);

// (1) ML-KEM encapsulation to generate shared secret
CryptoUtil.KEMEncapsulation kemResult = CryptoUtil.encapsulateMLKEM(
storagePubKey,
params.getSkLength());

sk = kemResult.sharedSecret;
session = kemResult.ciphertext;

logger.debug("StorageKeyUnit.encryptInternalPrivate: ML-KEM encapsulation complete");

} else {
// (1) RSA/EC: generate session key
logger.debug("StorageKeyUnit.encryptInternalPrivate: Using RSA/EC storage cert");

// (2) wrap private key with session key
sk = CryptoUtil.generateKey(
internalToken,
params.getSkKeyGenAlgorithm(),
params.getSkLength(),
null,
false);

// (3) wrap session with storage public
session = CryptoUtil.wrapUsingPublicKey(
internalToken,
storagePubKey,
sk,
params.getSkWrapAlgorithm());
}

// (2) wrap private key with session key (or shared secret for ML-KEM)
byte[] pri = CryptoUtil.encryptUsingSymmetricKey(
internalToken,
sk,
priKey,
params.getPayloadEncryptionAlgorithm(),
params.getPayloadEncryptionIV());

// (3) wrap session with storage public
byte[] session = CryptoUtil.wrapUsingPublicKey(
internalToken,
getPublicKey(),
sk,
params.getSkWrapAlgorithm());

// use MY own structure for now:
// SEQUENCE {
// encryptedSession OCTET STRING,
// encryptedPrivate OCTET STRING
// encryptedSession OCTET STRING, // RSA/EC: wrapped session key
// // ML-KEM: KEM ciphertext
// encryptedPrivate OCTET STRING // Private key wrapped with session key/shared secret
// }

DerOutputStream tmp = new DerOutputStream();
Expand Down Expand Up @@ -1176,21 +1205,57 @@ private byte[] _wrap(PrivateKey priKey, SymmetricKey symmKey, WrappingParams par
logger.debug("StorageKeyUnit.wrap interal.");
CryptoToken token = getToken();

SymmetricKey.Usage usages[] = new SymmetricKey.Usage[2];
usages[0] = SymmetricKey.Usage.WRAP;
usages[1] = SymmetricKey.Usage.UNWRAP;
PublicKey storagePubKey = getPublicKey();
String storageAlg = storagePubKey.getAlgorithm();

// (1) generate session key
SymmetricKey sk = CryptoUtil.generateKey(
token,
params.getSkKeyGenAlgorithm(),
params.getSkLength(),
usages,
true);
SymmetricKey sk;
byte[] session;

// (2) wrap private key with session key
// KeyWrapper wrapper = internalToken.getKeyWrapper(
// Check if storage cert is ML-KEM (PQC)
if (CryptoUtil.isAlgorithmMLKEM(storageAlg)) {
logger.debug("StorageKeyUnit:wrap() Using ML-KEM storage cert");

// ML-KEM requires the public key to be imported as a
// PKCS#11 object on the token for PK11_Encapsulate
token.importPublicKey(storagePubKey, false);

// (1) ML-KEM encapsulation to generate shared secret
CryptoUtil.KEMEncapsulation kemResult = CryptoUtil.encapsulateMLKEM(
storagePubKey,
params.getSkLength());
Comment thread
ladycfu marked this conversation as resolved.

sk = kemResult.sharedSecret;
session = kemResult.ciphertext;

logger.debug("StorageKeyUnit:wrap() ML-KEM encapsulation complete - ciphertext size: "
+ session.length + " bytes");

} else {
// (1) RSA/EC: generate session key
logger.debug("StorageKeyUnit:wrap() Using RSA/EC storage cert");

SymmetricKey.Usage usages[] = new SymmetricKey.Usage[2];
usages[0] = SymmetricKey.Usage.WRAP;
usages[1] = SymmetricKey.Usage.UNWRAP;

sk = CryptoUtil.generateKey(
token,
params.getSkKeyGenAlgorithm(),
params.getSkLength(),
usages,
true);

// (3) wrap session key with storage public key
session = CryptoUtil.wrapUsingPublicKey(
token,
storagePubKey,
sk,
params.getSkWrapAlgorithm());

logger.debug("StorageKeyUnit:wrap() session key wrapped");
}

// (2) wrap private key with session key (or shared secret for ML-KEM)
byte pri[] = null;

if (priKey != null) {
Expand All @@ -1211,17 +1276,11 @@ private byte[] _wrap(PrivateKey priKey, SymmetricKey symmKey, WrappingParams par

logger.debug("StorageKeyUnit:wrap() privKey wrapped");

byte[] session = CryptoUtil.wrapUsingPublicKey(
token,
getPublicKey(),
sk,
params.getSkWrapAlgorithm());
logger.debug("StorageKeyUnit:wrap() session key wrapped");

// use MY own structure for now:
// SEQUENCE {
// encryptedSession OCTET STRING,
// encryptedPrivate OCTET STRING
// encryptedSession OCTET STRING, // RSA/EC: wrapped session key
// // ML-KEM: KEM ciphertext
// encryptedPrivate OCTET STRING // User private key wrapped with session key/shared secret
// }

DerOutputStream tmp = new DerOutputStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ public boolean serviceRequest(Request request)
}

try {
rec.setWrappingParams(params, allowEncDecrypt_archival);
String storageKeyAlg = mStorageUnit.getPublicKey().getAlgorithm();
rec.setWrappingParams(params, allowEncDecrypt_archival, storageKeyAlg);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} catch (Exception e) {
String message = "Unable to store wrapping parameters: " + e.getMessage();
logger.error("SymKeyGenService: " + message, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
public final static String OUT_RECOVERED_ON = "recoveredOn";

/* parameters to populate WrappingParams */
public final static String OUT_STORAGE_KEY_ALGORITHM = "storageKeyAlgorithm";

Check warning on line 50 in base/server/src/main/java/com/netscape/cms/servlet/key/KeyRecordParser.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Reorder the modifiers to comply with the Java Language Specification.

See more on https://sonarcloud.io/project/issues?id=dogtagpki_pki&issues=AZ5xBSkRE1924JjNBNAc&open=AZ5xBSkRE1924JjNBNAc&pullRequest=5358
public final static String OUT_SK_TYPE = "sessionKeyType";
public final static String OUT_SK_KEYGEN_ALGORITHM = "sessionKeyKeyGenAlgorithm";
public final static String OUT_SK_LENGTH = "sessionKeyLength";
Expand Down
Loading
Loading