FileDocCategorySizeDatePackage
JDKPKCS12KeyStore.javaAPI DocAndroid 1.5 API52692Wed May 06 22:41:06 BST 2009org.bouncycastle.jce.provider

JDKPKCS12KeyStore

public class JDKPKCS12KeyStore extends KeyStoreSpi implements org.bouncycastle.asn1.x509.X509ObjectIdentifiers, org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers, org.bouncycastle.jce.interfaces.BCKeyStore

Fields Summary
private static final int
SALT_SIZE
private static final int
MIN_ITERATIONS
private static final String
KEY_ALGORITHM
private static final String
CERT_ALGORITHM
private Hashtable
keys
private Hashtable
localIds
private Hashtable
certs
private Hashtable
chainCerts
private Hashtable
keyCerts
static final int
NULL
static final int
CERTIFICATE
static final int
KEY
static final int
SECRET
static final int
SEALED
static final int
KEY_PRIVATE
static final int
KEY_PUBLIC
static final int
KEY_SECRET
protected SecureRandom
random
private CertificateFactory
certFact
Constructors Summary
public JDKPKCS12KeyStore(String provider)

        try
        {
            if (provider != null)
            {
                certFact = CertificateFactory.getInstance("X.509", provider);
            }
            else
            {
                certFact = CertificateFactory.getInstance("X.509");
            }
        }
        catch (Exception e)
        {
            throw new IllegalArgumentException("can't create cert factory - " + e.toString());
        }
    
Methods Summary
private org.bouncycastle.asn1.x509.SubjectKeyIdentifiercreateSubjectKeyId(java.security.PublicKey pubKey)

        try
        {
            SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
                (ASN1Sequence)new ASN1InputStream(pubKey.getEncoded()).readObject());

            return new SubjectKeyIdentifier(info);
        }
        catch (Exception e)
        {
            throw new RuntimeException("error creating key");
        }
    
protected org.bouncycastle.asn1.ASN1SequencedecryptData(org.bouncycastle.asn1.x509.AlgorithmIdentifier algId, byte[] data, char[] password, boolean wrongPKCS12Zero)

        String              algorithm = algId.getObjectId().getId();
        PKCS12PBEParams     pbeParams = new PKCS12PBEParams((ASN1Sequence)algId.getParameters());

        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
        byte[]              out = null;

        try
        {
            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
                                                algorithm, "BC");
            PBEParameterSpec    defParams = new PBEParameterSpec(
                                                pbeParams.getIV(),
                                                pbeParams.getIterations().intValue());
            SecretKey           k = keyFact.generateSecret(pbeSpec);
            
            ((JCEPBEKey)k).setTryWrongPKCS12Zero(wrongPKCS12Zero);

            Cipher cipher = Cipher.getInstance(algorithm, "BC");

            cipher.init(Cipher.DECRYPT_MODE, k, defParams);

            out = cipher.doFinal(data);
        }
        catch (Exception e)
        {
            throw new IOException("exception decrypting data - " + e.toString());
        }

        ASN1InputStream  aIn = new ASN1InputStream(out);

        return (ASN1Sequence)aIn.readObject();
    
protected byte[]encryptData(java.lang.String algorithm, byte[] data, org.bouncycastle.asn1.pkcs.PKCS12PBEParams pbeParams, char[] password)

        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
        byte[]              out;

        try
        {
            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
                                                algorithm, "BC");
            PBEParameterSpec    defParams = new PBEParameterSpec(
                                                pbeParams.getIV(),
                                                pbeParams.getIterations().intValue());

            Cipher cipher = Cipher.getInstance(algorithm, "BC");

            cipher.init(Cipher.ENCRYPT_MODE, keyFact.generateSecret(pbeSpec), defParams);

            out = cipher.doFinal(data);
        }
        catch (Exception e)
        {
            throw new IOException("exception encrypting data - " + e.toString());
        }

        return out;
    
public java.util.EnumerationengineAliases()

        Hashtable  tab = new Hashtable();

        Enumeration e = certs.keys();
        while (e.hasMoreElements())
        {
            tab.put(e.nextElement(), "cert");
        }

        e = keys.keys();
        while (e.hasMoreElements())
        {
            String  a = (String)e.nextElement();
            if (tab.get(a) == null)
            {
                tab.put(a, "key");
            }
        }

        return tab.keys();
    
public booleanengineContainsAlias(java.lang.String alias)

        return (certs.get(alias) != null || keys.get(alias) != null);
    
public voidengineDeleteEntry(java.lang.String alias)
this is quite complete - we should follow up on the chain, a bit tricky if a certificate appears in more than one chain...

        Key k = (Key)keys.remove(alias);

        Certificate c = (Certificate)certs.remove(alias);

        if (c != null)
        {
            chainCerts.remove(new CertId(c.getPublicKey()));
        }

        if (k != null)
        {
            String  id = (String)localIds.remove(alias);
            if (id != null)
            {
                c = (Certificate)keyCerts.remove(id);
            }
            if (c != null)
            {
                chainCerts.remove(new CertId(c.getPublicKey()));
            }
        }

        if (c == null && k == null)
        {
            throw new KeyStoreException("no such entry as " + alias);
        }
    
public java.security.cert.CertificateengineGetCertificate(java.lang.String alias)
simply return the cert for the private key

        if (alias == null)
        {
            throw new IllegalArgumentException("null alias passed to getCertificate.");
        }
        
        Certificate c = (Certificate)certs.get(alias);

        //
        // look up the key table - and try the local key id
        //
        if (c == null)
        {
            String  id = (String)localIds.get(alias);
            if (id != null)
            {
                c = (Certificate)keyCerts.get(id);
            }
            else
            {
                c = (Certificate)keyCerts.get(alias);
            }
        }

        return c;
    
public java.lang.StringengineGetCertificateAlias(java.security.cert.Certificate cert)

        Enumeration c = certs.elements();
        Enumeration k = certs.keys();

        while (c.hasMoreElements())
        {
            Certificate tc = (Certificate)c.nextElement();
            String      ta = (String)k.nextElement();

            if (tc.equals(cert))
            {
                return ta;
            }
        }

        c = keyCerts.elements();
        k = keyCerts.keys();

        while (c.hasMoreElements())
        {
            Certificate tc = (Certificate)c.nextElement();
            String      ta = (String)k.nextElement();

            if (tc.equals(cert))
            {
                return ta;
            }
        }
        
        return null;
    
public java.security.cert.Certificate[]engineGetCertificateChain(java.lang.String alias)

        if (alias == null)
        {
            throw new IllegalArgumentException("null alias passed to getCertificateChain.");
        }
        
        if (!engineIsKeyEntry(alias))
        {
            return null;
        }
        
        Certificate c = engineGetCertificate(alias);

        if (c != null)
        {
            Vector  cs = new Vector();

            while (c != null)
            {
                X509Certificate     x509c = (X509Certificate)c;
                Certificate         nextC = null;

                byte[]  bytes = x509c.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
                if (bytes != null)
                {
                    try
                    {
                        ASN1InputStream         aIn = new ASN1InputStream(bytes);

                        byte[] authBytes = ((ASN1OctetString)aIn.readObject()).getOctets();
                        aIn = new ASN1InputStream(authBytes);

                        AuthorityKeyIdentifier id = new AuthorityKeyIdentifier((ASN1Sequence)aIn.readObject());
                        if (id.getKeyIdentifier() != null)
                        {
                            nextC = (Certificate)chainCerts.get(new CertId(id.getKeyIdentifier()));
                        }
                        
                    }
                    catch (IOException e)
                    {
                        throw new RuntimeException(e.toString());
                    }
                }

                if (nextC == null)
                {
                    //
                    // no authority key id, try the Issuer DN
                    //
                    Principal  i = x509c.getIssuerDN();
                    Principal  s = x509c.getSubjectDN();

                    if (!i.equals(s))
                    {
                        Enumeration e = chainCerts.keys();

                        while (e.hasMoreElements())
                        {
                            X509Certificate crt = (X509Certificate)chainCerts.get(e.nextElement());
                            Principal  sub = crt.getSubjectDN();
                            if (sub.equals(i))
                            {
                                try
                                {
                                    x509c.verify(crt.getPublicKey());
                                    nextC = crt;
                                    break;
                                }
                                catch (Exception ex)
                                {
                                    // continue
                                }
                            }
                        }
                    }
                }

                cs.addElement(c);
                if (nextC != c)     // self signed - end of the chain
                {
                    c = nextC;
                }
                else
                {
                    c = null;
                }
            }

            Certificate[]   certChain = new Certificate[cs.size()];

            for (int i = 0; i != certChain.length; i++)
            {
                certChain[i] = (Certificate)cs.elementAt(i);
            }

            return certChain;
        }

        return null;
    
public java.util.DateengineGetCreationDate(java.lang.String alias)

        return new Date();
    
public java.security.KeyengineGetKey(java.lang.String alias, char[] password)

        if (alias == null)
        {
            throw new IllegalArgumentException("null alias passed to getKey.");
        }
        
        return (Key)keys.get(alias);
    
public booleanengineIsCertificateEntry(java.lang.String alias)

        return (certs.get(alias) != null && keys.get(alias) == null);
    
public booleanengineIsKeyEntry(java.lang.String alias)

        return (keys.get(alias) != null);
    
public voidengineLoad(java.io.InputStream stream, char[] password)

        if (stream == null)     // just initialising
        {
            return;
        }

        if (password == null)
        {
            throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
        }

        // BEGIN android-modified
        BufferedInputStream             bufIn = new BufferedInputStream(stream, 8192);
        // END android-modified

        bufIn.mark(10);

        int head = bufIn.read();

        if (head != 0x30)
        {
            throw new IOException("stream does not represent a PKCS12 key store");
        }

        bufIn.reset();

        ASN1InputStream bIn = new ASN1InputStream(bufIn);
        ASN1Sequence    obj = (ASN1Sequence)bIn.readObject();
        Pfx             bag = new Pfx(obj);
        ContentInfo     info = bag.getAuthSafe();
        Vector          chain = new Vector();
        boolean         unmarkedKey = false;
        boolean         wrongPKCS12Zero = false;

        if (bag.getMacData() != null)           // check the mac code
        {
            ByteArrayOutputStream       bOut = new ByteArrayOutputStream();
            BEROutputStream             berOut = new BEROutputStream(bOut);
            MacData                     mData = bag.getMacData();
            DigestInfo                  dInfo = mData.getMac();
            AlgorithmIdentifier         algId = dInfo.getAlgorithmId();
            byte[]                      salt = mData.getSalt();
            int                         itCount = mData.getIterationCount().intValue();
        
            berOut.writeObject(info);

            byte[]  data = ((ASN1OctetString)info.getContent()).getOctets();

            try
            {
                Mac                 mac = Mac.getInstance(algId.getObjectId().getId(), "BC");
                SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(algId.getObjectId().getId(), "BC");
                PBEParameterSpec    defParams = new PBEParameterSpec(salt, itCount);
                PBEKeySpec          pbeSpec = new PBEKeySpec(password);

                mac.init(keyFact.generateSecret(pbeSpec), defParams);

                mac.update(data);

                byte[]  res = mac.doFinal();
                byte[]  dig = dInfo.getDigest();

                if (res.length != dInfo.getDigest().length)
                {
                    throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
                }

                boolean okay = true;
                
                for (int i = 0; i != res.length; i++)
                {
                    if (res[i] != dig[i])
                    {
                        if (password.length != 0)  // may be dodgey zero password
                        {
                            throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
                        }
                        else
                        {
                            okay = false;
                            break;
                        }
                    }
                }
                
                //
                // may be incorrect zero length password
                //
                if (!okay)
                {
                    SecretKey k = keyFact.generateSecret(pbeSpec);
                    
                    ((JCEPBEKey)k).setTryWrongPKCS12Zero(true);
                    
                    mac.init(k, defParams);
    
                    mac.update(data);
    
                    res = mac.doFinal();
                    dig = dInfo.getDigest();
                    
                    for (int i = 0; i != res.length; i++)
                    {
                        if (res[i] != dig[i])
                        {
                           throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
                        }
                    }
                    
                    wrongPKCS12Zero = true;
                }
            }
            catch (IOException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                throw new IOException("error constructing MAC: " + e.toString());
            }
        }

        keys = new Hashtable();
        localIds = new Hashtable();

        if (info.getContentType().equals(data))
        {
            bIn = new ASN1InputStream(((ASN1OctetString)info.getContent()).getOctets());

            AuthenticatedSafe   authSafe = new AuthenticatedSafe((ASN1Sequence)bIn.readObject());
            ContentInfo[]       c = authSafe.getContentInfo();

            for (int i = 0; i != c.length; i++)
            {
                if (c[i].getContentType().equals(data))
                {
                    ASN1InputStream dIn = new ASN1InputStream(((ASN1OctetString)c[i].getContent()).getOctets());
                    ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();

                    for (int j = 0; j != seq.size(); j++)
                    {
                        SafeBag b = new SafeBag((ASN1Sequence)seq.getObjectAt(j));
                        if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                        {
                            org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = new org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo((ASN1Sequence)b.getBagValue());
                            PrivateKey              privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);

                            //
                            // set the attributes on the key
                            //
                            PKCS12BagAttributeCarrier   bagAttr = (PKCS12BagAttributeCarrier)privKey;
                            String                                   alias = null;
                            ASN1OctetString                   localId = null;

                            if (b.getBagAttributes() != null)
                            {
                                Enumeration e = b.getBagAttributes().getObjects();
                                while (e.hasMoreElements())
                                {
                                    ASN1Sequence  sq = (ASN1Sequence)e.nextElement();
                                    DERObjectIdentifier     aOid = (DERObjectIdentifier)sq.getObjectAt(0);
                                    ASN1Set                 attrSet = (ASN1Set)sq.getObjectAt(1);
                                    DERObject               attr = null;
    
                                    if (attrSet.size() > 0)
                                    {
                                        attr = (DERObject)attrSet.getObjectAt(0);
    
                                        bagAttr.setBagAttribute(aOid, attr);
                                    }
    
                                    if (aOid.equals(pkcs_9_at_friendlyName))
                                    {
                                        alias = ((DERBMPString)attr).getString();
                                        keys.put(alias, privKey);
                                    }
                                    else if (aOid.equals(pkcs_9_at_localKeyId))
                                    {
                                        localId = (ASN1OctetString)attr;
                                    }
                                }
                            }
                        
                            if (localId != null)
                            {
                                String name = new String(Hex.encode(localId.getOctets()));
    
                                if (alias == null)
                                {
                                    keys.put(name, privKey);
                                }
                                else
                                {
                                    localIds.put(alias, name);
                                }
                             }
                             else
                             {
                                 unmarkedKey = true;
                                 keys.put("unmarked", privKey);
                             }
                        }
                        else if (b.getBagId().equals(certBag))
                        {
                            chain.addElement(b);
                        }
                        else
                        {
                            System.out.println("extra in data " + b.getBagId());
                            System.out.println(ASN1Dump.dumpAsString(b));
                        }
                    }
                }
                else if (c[i].getContentType().equals(encryptedData))
                {
                    EncryptedData d = new EncryptedData((ASN1Sequence)c[i].getContent());
                    ASN1Sequence seq = decryptData(d.getEncryptionAlgorithm(), d.getContent().getOctets(), password, wrongPKCS12Zero);

                    for (int j = 0; j != seq.size(); j++)
                    {
                        SafeBag b = new SafeBag((ASN1Sequence)seq.getObjectAt(j));
                        
                        if (b.getBagId().equals(certBag))
                        {
                            chain.addElement(b);
                        }
                        else if (b.getBagId().equals(pkcs8ShroudedKeyBag))
                        {
                            org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = new org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo((ASN1Sequence)b.getBagValue());
                            PrivateKey              privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);

                            //
                            // set the attributes on the key
                            //
                            PKCS12BagAttributeCarrier   bagAttr = (PKCS12BagAttributeCarrier)privKey;
                            String                      alias = null;
                            ASN1OctetString              localId = null;

                            Enumeration e = b.getBagAttributes().getObjects();
                            while (e.hasMoreElements())
                            {
                                ASN1Sequence  sq = (ASN1Sequence)e.nextElement();
                                DERObjectIdentifier     aOid = (DERObjectIdentifier)sq.getObjectAt(0);
                                ASN1Set                 attrSet= (ASN1Set)sq.getObjectAt(1);
                                DERObject               attr = null;

                                if (attrSet.size() > 0)
                                {
                                    attr = (DERObject)attrSet.getObjectAt(0);

                                    bagAttr.setBagAttribute(aOid, attr);
                                }

                                if (aOid.equals(pkcs_9_at_friendlyName))
                                {
                                    alias = ((DERBMPString)attr).getString();
                                    keys.put(alias, privKey);
                                }
                                else if (aOid.equals(pkcs_9_at_localKeyId))
                                {
                                    localId = (ASN1OctetString)attr;
                                }
                            }

                            String name = new String(Hex.encode(localId.getOctets()));

                            if (alias == null)
                            {
                                keys.put(name, privKey);
                            }
                            else
                            {
                                localIds.put(alias, name);
                            }
                        }
                        else if (b.getBagId().equals(keyBag))
                        {
                            org.bouncycastle.asn1.pkcs.PrivateKeyInfo pIn = new org.bouncycastle.asn1.pkcs.PrivateKeyInfo((ASN1Sequence)b.getBagValue());
                            PrivateKey              privKey = JDKKeyFactory.createPrivateKeyFromPrivateKeyInfo(pIn);

                            //
                            // set the attributes on the key
                            //
                            PKCS12BagAttributeCarrier   bagAttr = (PKCS12BagAttributeCarrier)privKey;
                            String                      alias = null;
                            ASN1OctetString             localId = null;

                            Enumeration e = b.getBagAttributes().getObjects();
                            while (e.hasMoreElements())
                            {
                                ASN1Sequence  sq = (ASN1Sequence)e.nextElement();
                                DERObjectIdentifier     aOid = (DERObjectIdentifier)sq.getObjectAt(0);
                                ASN1Set                 attrSet = (ASN1Set)sq.getObjectAt(1);
                                DERObject   attr = null;

                                if (attrSet.size() > 0)
                                {
                                    attr = (DERObject)attrSet.getObjectAt(0);

                                    bagAttr.setBagAttribute(aOid, attr);
                                }

                                if (aOid.equals(pkcs_9_at_friendlyName))
                                {
                                    alias = ((DERBMPString)attr).getString();
                                    keys.put(alias, privKey);
                                }
                                else if (aOid.equals(pkcs_9_at_localKeyId))
                                {
                                    localId = (ASN1OctetString)attr;
                                }
                            }

                            String name = new String(Hex.encode(localId.getOctets()));

                            if (alias == null)
                            {
                                keys.put(name, privKey);
                            }
                            else
                            {
                                localIds.put(alias, name);
                            }
                        }
                        else
                        {
                            System.out.println("extra in encryptedData " + b.getBagId());
                            System.out.println(ASN1Dump.dumpAsString(b));
                        }
                    }
                }
                else
                {
                    System.out.println("extra " + c[i].getContentType().getId());
                    System.out.println("extra " + ASN1Dump.dumpAsString(c[i].getContent()));
                }
            }
        }

        certs = new Hashtable();
        chainCerts = new Hashtable();
        keyCerts = new Hashtable();

        for (int i = 0; i != chain.size(); i++)
        {
            SafeBag     b = (SafeBag)chain.elementAt(i);
            CertBag     cb = new CertBag((ASN1Sequence)b.getBagValue());
            Certificate cert = null;

            try
            {
                ByteArrayInputStream  cIn = new ByteArrayInputStream(
                                ((ASN1OctetString)cb.getCertValue()).getOctets());
                cert = certFact.generateCertificate(cIn);
            }
            catch (Exception e)
            {
                throw new RuntimeException(e.toString());
            }


            //
            // set the attributes
            //
            ASN1OctetString              localId = null;
            String                      alias = null;

            if (b.getBagAttributes() != null)
            {
                Enumeration e = b.getBagAttributes().getObjects();
                while (e.hasMoreElements())
                {
                    ASN1Sequence  sq = (ASN1Sequence)e.nextElement();
                    DERObjectIdentifier     oid = (DERObjectIdentifier)sq.getObjectAt(0);
                    DERObject               attr = (DERObject)((ASN1Set)sq.getObjectAt(1)).getObjectAt(0);

                    if (cert instanceof PKCS12BagAttributeCarrier)
                    {
                        PKCS12BagAttributeCarrier   bagAttr = (PKCS12BagAttributeCarrier)cert;
                        bagAttr.setBagAttribute(oid, attr);
                    }

                    if (oid.equals(pkcs_9_at_friendlyName))
                    {
                        alias = ((DERBMPString)attr).getString();
                    }
                    else if (oid.equals(pkcs_9_at_localKeyId))
                    {
                        localId = (ASN1OctetString)attr;
                    }
                }
            }

            chainCerts.put(new CertId(cert.getPublicKey()), cert);

            if (unmarkedKey)
            {
                if (keyCerts.isEmpty())
                {
                    String    name = new String(Hex.encode(createSubjectKeyId(cert.getPublicKey()).getKeyIdentifier()));
                    
                    keyCerts.put(name, cert);
                    keys.put(name, keys.remove("unmarked"));
                }
            }
            else
            {
                //
                // the local key id needs to override the friendly name
                //
                if (localId != null)
                {
                    String name = new String(Hex.encode(localId.getOctets()));

                    keyCerts.put(name, cert);
                }
                if (alias != null)
                {
                    certs.put(alias, cert);
                }
            }
        }
    
public voidengineSetCertificateEntry(java.lang.String alias, java.security.cert.Certificate cert)

        if (certs.get(alias) != null)
        {
            throw new KeyStoreException("There is already a certificate with the name " + alias + ".");
        }

        certs.put(alias, cert);
        chainCerts.put(new CertId(cert.getPublicKey()), cert);
    
public voidengineSetKeyEntry(java.lang.String alias, byte[] key, java.security.cert.Certificate[] chain)

        throw new RuntimeException("operation not supported");
    
public voidengineSetKeyEntry(java.lang.String alias, java.security.Key key, char[] password, java.security.cert.Certificate[] chain)

        if ((key instanceof PrivateKey) && (chain == null))
        {
            throw new KeyStoreException("no certificate chain for private key");
        }

        if (keys.get(alias) != null && !key.equals(keys.get(alias)))
        {
            throw new KeyStoreException("There is already a key with the name " + alias + ".");
        }

        keys.put(alias, key);
        certs.put(alias, chain[0]);

        for (int i = 0; i != chain.length; i++)
        {
            chainCerts.put(new CertId(chain[i].getPublicKey()), chain[i]);
        }
    
public intengineSize()

        Hashtable  tab = new Hashtable();

        Enumeration e = certs.keys();
        while (e.hasMoreElements())
        {
            tab.put(e.nextElement(), "cert");
        }

        e = keys.keys();
        while (e.hasMoreElements())
        {
            String  a = (String)e.nextElement();
            if (tab.get(a) == null)
            {
                tab.put(a, "key");
            }
        }

        return tab.size();
    
public voidengineStore(java.io.OutputStream stream, char[] password)

        if (password == null)
        {
            throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
        }

        ContentInfo[]   c = new ContentInfo[2];


        //
        // handle the key
        //
        ASN1EncodableVector  keyS = new ASN1EncodableVector();


        Enumeration ks = keys.keys();

        while (ks.hasMoreElements())
        {
            byte[]                  kSalt = new byte[SALT_SIZE];

            random.nextBytes(kSalt);

            String                  name = (String)ks.nextElement();
            PrivateKey              privKey = (PrivateKey)keys.get(name);
            PKCS12PBEParams         kParams = new PKCS12PBEParams(kSalt, MIN_ITERATIONS);
            byte[]                  kBytes = wrapKey(KEY_ALGORITHM, privKey, kParams, password);
            AlgorithmIdentifier     kAlgId = new AlgorithmIdentifier(new DERObjectIdentifier(KEY_ALGORITHM), kParams.getDERObject());
            org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo kInfo = new org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo(kAlgId, kBytes);
            boolean                 attrSet = false;
            ASN1EncodableVector     kName = new ASN1EncodableVector();

            if (privKey instanceof PKCS12BagAttributeCarrier)
            {
                PKCS12BagAttributeCarrier   bagAttrs = (PKCS12BagAttributeCarrier)privKey;
                //
                // make sure we are using the local alias on store
                //
                DERBMPString    nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                if (nm == null || !nm.getString().equals(name))
                {
                    bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
                }

                //
                // make sure we have a local key-id
                //
                if (bagAttrs.getBagAttribute(pkcs_9_at_localKeyId) == null)
                {
                    Certificate             ct = engineGetCertificate(name);

                    bagAttrs.setBagAttribute(pkcs_9_at_localKeyId, createSubjectKeyId(ct.getPublicKey()));
                }

                Enumeration e = bagAttrs.getBagAttributeKeys();

                while (e.hasMoreElements())
                {
                    DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
                    ASN1EncodableVector  kSeq = new ASN1EncodableVector();

                    kSeq.add(oid);
                    kSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));

                    attrSet = true;

                    kName.add(new DERSequence(kSeq));
                }
            }

            if (!attrSet)
            {
                //
                // set a default friendly name (from the key id) and local id
                //
                ASN1EncodableVector     kSeq = new ASN1EncodableVector();
                Certificate             ct = engineGetCertificate(name);

                kSeq.add(pkcs_9_at_localKeyId);
                kSeq.add(new DERSet(createSubjectKeyId(ct.getPublicKey())));

                kName.add(new DERSequence(kSeq));

                kSeq = new ASN1EncodableVector();

                kSeq.add(pkcs_9_at_friendlyName);
                kSeq.add(new DERSet(new DERBMPString(name)));

                kName.add(new DERSequence(kSeq));
            }

            SafeBag                 kBag = new SafeBag(pkcs8ShroudedKeyBag, kInfo.getDERObject(), new DERSet(kName));
            keyS.add(kBag);
        }

        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
        DEROutputStream         dOut = new DEROutputStream(bOut);

        dOut.writeObject(new DERSequence(keyS));

        BERConstructedOctetString          keyString = new BERConstructedOctetString(bOut.toByteArray());

        //
        // certficate processing
        //
        byte[]                  cSalt = new byte[SALT_SIZE];

        random.nextBytes(cSalt);

        ASN1EncodableVector  certSeq = new ASN1EncodableVector();
        PKCS12PBEParams         cParams = new PKCS12PBEParams(cSalt, MIN_ITERATIONS);
        AlgorithmIdentifier     cAlgId = new AlgorithmIdentifier(new DERObjectIdentifier(CERT_ALGORITHM), cParams.getDERObject());
        Hashtable               doneCerts = new Hashtable();

        Enumeration cs = keys.keys();
        while (cs.hasMoreElements())
        {
            try
            {
                String              name = (String)cs.nextElement();
                Certificate         cert = engineGetCertificate(name);
                boolean             cAttrSet = false;
                CertBag             cBag = new CertBag(
                                        x509certType,
                                        new DEROctetString(cert.getEncoded()));
                ASN1EncodableVector fName = new ASN1EncodableVector();

                if (cert instanceof PKCS12BagAttributeCarrier)
                {
                    PKCS12BagAttributeCarrier   bagAttrs = (PKCS12BagAttributeCarrier)cert;
                    //
                    // make sure we are using the local alias on store
                    //
                    DERBMPString    nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                    if (nm == null || !nm.getString().equals(name))
                    {
                        bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
                    }

                    //
                    // make sure we have a local key-id
                    //
                    if (bagAttrs.getBagAttribute(pkcs_9_at_localKeyId) == null)
                    {
                        bagAttrs.setBagAttribute(pkcs_9_at_localKeyId, createSubjectKeyId(cert.getPublicKey()));
                    }

                    Enumeration e = bagAttrs.getBagAttributeKeys();

                    while (e.hasMoreElements())
                    {
                        DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
                        ASN1EncodableVector fSeq = new ASN1EncodableVector();

                        fSeq.add(oid);
                        fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
                        fName.add(new DERSequence(fSeq));

                        cAttrSet = true;
                    }
                }

                if (!cAttrSet)
                {
                    ASN1EncodableVector  fSeq = new ASN1EncodableVector();

                    fSeq.add(pkcs_9_at_localKeyId);
                    fSeq.add(new DERSet(createSubjectKeyId(cert.getPublicKey())));
                    fName.add(new DERSequence(fSeq));

                    fSeq = new ASN1EncodableVector();

                    fSeq.add(pkcs_9_at_friendlyName);
                    fSeq.add(new DERSet(new DERBMPString(name)));

                    fName.add(new DERSequence(fSeq));
                }

                SafeBag sBag = new SafeBag(certBag, cBag.getDERObject(), new DERSet(fName));

                certSeq.add(sBag);

                doneCerts.put(cert, cert);
            }
            catch (CertificateEncodingException e)
            {
                throw new IOException("Error encoding certificate: " + e.toString());
            }
        }

        cs = certs.keys();
        while (cs.hasMoreElements())
        {
            try
            {
                String              certId = (String)cs.nextElement();
                Certificate         cert = (Certificate)certs.get(certId);
                boolean             cAttrSet = false;

                if (keys.get(certId) != null)
                {
                    continue;
                }

                CertBag             cBag = new CertBag(
                                        x509certType,
                                        new DEROctetString(cert.getEncoded()));
                ASN1EncodableVector fName = new ASN1EncodableVector();

                if (cert instanceof PKCS12BagAttributeCarrier)
                {
                    PKCS12BagAttributeCarrier   bagAttrs = (PKCS12BagAttributeCarrier)cert;
                    //
                    // make sure we are using the local alias on store
                    //
                    DERBMPString    nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
                    if (nm == null || !nm.getString().equals(certId))
                    {
                        bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(certId));
                    }

                    Enumeration e = bagAttrs.getBagAttributeKeys();

                    while (e.hasMoreElements())
                    {
                        DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
                        ASN1EncodableVector fSeq = new ASN1EncodableVector();

                        fSeq.add(oid);
                        fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
                        fName.add(new DERSequence(fSeq));

                        cAttrSet = true;
                    }
                }

                if (!cAttrSet)
                {
                    ASN1EncodableVector  fSeq = new ASN1EncodableVector();

                    fSeq.add(pkcs_9_at_friendlyName);
                    fSeq.add(new DERSet(new DERBMPString(certId)));

                    fName.add(new DERSequence(fSeq));
                }

                SafeBag sBag = new SafeBag(certBag, cBag.getDERObject(), new DERSet(fName));

                certSeq.add(sBag);

                doneCerts.put(cert, cert);
            }
            catch (CertificateEncodingException e)
            {
                throw new IOException("Error encoding certificate: " + e.toString());
            }
        }

        cs = chainCerts.keys();
        while (cs.hasMoreElements())
        {
            try
            {
                CertId              certId = (CertId)cs.nextElement();
                Certificate         cert = (Certificate)chainCerts.get(certId);

                if (doneCerts.get(cert) != null)
                {
                    continue;
                }

                CertBag             cBag = new CertBag(
                                        x509certType,
                                        new DEROctetString(cert.getEncoded()));
                ASN1EncodableVector fName = new ASN1EncodableVector();

                if (cert instanceof PKCS12BagAttributeCarrier)
                {
                    PKCS12BagAttributeCarrier   bagAttrs = (PKCS12BagAttributeCarrier)cert;
                    Enumeration e = bagAttrs.getBagAttributeKeys();

                    while (e.hasMoreElements())
                    {
                        DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
                        ASN1EncodableVector fSeq = new ASN1EncodableVector();

                        fSeq.add(oid);
                        fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
                        fName.add(new DERSequence(fSeq));
                    }
                }

                SafeBag sBag = new SafeBag(certBag, cBag.getDERObject(), new DERSet(fName));

                certSeq.add(sBag);
            }
            catch (CertificateEncodingException e)
            {
                throw new IOException("Error encoding certificate: " + e.toString());
            }
        }

        bOut.reset();

        dOut = new DEROutputStream(bOut);

        dOut.writeObject(new DERSequence(certSeq));

        dOut.close();

        byte[]                  certBytes = encryptData(CERT_ALGORITHM, bOut.toByteArray(), cParams, password);
        EncryptedData           cInfo = new EncryptedData(data, cAlgId, new BERConstructedOctetString(certBytes));

        c[0] = new ContentInfo(data, keyString);

        c[1] = new ContentInfo(encryptedData, cInfo.getDERObject());

        AuthenticatedSafe   auth = new AuthenticatedSafe(c);

        bOut.reset();

        BEROutputStream         berOut = new BEROutputStream(bOut);

        berOut.writeObject(auth);

        byte[]              pkg = bOut.toByteArray();

        ContentInfo         mainInfo = new ContentInfo(data, new BERConstructedOctetString(pkg));

        //
        // create the mac
        //
        byte[]                      mSalt = new byte[20];
        int                         itCount = MIN_ITERATIONS;

        random.nextBytes(mSalt);
    
        byte[]  data = ((ASN1OctetString)mainInfo.getContent()).getOctets();

        MacData                 mData = null;

        try
        {
            Mac                 mac = Mac.getInstance(id_SHA1.getId(), "BC");
            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(id_SHA1.getId(), "BC");
            PBEParameterSpec    defParams = new PBEParameterSpec(mSalt, itCount);
            PBEKeySpec          pbeSpec = new PBEKeySpec(password);

            mac.init(keyFact.generateSecret(pbeSpec), defParams);

            mac.update(data);

            byte[]      res = mac.doFinal();

            // BEGIN android-changed
            AlgorithmIdentifier     algId = new AlgorithmIdentifier(id_SHA1, DERNull.THE_ONE);
            // END android-changed
            DigestInfo              dInfo = new DigestInfo(algId, res);

            mData = new MacData(dInfo, mSalt, itCount);
        }
        catch (Exception e)
        {
            throw new IOException("error constructing MAC: " + e.toString());
        }
        
        //
        // output the Pfx
        //
        Pfx                 pfx = new Pfx(mainInfo, mData);

        berOut = new BEROutputStream(stream);

        berOut.writeObject(pfx);
    
public voidsetRandom(java.security.SecureRandom rand)

        this.random = rand;
    
protected java.security.PrivateKeyunwrapKey(org.bouncycastle.asn1.x509.AlgorithmIdentifier algId, byte[] data, char[] password, boolean wrongPKCS12Zero)

        String              algorithm = algId.getObjectId().getId();
        PKCS12PBEParams     pbeParams = new PKCS12PBEParams((ASN1Sequence)algId.getParameters());

        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
        PrivateKey          out = null;

        try
        {
            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
                                                algorithm, "BC");
            PBEParameterSpec    defParams = new PBEParameterSpec(
                                                pbeParams.getIV(),
                                                pbeParams.getIterations().intValue());

            SecretKey           k = keyFact.generateSecret(pbeSpec);
            
            ((JCEPBEKey)k).setTryWrongPKCS12Zero(wrongPKCS12Zero);

            Cipher cipher = Cipher.getInstance(algorithm, "BC");

            cipher.init(Cipher.UNWRAP_MODE, k, defParams);

            // we pass "" as the key algorithm type as it is unknown at this point
            out = (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY);
        }
        catch (Exception e)
        {
            throw new IOException("exception unwrapping private key - " + e.toString());
        }

        return out;
    
protected byte[]wrapKey(java.lang.String algorithm, java.security.Key key, org.bouncycastle.asn1.pkcs.PKCS12PBEParams pbeParams, char[] password)

        PBEKeySpec          pbeSpec = new PBEKeySpec(password);
        byte[]              out;

        try
        {
            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(
                                                algorithm, "BC");
            PBEParameterSpec    defParams = new PBEParameterSpec(
                                                pbeParams.getIV(),
                                                pbeParams.getIterations().intValue());

            Cipher cipher = Cipher.getInstance(algorithm, "BC");

            cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), defParams);

            out = cipher.wrap(key);
        }
        catch (Exception e)
        {
            throw new IOException("exception encrypting data - " + e.toString());
        }

        return out;