0

In the bouncy castle examples, I found this code:

package crypto;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.Attribute;
import org.bouncycastle.asn1.pkcs.ContentInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.operator.bc.BcDefaultDigestProvider;
import org.bouncycastle.pkcs.PKCS12PfxPdu;
import org.bouncycastle.pkcs.PKCS12PfxPduBuilder;
import org.bouncycastle.pkcs.PKCS12SafeBag;
import org.bouncycastle.pkcs.PKCS12SafeBagBuilder;
import org.bouncycastle.pkcs.PKCS12SafeBagFactory;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.bc.BcPKCS12MacCalculatorBuilderProvider;
import org.bouncycastle.pkcs.jcajce.JcaPKCS12SafeBagBuilder;
import org.bouncycastle.pkcs.jcajce.JcePKCS12MacCalculatorBuilder;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEOutputEncryptorBuilder;
import org.bouncycastle.util.io.Streams;


public class PKCS12 {
    public static void main(String[] args)
            throws Exception
    {
        Security.addProvider(new BouncyCastleProvider());

        KeyStore credentials = JcaUtils.createCredentials();
        PrivateKey key = (PrivateKey)credentials.getKey(JcaUtils.END_ENTITY_ALIAS, JcaUtils.KEY_PASSWD);
        Certificate[] chain = credentials.getCertificateChain(JcaUtils.END_ENTITY_ALIAS);

        createPKCS12File(new FileOutputStream("id.p12"), key, chain);

        //
        // first do a "blow by blow" read of the PKCS#12 file.
        //
        PKCS12PfxPdu pfx = readPKCS12File(new FileInputStream("id.p12"));

        //
        // or alternately just load it up using a KeyStore
        //
        KeyStore pkcs12Store = KeyStore.getInstance("PKCS12", "BC");

        pkcs12Store.load(new FileInputStream("id.p12"), JcaUtils.KEY_PASSWD);

        System.out.println("########## KeyStore Dump");

        for (Enumeration en = pkcs12Store.aliases(); en.hasMoreElements();)
        {
            String alias = (String)en.nextElement();

            if (pkcs12Store.isCertificateEntry(alias))
            {
                System.out.println("Certificate Entry: " + alias + ", Subject: " + (((X509Certificate)pkcs12Store.getCertificate(alias)).getSubjectDN()));
            }
            else if (pkcs12Store.isKeyEntry(alias))
            {
                System.out.println("Key Entry: " + alias + ", Subject: " + (((X509Certificate)pkcs12Store.getCertificate(alias)).getSubjectDN()));
            }
        }

        System.out.println();
    }

    private static void createPKCS12File(OutputStream pfxOut, PrivateKey key, Certificate[] chain)
            throws Exception
    {
        OutputEncryptor encOut = new JcePKCSPBEOutputEncryptorBuilder(NISTObjectIdentifiers.id_aes256_CBC).setProvider("BC").build(JcaUtils.KEY_PASSWD);

        PKCS12SafeBagBuilder taCertBagBuilder = new JcaPKCS12SafeBagBuilder((X509Certificate)chain[2]);

        taCertBagBuilder.addBagAttribute(PKCS12SafeBag.friendlyNameAttribute, new DERBMPString("Bouncy Primary Certificate"));

        PKCS12SafeBagBuilder caCertBagBuilder = new JcaPKCS12SafeBagBuilder((X509Certificate)chain[1]);

        caCertBagBuilder.addBagAttribute(PKCS12SafeBag.friendlyNameAttribute, new DERBMPString("Bouncy Intermediate Certificate"));

        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
        PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder((X509Certificate)chain[0]);

        eeCertBagBuilder.addBagAttribute(PKCS12SafeBag.friendlyNameAttribute, new DERBMPString("Eric's Key"));
        SubjectKeyIdentifier pubKeyId = extUtils.createSubjectKeyIdentifier(chain[0].getPublicKey());
        eeCertBagBuilder.addBagAttribute(PKCS12SafeBag.localKeyIdAttribute, pubKeyId);

        PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(key, encOut);

        keyBagBuilder.addBagAttribute(PKCS12SafeBag.friendlyNameAttribute, new DERBMPString("Eric's Key"));
        keyBagBuilder.addBagAttribute(PKCS12SafeBag.localKeyIdAttribute, pubKeyId);

        PKCS12PfxPduBuilder builder = new PKCS12PfxPduBuilder();

        builder.addData(keyBagBuilder.build());

        builder.addEncryptedData(new JcePKCSPBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC).setProvider("BC").build(JcaUtils.KEY_PASSWD), new PKCS12SafeBag[]{eeCertBagBuilder.build(), caCertBagBuilder.build(), taCertBagBuilder.build()});

        PKCS12PfxPdu pfx = builder.build(new JcePKCS12MacCalculatorBuilder(NISTObjectIdentifiers.id_sha256), JcaUtils.KEY_PASSWD);

        // make sure we don't include indefinite length encoding
        pfxOut.write(pfx.getEncoded(ASN1Encoding.DL));

        pfxOut.close();
    }

    private static PKCS12PfxPdu readPKCS12File(InputStream pfxIn)
            throws Exception
    {
        PKCS12PfxPdu pfx = new PKCS12PfxPdu(Streams.readAll(pfxIn));

        if (!pfx.isMacValid(new BcPKCS12MacCalculatorBuilderProvider(BcDefaultDigestProvider.INSTANCE), JcaUtils.KEY_PASSWD))
        {
            System.err.println("PKCS#12 MAC test failed!");
        }

        ContentInfo[] infos = pfx.getContentInfos();

        Map certMap = new HashMap();
        Map certKeyIds = new HashMap();
        Map privKeyMap = new HashMap();
        Map privKeyIds = new HashMap();

        InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder()
                .setProvider("BC").build(JcaUtils.KEY_PASSWD);
        JcaX509CertificateConverter  jcaConverter = new JcaX509CertificateConverter().setProvider("BC");

        for (int i = 0; i != infos.length; i++)
        {
            if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData))
            {
                PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider);

                PKCS12SafeBag[] bags = dataFact.getSafeBags();

                for (int b = 0; b != bags.length; b++)
                {
                    PKCS12SafeBag bag = bags[b];

                    X509CertificateHolder certHldr = (X509CertificateHolder)bag.getBagValue();
                    X509Certificate       cert = jcaConverter.getCertificate(certHldr);

                    Attribute[] attributes = bag.getAttributes();
                    for (int a = 0; a != attributes.length; a++)
                    {
                        Attribute attr = attributes[a];

                        if (attr.getAttrType().equals(PKCS12SafeBag.friendlyNameAttribute))
                        {
                            certMap.put(((DERBMPString)attr.getAttributeValues()[0]).getString(), cert);
                        }
                        else if (attr.getAttrType().equals(PKCS12SafeBag.localKeyIdAttribute))
                        {
                            certKeyIds.put(attr.getAttributeValues()[0], cert);
                        }
                    }
                }
            }
            else
            {
                PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]);

                PKCS12SafeBag[] bags = dataFact.getSafeBags();

                PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo)bags[0].getBagValue();
                PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider);

                KeyFactory keyFact = KeyFactory .getInstance(info.getPrivateKeyAlgorithm().getAlgorithm().getId(), "BC");
                PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(info.getEncoded()));

                Attribute[] attributes = bags[0].getAttributes();
                for (int a = 0; a != attributes.length; a++)
                {
                    Attribute attr = attributes[a];

                    if (attr.getAttrType().equals(PKCS12SafeBag.friendlyNameAttribute))
                    {
                        privKeyMap.put(((DERBMPString)attr.getAttributeValues()[0]).getString(), privKey);
                    }
                    else if (attr.getAttrType().equals(PKCS12SafeBag.localKeyIdAttribute))
                    {
                        privKeyIds.put(privKey, attr.getAttributeValues()[0]);
                    }
                }
            }
        }

        System.out.println("########## PFX Dump");
        for (Iterator it = privKeyMap.keySet().iterator(); it.hasNext();)
        {
            String alias = (String)it.next();

            System.out.println("Key Entry: " + alias + ", Subject: " + (((X509Certificate)certKeyIds.get(privKeyIds.get(privKeyMap.get(alias)))).getSubjectDN()));
        }

        for (Iterator it = certMap.keySet().iterator(); it.hasNext();)
        {
            String alias = (String)it.next();

            System.out.println("Certificate Entry: " + alias + ", Subject: " + (((X509Certificate)certMap.get(alias)).getSubjectDN()));
        }
        System.out.println();

        return pfx;
    }
}

When I use this code to write a p12, I try to open it using openssl or keychain and I'm prompted to put in a password. I use their password

public static char[] KEY_PASSWD = "keyPassword".toCharArray();

to try to open the P12, but it does not work. Also, when I just run this main function as is, this line gets executed

System.err.println("PKCS#12 MAC test failed!");

Is there something wrong with their example in order to correctly put a password on the p12? I'm basically trying to do the equivalent of

openssl pkcs12 -export -des3

in Java using Bouncy Castle if anyone has other ideas as well.

Edit

Looking at the NISTObjectIdentifiers to choose from, I don't see p12pbmac/sha1. This is what I see:

public interface NISTObjectIdentifiers {
    ASN1ObjectIdentifier nistAlgorithm = new ASN1ObjectIdentifier("2.16.840.1.101.3.4");
    ASN1ObjectIdentifier hashAlgs = nistAlgorithm.branch("2");
    ASN1ObjectIdentifier id_sha256 = hashAlgs.branch("1");
    ASN1ObjectIdentifier id_sha384 = hashAlgs.branch("2");
    ASN1ObjectIdentifier id_sha512 = hashAlgs.branch("3");
    ASN1ObjectIdentifier id_sha224 = hashAlgs.branch("4");
    ASN1ObjectIdentifier id_sha512_224 = hashAlgs.branch("5");
    ASN1ObjectIdentifier id_sha512_256 = hashAlgs.branch("6");
    ASN1ObjectIdentifier id_sha3_224 = hashAlgs.branch("7");
    ASN1ObjectIdentifier id_sha3_256 = hashAlgs.branch("8");
    ASN1ObjectIdentifier id_sha3_384 = hashAlgs.branch("9");
    ASN1ObjectIdentifier id_sha3_512 = hashAlgs.branch("10");
    ASN1ObjectIdentifier id_shake128 = hashAlgs.branch("11");
    ASN1ObjectIdentifier id_shake256 = hashAlgs.branch("12");
    ASN1ObjectIdentifier id_hmacWithSHA3_224 = hashAlgs.branch("13");
    ASN1ObjectIdentifier id_hmacWithSHA3_256 = hashAlgs.branch("14");
    ASN1ObjectIdentifier id_hmacWithSHA3_384 = hashAlgs.branch("15");
    ASN1ObjectIdentifier id_hmacWithSHA3_512 = hashAlgs.branch("16");
    ASN1ObjectIdentifier aes = nistAlgorithm.branch("1");
    ASN1ObjectIdentifier id_aes128_ECB = aes.branch("1");
    ASN1ObjectIdentifier id_aes128_CBC = aes.branch("2");
    ASN1ObjectIdentifier id_aes128_OFB = aes.branch("3");
    ASN1ObjectIdentifier id_aes128_CFB = aes.branch("4");
    ASN1ObjectIdentifier id_aes128_wrap = aes.branch("5");
    ASN1ObjectIdentifier id_aes128_GCM = aes.branch("6");
    ASN1ObjectIdentifier id_aes128_CCM = aes.branch("7");
    ASN1ObjectIdentifier id_aes128_wrap_pad = aes.branch("8");
    ASN1ObjectIdentifier id_aes192_ECB = aes.branch("21");
    ASN1ObjectIdentifier id_aes192_CBC = aes.branch("22");
    ASN1ObjectIdentifier id_aes192_OFB = aes.branch("23");
    ASN1ObjectIdentifier id_aes192_CFB = aes.branch("24");
    ASN1ObjectIdentifier id_aes192_wrap = aes.branch("25");
    ASN1ObjectIdentifier id_aes192_GCM = aes.branch("26");
    ASN1ObjectIdentifier id_aes192_CCM = aes.branch("27");
    ASN1ObjectIdentifier id_aes192_wrap_pad = aes.branch("28");
    ASN1ObjectIdentifier id_aes256_ECB = aes.branch("41");
    ASN1ObjectIdentifier id_aes256_CBC = aes.branch("42");
    ASN1ObjectIdentifier id_aes256_OFB = aes.branch("43");
    ASN1ObjectIdentifier id_aes256_CFB = aes.branch("44");
    ASN1ObjectIdentifier id_aes256_wrap = aes.branch("45");
    ASN1ObjectIdentifier id_aes256_GCM = aes.branch("46");
    ASN1ObjectIdentifier id_aes256_CCM = aes.branch("47");
    ASN1ObjectIdentifier id_aes256_wrap_pad = aes.branch("48");
    ASN1ObjectIdentifier sigAlgs = nistAlgorithm.branch("3");
    ASN1ObjectIdentifier id_dsa_with_sha2 = sigAlgs;
    ASN1ObjectIdentifier dsa_with_sha224 = sigAlgs.branch("1");
    ASN1ObjectIdentifier dsa_with_sha256 = sigAlgs.branch("2");
    ASN1ObjectIdentifier dsa_with_sha384 = sigAlgs.branch("3");
    ASN1ObjectIdentifier dsa_with_sha512 = sigAlgs.branch("4");
    ASN1ObjectIdentifier id_dsa_with_sha3_224 = sigAlgs.branch("5");
    ASN1ObjectIdentifier id_dsa_with_sha3_256 = sigAlgs.branch("6");
    ASN1ObjectIdentifier id_dsa_with_sha3_384 = sigAlgs.branch("7");
    ASN1ObjectIdentifier id_dsa_with_sha3_512 = sigAlgs.branch("8");
    ASN1ObjectIdentifier id_ecdsa_with_sha3_224 = sigAlgs.branch("9");
    ASN1ObjectIdentifier id_ecdsa_with_sha3_256 = sigAlgs.branch("10");
    ASN1ObjectIdentifier id_ecdsa_with_sha3_384 = sigAlgs.branch("11");
    ASN1ObjectIdentifier id_ecdsa_with_sha3_512 = sigAlgs.branch("12");
    ASN1ObjectIdentifier id_rsassa_pkcs1_v1_5_with_sha3_224 = sigAlgs.branch("13");
    ASN1ObjectIdentifier id_rsassa_pkcs1_v1_5_with_sha3_256 = sigAlgs.branch("14");
    ASN1ObjectIdentifier id_rsassa_pkcs1_v1_5_with_sha3_384 = sigAlgs.branch("15");
    ASN1ObjectIdentifier id_rsassa_pkcs1_v1_5_with_sha3_512 = sigAlgs.branch("16");
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Crystal
  • 28,460
  • 62
  • 219
  • 393
  • Define 'does not work'. – user207421 Oct 10 '17 at 03:24
  • @EJP the password does not let me in – Crystal Oct 10 '17 at 03:26
  • I'm not sure why isValidMac fails and for me it varies with BC version (1.53 through 1.56 only) but if you truly want the equivalent of `openssl pkcs12 -export -des3` that should be keybag=pbeWithSHAAnd3_KeyTripleDES_CBC (not pbes2/sha1,AES256) certbag=pbeWithSHAAnd40BitRC2_CBC (not 128) and p12pbmac/sha1 (not SHA256), all with itercount 2048 not 1024, and with those algorithm changes (but not bothering with itercount) it works for me consistently in BC and openssl (I can't test keychain). – dave_thompson_085 Oct 10 '17 at 11:10
  • @dave_thompson_085 is there a different version of BC i should be using for Java 1.7? – Crystal Oct 10 '17 at 15:36
  • @dave_thompson_085 Can you explain which SHA256 i need to replace? I don't see any p12pbmac/sha1 option as part of NISTObjectIdentifiers. The pbeWithSHAAnd3_KeyTripleDES_CBC made sense. – Crystal Oct 10 '17 at 16:02
  • Define 'the password does not let me in'. You get an exception of some kind? Stack trace? Evidence? – user207421 Oct 10 '17 at 16:45
  • @EJP when I try to use openssl to view the p12, and when I put in the password, it says invalid password. No exception. – Crystal Oct 10 '17 at 16:48
  • (1) The current BC version is 1.58; all BC versions for years have run identically on Java versions (1.)5 to 8 which is why they name the files e.g. bcprov-jdk15on (I don't know yet if j9 changes anything here). Since 1.57 and 1.58 work for me on both j7 and j8, I recommend one of those, unless there is something else in your program incompatible with them. – dave_thompson_085 Oct 11 '17 at 00:44
  • (2) The sha256 I mean is the one in `JcePKCS12MacCalculatorBuilder`. This is used for the password-based MAC defined by PKCS12 appendix A and B, which has no OID but I abbreviate as p12pbmac meaning PKCS12 password-based MAC. You specify, and the `macData` field stores, the OID of the underlying hash only; the 'p12pbmac' is implicit because it's in a p12. – dave_thompson_085 Oct 11 '17 at 00:44
  • In fact with some further thought and a little testing I note you probably don't need this code at all; for all BC since (at least) 1.49 the BCprov `KeyStore` instance for `PKCS12` creates a P12 with nearly the same crypto as OpenSSL: pbe-tdes-3key pbe-rc2-40 a and p12pbmac/sha1 but itercounts 1024 not 2048; also it doesn't set friendlyname for non-leaf certs, but I've never seen that really needed. Even without BC, the standard SunJSSE provider since (at least) j6 also does that crypto. Both of these are readable with openssl. – dave_thompson_085 Oct 11 '17 at 01:46
  • @dave_thompson_085 do you have any sample code for that? I literally got thrown to certs and am just trying to make sense of any of it. – Crystal Oct 11 '17 at 03:32
  • See pseudo-answer. – dave_thompson_085 Oct 12 '17 at 10:15

1 Answers1

1

Meta: this is a workaround not an answer for the Q as posed, but needs formatting.

To create a PKCS12 the easy way, just use KeyStore as explained in its javadoc, which should be available in your IDE or on Oracle's website. Here is a minimal example combining the pieces from the javadoc. Since your Q doesn't show where you're getting the createCredentials data from, for this example I just read from an existing JKS; alter this as needed. And use a better password!

I demonstrate both using the default provider (SunJSSE) and the BC provider. One caveat I found in final testing: BC needs the 'unlimited strength policy' installed on Oracle java -- at least up to j8; this 'feature' reportedly has finally been removed in j9 but I haven't tested yet. Perhaps it treats TDES key size of 168 as exceeding 128, even though TDES strength is only 112 and should be permitted.

char[] pw = "password".toCharArray(); // dummy for test
boolean useBC = System.getProperty("useBC")!=null;
KeyStore jks = KeyStore.getInstance("JKS");
InputStream f1 = new FileInputStream ("old.jks"); jks.load(f1, pw); f1.close();
PrivateKey pkey = (PrivateKey)jks.getKey("mykey", pw);
Certificate[] chain = jks.getCertificateChain("mykey");

// only needed once, and not at all if pre-configured in java.security  
if(useBC) Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

// then do something like this
KeyStore p12 = useBC? KeyStore.getInstance("PKCS12","BC"): KeyStore.getInstance("PKCS12");
p12.load(null); p12.setKeyEntry("mykey", pkey, pw, chain);
OutputStream f2 = new FileOutputStream ("new.p12"); p12.store(f2,pw); f2.close();
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70