Skip to content

Commit

Permalink
PKCS12Util: use AES to encrypt private keys
Browse files Browse the repository at this point in the history
Update PKCS12Util to use AES-256-CBC to encrypt private keys.
Use JSS CryptoStore methods to ensure that all key wrapping and
unwrapping is done on the token.

Specifically, CryptoStore.getEncryptedPrivateKeyInfo replaces the
previous process where a symmetric key was generated, the private
key wrapped to the symmetric key, then decryted into Dogtag's
memory, then re-encrypted under the supplied passphrase.  Now the
key gets wrapped directly to the supplied passphrase.

Similarly, for import, the EncryptedPrivateKeyInfo was decrypted
using the supplied passphrase, then encrypted to a freshly generated
symmetric key, which was then used to unwrap the key into the token.
Now, the new JSS method CryptoStore.importEncryptedPrivateKeyInfo is
used to unwrap the EncryptedPrivateKeyInfo directly into the token,
using the supplied passphrase.

As a result, the PKCS12KeyInfo class, which previously stored
unencrypted key material (a PrivateKeyInfo object), it now only
deals with PrivateKey (an opaque handle to an PKCS #11 object)
on export and encoded (byte[]) EncryptedPrivateKeyInfo data on
import.  This split suggests that PKCS12KeyInfo should be decomposed
into two classes - one containing a PrivateKey and the other
containing a byte[] encryptedPrivateKeyInfo - but this refactoring
is left for another day.

Part of: https://pagure.io/dogtagpki/issue/2610

Change-Id: I75d48de4d7040c9fb3a9a6d1e920c191aa757b70
(cherry picked from commit 2e198dd)
  • Loading branch information
frasertweedale authored and mharmsen99 committed Apr 30, 2017
1 parent f31ad87 commit 633c7c6
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 91 deletions.
Expand Up @@ -124,12 +124,12 @@ public void execute(String[] args) throws Exception {

if (nicknames.length == 0) {
// store all certificates
util.storeIntoNSS(pkcs12, overwrite);
util.storeIntoNSS(pkcs12, password, overwrite);

} else {
// load specified certificates
for (String nickname : nicknames) {
util.storeCertIntoNSS(pkcs12, nickname, overwrite);
util.storeCertIntoNSS(pkcs12, password, nickname, overwrite);
}
}

Expand Down
Expand Up @@ -38,6 +38,5 @@ public static void printKeyInfo(PKCS12KeyInfo keyInfo) throws Exception {

System.out.println(" Key ID: " + keyInfo.getID().toString(16));
System.out.println(" Subject DN: " + keyInfo.getSubjectDN());
System.out.println(" Algorithm: " + keyInfo.getPrivateKeyInfo().getAlgorithm());
}
}
29 changes: 19 additions & 10 deletions base/util/src/netscape/security/pkcs/PKCS12KeyInfo.java
Expand Up @@ -19,31 +19,40 @@

import java.math.BigInteger;

import org.mozilla.jss.pkix.primitive.PrivateKeyInfo;
import org.mozilla.jss.crypto.PrivateKey;

public class PKCS12KeyInfo {

private PrivateKey privateKey;
private byte[] epkiBytes;
BigInteger id;
PrivateKeyInfo privateKeyInfo;
String subjectDN;

public PKCS12KeyInfo() {
}

public BigInteger getID() {
return id;
public PKCS12KeyInfo(PrivateKey k) {
this.privateKey = k;
}

public void setID(BigInteger id) {
this.id = id;
public PKCS12KeyInfo(byte[] epkiBytes) {
this.epkiBytes = epkiBytes;
}

public PrivateKey getPrivateKey() {
return this.privateKey;
}

public PrivateKeyInfo getPrivateKeyInfo() {
return privateKeyInfo;
public byte[] getEncryptedPrivateKeyInfoBytes() {
return epkiBytes;
}

public void setPrivateKeyInfo(PrivateKeyInfo privateKeyInfo) {
this.privateKeyInfo = privateKeyInfo;
public BigInteger getID() {
return id;
}

public void setID(BigInteger id) {
this.id = id;
}

public String getSubjectDN() {
Expand Down
122 changes: 44 additions & 78 deletions base/util/src/netscape/security/pkcs/PKCS12Util.java
Expand Up @@ -33,42 +33,30 @@
import org.apache.commons.lang.StringUtils;
import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.asn1.ANY;
import org.mozilla.jss.asn1.ASN1Util;
import org.mozilla.jss.asn1.ASN1Value;
import org.mozilla.jss.asn1.BMPString;
import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
import org.mozilla.jss.asn1.OCTET_STRING;
import org.mozilla.jss.asn1.SEQUENCE;
import org.mozilla.jss.asn1.SET;
import org.mozilla.jss.crypto.Cipher;
import org.mozilla.jss.crypto.CryptoStore;
import org.mozilla.jss.crypto.CryptoToken;
import org.mozilla.jss.crypto.EncryptionAlgorithm;
import org.mozilla.jss.crypto.IVParameterSpec;
import org.mozilla.jss.crypto.InternalCertificate;
import org.mozilla.jss.crypto.KeyGenAlgorithm;
import org.mozilla.jss.crypto.KeyWrapAlgorithm;
import org.mozilla.jss.crypto.KeyWrapper;
import org.mozilla.jss.crypto.NoSuchItemOnTokenException;
import org.mozilla.jss.crypto.ObjectNotFoundException;
import org.mozilla.jss.crypto.PBEAlgorithm;
import org.mozilla.jss.crypto.PrivateKey;
import org.mozilla.jss.crypto.SymmetricKey;
import org.mozilla.jss.crypto.X509Certificate;
import org.mozilla.jss.pkcs12.AuthenticatedSafes;
import org.mozilla.jss.pkcs12.CertBag;
import org.mozilla.jss.pkcs12.PFX;
import org.mozilla.jss.pkcs12.PasswordConverter;
import org.mozilla.jss.pkcs12.SafeBag;
import org.mozilla.jss.pkix.primitive.Attribute;
import org.mozilla.jss.pkix.primitive.EncryptedPrivateKeyInfo;
import org.mozilla.jss.pkix.primitive.PrivateKeyInfo;
import org.mozilla.jss.util.Password;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netscape.cmsutil.crypto.CryptoUtil;

import netscape.ldap.LDAPDN;
import netscape.ldap.util.DN;
import netscape.security.x509.X509CertImpl;
Expand Down Expand Up @@ -114,41 +102,30 @@ public void setTrustFlags(X509Certificate cert, String trustFlags) throws Except
icert.setObjectSigningTrust(PKCS12.decodeFlags(flags[2]));
}

byte[] getEncodedKey(PrivateKey privateKey) throws Exception {
CryptoManager cm = CryptoManager.getInstance();
CryptoToken token = cm.getInternalKeyStorageToken();

byte[] iv = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
IVParameterSpec param = new IVParameterSpec(iv);

SymmetricKey sk = CryptoUtil.generateKey(token, KeyGenAlgorithm.DES3, 0, null, true);
byte[] enckey = CryptoUtil.wrapUsingSymmetricKey(
token,
sk,
privateKey,
param,
KeyWrapAlgorithm.DES3_CBC_PAD);

Cipher c = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD);
c.initDecrypt(sk, param);
return c.doFinal(enckey);
}

public void addKeyBag(PKCS12KeyInfo keyInfo, Password password,
SEQUENCE encSafeContents) throws Exception {
PrivateKey k = keyInfo.getPrivateKey();
if (k == null) {
logger.debug("NO PRIVATE KEY for " + keyInfo.subjectDN);
return;
}

logger.debug("Creating key bag for " + keyInfo.subjectDN);

PasswordConverter passConverter = new PasswordConverter();
byte salt[] = { 0x01, 0x01, 0x01, 0x01 };

EncryptedPrivateKeyInfo encPrivateKeyInfo = EncryptedPrivateKeyInfo.createPBE(
PBEAlgorithm.PBE_SHA1_DES3_CBC,
password, salt, 1, passConverter, keyInfo.privateKeyInfo);
byte[] epkiBytes = CryptoManager.getInstance()
.getInternalKeyStorageToken()
.getCryptoStore()
.getEncryptedPrivateKeyInfo(
/* NSS has a bug that causes any AES CBC encryption
* to use AES-256, but AlgorithmID contains chosen
* alg. To avoid mismatch, use AES_256_CBC. */
passConverter, password, EncryptionAlgorithm.AES_256_CBC, 0, k);

SET keyAttrs = createKeyBagAttrs(keyInfo);

SafeBag safeBag = new SafeBag(SafeBag.PKCS8_SHROUDED_KEY_BAG, encPrivateKeyInfo, keyAttrs);
SafeBag safeBag = new SafeBag(
SafeBag.PKCS8_SHROUDED_KEY_BAG, new ANY(epkiBytes), keyAttrs);
encSafeContents.addElement(safeBag);
}

Expand Down Expand Up @@ -318,14 +295,10 @@ public void loadKeyInfoFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger i
PrivateKey privateKey = cm.findPrivKeyByCert(cert);
logger.debug("Certificate \"" + nickname + "\" has private key");

PKCS12KeyInfo keyInfo = new PKCS12KeyInfo();
PKCS12KeyInfo keyInfo = new PKCS12KeyInfo(privateKey);
keyInfo.id = id;
keyInfo.subjectDN = cert.getSubjectDN().toString();

byte[] privateData = getEncodedKey(privateKey);
keyInfo.privateKeyInfo = (PrivateKeyInfo)
ASN1Util.decode(PrivateKeyInfo.getTemplate(), privateData);

pkcs12.addKeyInfo(keyInfo);

} catch (ObjectNotFoundException e) {
Expand Down Expand Up @@ -375,11 +348,7 @@ public void storeIntoFile(PKCS12 pkcs12, String filename, Password password) thr

public PKCS12KeyInfo getKeyInfo(SafeBag bag, Password password) throws Exception {

PKCS12KeyInfo keyInfo = new PKCS12KeyInfo();

// get private key info
EncryptedPrivateKeyInfo encPrivateKeyInfo = (EncryptedPrivateKeyInfo) bag.getInterpretedBagContent();
keyInfo.privateKeyInfo = encPrivateKeyInfo.decrypt(password, new PasswordConverter());
PKCS12KeyInfo keyInfo = new PKCS12KeyInfo(bag.getBagContent().getEncoded());

// get key attributes
SET bagAttrs = bag.getBagAttributes();
Expand Down Expand Up @@ -491,7 +460,7 @@ public PKCS12CertInfo getCertInfo(SafeBag bag) throws Exception {

public void getKeyInfos(PKCS12 pkcs12, PFX pfx, Password password) throws Exception {

logger.debug("Load private keys:");
logger.debug("Load encrypted private keys:");

AuthenticatedSafes safes = pfx.getAuthSafes();

Expand Down Expand Up @@ -590,20 +559,12 @@ public PKCS12CertInfo getCertBySubjectDN(PKCS12 pkcs12, String subjectDN)

public void importKey(
PKCS12 pkcs12,
Password password,
String nickname,
PKCS12KeyInfo keyInfo) throws Exception {

logger.debug("Importing private key " + keyInfo.subjectDN);

byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
IVParameterSpec param = new IVParameterSpec(iv);

PrivateKeyInfo privateKeyInfo = keyInfo.privateKeyInfo;

// encode private key
ByteArrayOutputStream bos = new ByteArrayOutputStream();
privateKeyInfo.encode(bos);
byte[] privateKey = bos.toByteArray();

PKCS12CertInfo certInfo = pkcs12.getCertInfoByID(keyInfo.getID());
if (certInfo == null) {
logger.debug("Private key has no certificate, ignore");
Expand All @@ -619,26 +580,29 @@ public void importKey(
// get public key
PublicKey publicKey = cert.getPublicKey();

// delete the cert again
byte[] epkiBytes = keyInfo.getEncryptedPrivateKeyInfoBytes();
if (epkiBytes == null) {
logger.debug(
"No EncryptedPrivateKeyInfo for key '"
+ keyInfo.subjectDN + "'; skipping key");
}
store.importEncryptedPrivateKeyInfo(
new PasswordConverter(), password, nickname, publicKey, epkiBytes);

// delete the cert again (it will be imported again later
// with the correct nickname)
try {
store.deleteCert(cert);
} catch (NoSuchItemOnTokenException e) {
// this is OK
}

// encrypt private key
SymmetricKey sk = CryptoUtil.generateKey(token, KeyGenAlgorithm.DES3, 0, null, true);
byte[] encpkey = CryptoUtil.encryptUsingSymmetricKey(
token, sk, privateKey, EncryptionAlgorithm.DES3_CBC_PAD, param);

// unwrap private key to load into database
KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
wrapper.initUnwrap(sk, param);
wrapper.unwrapPrivate(encpkey, getPrivateKeyType(publicKey), publicKey);
}

public void storeCertIntoNSS(PKCS12 pkcs12, PKCS12CertInfo certInfo, boolean overwrite) throws Exception {

public void storeCertIntoNSS(
PKCS12 pkcs12, Password password,
PKCS12CertInfo certInfo, boolean overwrite)
throws Exception
{
CryptoManager cm = CryptoManager.getInstance();
CryptoToken ct = cm.getInternalKeyStorageToken();
CryptoStore store = ct.getCryptoStore();
Expand All @@ -656,7 +620,7 @@ public void storeCertIntoNSS(PKCS12 pkcs12, PKCS12CertInfo certInfo, boolean ove
X509Certificate cert;
if (keyInfo != null) { // cert has key
logger.debug("Importing user key for " + certInfo.nickname);
importKey(pkcs12, keyInfo);
importKey(pkcs12, password, certInfo.nickname, keyInfo);

logger.debug("Importing user certificate " + certInfo.nickname);
cert = cm.importUserCACertPackage(certInfo.cert.getEncoded(), certInfo.nickname);
Expand All @@ -671,19 +635,21 @@ public void storeCertIntoNSS(PKCS12 pkcs12, PKCS12CertInfo certInfo, boolean ove
setTrustFlags(cert, certInfo.trustFlags);
}

public void storeCertIntoNSS(PKCS12 pkcs12, String nickname, boolean overwrite) throws Exception {
public void storeCertIntoNSS(PKCS12 pkcs12, Password password, String nickname, boolean overwrite) throws Exception {
Collection<PKCS12CertInfo> certInfos = pkcs12.getCertInfosByNickname(nickname);
for (PKCS12CertInfo certInfo : certInfos) {
storeCertIntoNSS(pkcs12, certInfo, overwrite);
storeCertIntoNSS(pkcs12, password, certInfo, overwrite);
}
}

public void storeIntoNSS(PKCS12 pkcs12, boolean overwrite) throws Exception {

public void storeIntoNSS(
PKCS12 pkcs12, Password password, boolean overwrite)
throws Exception
{
logger.info("Storing data into NSS database");

for (PKCS12CertInfo certInfo : pkcs12.getCertInfos()) {
storeCertIntoNSS(pkcs12, certInfo, overwrite);
storeCertIntoNSS(pkcs12, password, certInfo, overwrite);
}
}
}

0 comments on commit 633c7c6

Please sign in to comment.