summary refs log tree commit diff
path: root/crypto/src/pkcs/Pkcs12Store.cs
diff options
context:
space:
mode:
authorOren Novotny <oren@novotny.org>2014-10-14 09:24:13 -0400
committerOren Novotny <oren@novotny.org>2014-10-14 09:24:13 -0400
commitcdd270cb0033455205cdb9a82e9632a733ef472b (patch)
treec1dba7a8dde3980e4e2db366ae611d99a5bb7608 /crypto/src/pkcs/Pkcs12Store.cs
parentRename csproj to avoid conflicts (diff)
parentUpdate ignore file (diff)
downloadBouncyCastle.NET-ed25519-cdd270cb0033455205cdb9a82e9632a733ef472b.tar.xz
Merge master-v12 into pcl. Includes 1.8-beta4
Diffstat (limited to 'crypto/src/pkcs/Pkcs12Store.cs')
-rw-r--r--crypto/src/pkcs/Pkcs12Store.cs401
1 files changed, 137 insertions, 264 deletions
diff --git a/crypto/src/pkcs/Pkcs12Store.cs b/crypto/src/pkcs/Pkcs12Store.cs
index 7e9976c9f..a024bcd6b 100644
--- a/crypto/src/pkcs/Pkcs12Store.cs
+++ b/crypto/src/pkcs/Pkcs12Store.cs
@@ -29,6 +29,8 @@ namespace Org.BouncyCastle.Pkcs
         private readonly DerObjectIdentifier	certAlgorithm;
         private readonly bool					useDerEncoding;
 
+        private AsymmetricKeyEntry unmarkedKeyEntry = null;
+
         private const int MinIterations = 1024;
         private const int SaltSize = 20;
 
@@ -108,95 +110,22 @@ namespace Org.BouncyCastle.Pkcs
             Load(input, password);
         }
 
-        public void Load(
-            Stream	input,
-            char[]	password)
+        protected virtual void LoadKeyBag(PrivateKeyInfo privKeyInfo, Asn1Set bagAttributes)
         {
-            if (input == null)
-                throw new ArgumentNullException("input");
-            if (password == null)
-                throw new ArgumentNullException("password");
-
-            Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input);
-            Pfx bag = new Pfx(obj);
-            ContentInfo info = bag.AuthSafe;
-            bool unmarkedKey = false;
-            bool wrongPkcs12Zero = false;
-
-            if (bag.MacData != null) // check the mac code
-            {
-                MacData mData = bag.MacData;
-                DigestInfo dInfo = mData.Mac;
-                AlgorithmIdentifier algId = dInfo.AlgorithmID;
-                byte[] salt = mData.GetSalt();
-                int itCount = mData.IterationCount.IntValue;
-
-                byte[] data = ((Asn1OctetString) info.Content).GetOctets();
-
-                byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data);
-                byte[] dig = dInfo.GetDigest();
-
-                if (!Arrays.ConstantTimeAreEqual(mac, dig))
-                {
-                    if (password.Length > 0)
-                        throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
-
-                    // Try with incorrect zero length password
-                    mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data);
-
-                    if (!Arrays.ConstantTimeAreEqual(mac, dig))
-                        throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
-
-                    wrongPkcs12Zero = true;
-                }
-            }
-
-            keys.Clear();
-            localIds.Clear();
-
-            IList chain = Platform.CreateArrayList();
-
-            if (info.ContentType.Equals(PkcsObjectIdentifiers.Data))
-            {
-                byte[] octs = ((Asn1OctetString)info.Content).GetOctets();
-                AuthenticatedSafe authSafe = new AuthenticatedSafe(
-                    (Asn1Sequence) Asn1OctetString.FromByteArray(octs));
-                ContentInfo[] cis = authSafe.GetContentInfo();
-
-                foreach (ContentInfo ci in cis)
-                {
-                    DerObjectIdentifier oid = ci.ContentType;
-
-                    if (oid.Equals(PkcsObjectIdentifiers.Data))
-                    {
-                        byte[] octets = ((Asn1OctetString)ci.Content).GetOctets();
-                        Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets);
+            AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo);
 
-                        foreach (Asn1Sequence subSeq in seq)
-                        {
-                            SafeBag b = new SafeBag(subSeq);
-
-                            if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
-                            {
-                                EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue);
-                                PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
-                                    password, wrongPkcs12Zero, eIn);
-                                AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo);
-
-                                //
-                                // set the attributes on the key
-                                //
                                 IDictionary attributes = Platform.CreateHashtable();
-                                AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
+            AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privKey, attributes);
+
                                 string alias = null;
                                 Asn1OctetString localId = null;
 
-                                if (b.BagAttributes != null)
+            if (bagAttributes != null)
                                 {
-                                    foreach (Asn1Sequence sq in b.BagAttributes)
+                foreach (Asn1Sequence sq in bagAttributes)
                                     {
-                                        DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
-                                        Asn1Set attrSet = (Asn1Set) sq[1];
+                    DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]);
+                    Asn1Set attrSet = Asn1Set.GetInstance(sq[1]);
                                         Asn1Encodable attr = null;
 
                                         if (attrSet.Count > 0)
@@ -210,10 +139,8 @@ namespace Org.BouncyCastle.Pkcs
                                             {
                                                 // OK, but the value has to be the same
                                                 if (!attributes[aOid.Id].Equals(attr))
-                                                {
                                                     throw new IOException("attempt to add existing attribute with different value");
                                                 }
-                                            }
                                             else
                                             {
                                                 attributes.Add(aOid.Id, attr);
@@ -223,7 +150,7 @@ namespace Org.BouncyCastle.Pkcs
                                             {
                                                 alias = ((DerBmpString)attr).GetString();
                                                 // TODO Do these in a separate loop, just collect aliases here
-                                                keys[alias] = pkcs12Key;
+                            keys[alias] = keyEntry;
                                             }
                                             else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
                                             {
@@ -239,7 +166,7 @@ namespace Org.BouncyCastle.Pkcs
 
                                     if (alias == null)
                                     {
-                                        keys[name] = pkcs12Key;
+                    keys[name] = keyEntry;
                                     }
                                     else
                                     {
@@ -249,183 +176,124 @@ namespace Org.BouncyCastle.Pkcs
                                 }
                                 else
                                 {
-                                    unmarkedKey = true;
-                                    keys["unmarked"] = pkcs12Key;
+                unmarkedKeyEntry = keyEntry;
                                 }
                             }
-                            else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
+
+        protected virtual void LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo encPrivKeyInfo, Asn1Set bagAttributes,
+            char[] password, bool wrongPkcs12Zero)
                             {
-                                chain.Add(b);
-                            }
-                            else
+            if (password != null)
                             {
-                                Debug.WriteLine("extra " + b.BagID);
-                                Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b));
+                PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
+                    password, wrongPkcs12Zero, encPrivKeyInfo);
+
+                LoadKeyBag(privInfo, bagAttributes);
                             }
                         }
-                    }
-                    else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData))
-                    {
-                        EncryptedData d = EncryptedData.GetInstance(ci.Content);
-                        byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm,
-                            password, wrongPkcs12Zero, d.Content.GetOctets());
-                        Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets);
 
-                        foreach (Asn1Sequence subSeq in seq)
+        public void Load(
+            Stream	input,
+            char[]	password)
                         {
-                            SafeBag b = new SafeBag(subSeq);
+            if (input == null)
+                throw new ArgumentNullException("input");
 
-                            if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
-                            {
-                                chain.Add(b);
-                            }
-                            else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
+            Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input);
+            Pfx bag = new Pfx(obj);
+            ContentInfo info = bag.AuthSafe;
+            bool wrongPkcs12Zero = false;
+
+            if (password != null && bag.MacData != null) // check the mac code
                             {
-                                EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue);
-                                PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
-                                    password, wrongPkcs12Zero, eIn);
-                                AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo);
-
-                                //
-                                // set the attributes on the key
-                                //
-                                IDictionary attributes = Platform.CreateHashtable();
-                                AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
-                                string alias = null;
-                                Asn1OctetString localId = null;
+                MacData mData = bag.MacData;
+                DigestInfo dInfo = mData.Mac;
+                AlgorithmIdentifier algId = dInfo.AlgorithmID;
+                byte[] salt = mData.GetSalt();
+                int itCount = mData.IterationCount.IntValue;
 
-                                foreach (Asn1Sequence sq in b.BagAttributes)
-                                {
-                                    DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
-                                    Asn1Set attrSet = (Asn1Set) sq[1];
-                                    Asn1Encodable attr = null;
+                byte[] data = ((Asn1OctetString) info.Content).GetOctets();
+
+                byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data);
+                byte[] dig = dInfo.GetDigest();
 
-                                    if (attrSet.Count > 0)
+                if (!Arrays.ConstantTimeAreEqual(mac, dig))
                                     {
-                                        // TODO We should be adding all attributes in the set
-                                        attr = attrSet[0];
+                    if (password.Length > 0)
+                        throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
 
-                                        // TODO We might want to "merge" attribute sets with
-                                        // the same OID - currently, differing values give an error
-                                        if (attributes.Contains(aOid.Id))
-                                        {
-                                            // OK, but the value has to be the same
-                                            if (!attributes[aOid.Id].Equals(attr))
-                                            {
-                                                throw new IOException("attempt to add existing attribute with different value");
-                                            }
-                                        }
-                                        else
-                                        {
-                                            attributes.Add(aOid.Id, attr);
-                                        }
+                    // Try with incorrect zero length password
+                    mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data);
 
-                                        if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
-                                        {
-                                            alias = ((DerBmpString)attr).GetString();
-                                            // TODO Do these in a separate loop, just collect aliases here
-                                            keys[alias] = pkcs12Key;
+                    if (!Arrays.ConstantTimeAreEqual(mac, dig))
+                        throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
+
+                    wrongPkcs12Zero = true;
                                         }
-                                        else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
-                                        {
-                                            localId = (Asn1OctetString)attr;
                                         }
-                                    }
-                                }
 
-                                // TODO Should we be checking localIds != null here
-                                // as for PkcsObjectIdentifiers.Data version above?
+            keys.Clear();
+            localIds.Clear();
+            unmarkedKeyEntry = null;
 
-                                string name = Hex.ToHexString(localId.GetOctets());
+            IList certBags = Platform.CreateArrayList();
 
-                                if (alias == null)
-                                {
-                                    keys[name] = pkcs12Key;
-                                }
-                                else
-                                {
-                                    // TODO There may have been more than one alias
-                                    localIds[alias] = name;
-                                }
-                            }
-                            else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag))
+            if (info.ContentType.Equals(PkcsObjectIdentifiers.Data))
                             {
-                                PrivateKeyInfo privKeyInfo = PrivateKeyInfo.GetInstance(b.BagValue);
-                                AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo);
-
-                                //
-                                // set the attributes on the key
-                                //
-                                string alias = null;
-                                Asn1OctetString localId = null;
-                                IDictionary attributes = Platform.CreateHashtable();
-                                AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
+                byte[] octs = ((Asn1OctetString)info.Content).GetOctets();
+                AuthenticatedSafe authSafe = new AuthenticatedSafe(
+                    (Asn1Sequence) Asn1OctetString.FromByteArray(octs));
+                ContentInfo[] cis = authSafe.GetContentInfo();
 
-                                foreach (Asn1Sequence sq in b.BagAttributes)
+                foreach (ContentInfo ci in cis)
                                 {
-                                    DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]);
-                                    Asn1Set attrSet = Asn1Set.GetInstance(sq[1]);
-                                    Asn1Encodable attr = null;
+                    DerObjectIdentifier oid = ci.ContentType;
 
-                                    if (attrSet.Count > 0)
+                    byte[] octets = null;
+                    if (oid.Equals(PkcsObjectIdentifiers.Data))
                                     {
-                                        // TODO We should be adding all attributes in the set
-                                        attr = attrSet[0];
-
-                                        // TODO We might want to "merge" attribute sets with
-                                        // the same OID - currently, differing values give an error
-                                        if (attributes.Contains(aOid.Id))
+                        octets = ((Asn1OctetString)ci.Content).GetOctets();
+                    }
+                    else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData))
                                         {
-                                            // OK, but the value has to be the same
-                                            if (!attributes[aOid.Id].Equals(attr))
+                        if (password != null)
                                             {
-                                                throw new IOException("attempt to add existing attribute with different value");
+                            EncryptedData d = EncryptedData.GetInstance(ci.Content);
+                            octets = CryptPbeData(false, d.EncryptionAlgorithm,
+                                password, wrongPkcs12Zero, d.Content.GetOctets());
                                             }
                                         }
                                         else
                                         {
-                                            attributes.Add(aOid.Id, attr);
+                        // TODO Other data types
                                         }
 
-                                        if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
+                    if (octets != null)
                                         {
-                                            alias = ((DerBmpString)attr).GetString();
-                                            // TODO Do these in a separate loop, just collect aliases here
-                                            keys[alias] = pkcs12Key;
-                                        }
-                                        else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
-                                        {
-                                            localId = (Asn1OctetString)attr;
-                                        }
-                                    }
-                                }
-
-                                // TODO Should we be checking localIds != null here
-                                // as for PkcsObjectIdentifiers.Data version above?
+                        Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(octets);
 
-                                string name = Hex.ToHexString(localId.GetOctets());
+                        foreach (Asn1Sequence subSeq in seq)
+                        {
+                            SafeBag b = new SafeBag(subSeq);
 
-                                if (alias == null)
+                            if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
                                 {
-                                    keys[name] = pkcs12Key;
+                                certBags.Add(b);
                                 }
-                                else
+                            else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
                                 {
-                                    // TODO There may have been more than one alias
-                                    localIds[alias] = name;
+                                LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo.GetInstance(b.BagValue),
+                                    b.BagAttributes, password, wrongPkcs12Zero);
                                 }
+                            else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag))
+                            {
+                                LoadKeyBag(PrivateKeyInfo.GetInstance(b.BagValue), b.BagAttributes);
                             }
                             else
                             {
-                                Debug.WriteLine("extra " + b.BagID);
-                                Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b));
-                            }
+                                // TODO Other bag types
                         }
                     }
-                    else
-                    {
-                        Debug.WriteLine("extra " + oid);
-                        Debug.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content));
                     }
                 }
             }
@@ -434,10 +302,10 @@ namespace Org.BouncyCastle.Pkcs
             chainCerts.Clear();
             keyCerts.Clear();
 
-            foreach (SafeBag b in chain)
+            foreach (SafeBag b in certBags)
             {
-                CertBag cb = new CertBag((Asn1Sequence)b.BagValue);
-                byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets();
+                CertBag certBag = new CertBag((Asn1Sequence)b.BagValue);
+                byte[] octets = ((Asn1OctetString)certBag.CertValue).GetOctets();
                 X509Certificate cert = new X509CertificateParser().ReadCertificate(octets);
 
                 //
@@ -487,21 +355,18 @@ namespace Org.BouncyCastle.Pkcs
                 }
 
                 CertId certId = new CertId(cert.GetPublicKey());
-                X509CertificateEntry pkcs12Cert = new X509CertificateEntry(cert, attributes);
+                X509CertificateEntry certEntry = new X509CertificateEntry(cert, attributes);
 
-                chainCerts[certId] = pkcs12Cert;
+                chainCerts[certId] = certEntry;
 
-                if (unmarkedKey)
+                if (unmarkedKeyEntry != null)
                 {
                     if (keyCerts.Count == 0)
                     {
                         string name = Hex.ToHexString(certId.Id);
 
-                        keyCerts[name] = pkcs12Cert;
-
-                        object temp = keys["unmarked"];
-                        keys.Remove("unmarked");
-                        keys[name] = temp;
+                        keyCerts[name] = certEntry;
+                        keys[name] = unmarkedKeyEntry;
                     }
                 }
                 else
@@ -510,13 +375,13 @@ namespace Org.BouncyCastle.Pkcs
                     {
                         string name = Hex.ToHexString(localId.GetOctets());
 
-                        keyCerts[name] = pkcs12Cert;
+                        keyCerts[name] = certEntry;
                     }
 
                     if (alias != null)
                     {
                         // TODO There may have been more than one alias
-                        certs[alias] = pkcs12Cert;
+                        certs[alias] = certEntry;
                     }
                 }
             }
@@ -842,24 +707,34 @@ namespace Org.BouncyCastle.Pkcs
         {
             if (stream == null)
                 throw new ArgumentNullException("stream");
-            if (password == null)
-                throw new ArgumentNullException("password");
             if (random == null)
                 throw new ArgumentNullException("random");
 
             //
-            // handle the key
+            // handle the keys
             //
-            Asn1EncodableVector keyS = new Asn1EncodableVector();
+            Asn1EncodableVector keyBags = new Asn1EncodableVector();
             foreach (string name in keys.Keys)
             {
                 byte[] kSalt = new byte[SaltSize];
                 random.NextBytes(kSalt);
 
-                AsymmetricKeyEntry privKey = (AsymmetricKeyEntry) keys[name];
-                EncryptedPrivateKeyInfo kInfo =
-                    EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
+                AsymmetricKeyEntry privKey = (AsymmetricKeyEntry)keys[name];
+
+                DerObjectIdentifier bagOid;
+                Asn1Encodable bagData;
+
+                if (password == null)
+                {
+                    bagOid = PkcsObjectIdentifiers.KeyBag;
+                    bagData = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey.Key);
+                }
+                else
+                {
+                    bagOid = PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag;
+                    bagData = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
                     keyAlgorithm, password, kSalt, MinIterations, privKey.Key);
+                }
 
                 Asn1EncodableVector kName = new Asn1EncodableVector();
 
@@ -904,13 +779,11 @@ namespace Org.BouncyCastle.Pkcs
                             new DerSet(subjectKeyID)));
                 }
 
-                SafeBag kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName));
-                keyS.Add(kBag);
+                keyBags.Add(new SafeBag(bagOid, bagData.ToAsn1Object(), new DerSet(kName)));
             }
 
-            byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded();
-
-            BerOctetString keyString = new BerOctetString(derEncodedBytes);
+            byte[] keyBagsEncoding = new DerSequence(keyBags).GetDerEncoded();
+            ContentInfo keysInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(keyBagsEncoding));
 
             //
             // certificate processing
@@ -919,7 +792,7 @@ namespace Org.BouncyCastle.Pkcs
 
             random.NextBytes(cSalt);
 
-            Asn1EncodableVector	certSeq = new Asn1EncodableVector();
+            Asn1EncodableVector	certBags = new Asn1EncodableVector();
             Pkcs12PbeParams		cParams = new Pkcs12PbeParams(cSalt, MinIterations);
             AlgorithmIdentifier	cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object());
             ISet				doneCerts = new HashSet();
@@ -973,10 +846,7 @@ namespace Org.BouncyCastle.Pkcs
                             new DerSet(subjectKeyID)));
                 }
 
-                SafeBag sBag = new SafeBag(
-                    PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));
-
-                certSeq.Add(sBag);
+                certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
 
                 doneCerts.Add(certEntry.Certificate);
             }
@@ -1027,10 +897,7 @@ namespace Org.BouncyCastle.Pkcs
                             new DerSet(new DerBmpString(certId))));
                 }
 
-                SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag,
-                    cBag.ToAsn1Object(), new DerSet(fName));
-
-                certSeq.Add(sBag);
+                certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
 
                 doneCerts.Add(cert.Certificate);
             }
@@ -1063,22 +930,24 @@ namespace Org.BouncyCastle.Pkcs
                             new DerSet(cert[oid])));
                 }
 
-                SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));
-
-                certSeq.Add(sBag);
+                certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
             }
 
-            derEncodedBytes = new DerSequence(certSeq).GetDerEncoded();
-
-            byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes);
+            byte[] certBagsEncoding = new DerSequence(certBags).GetDerEncoded();
 
+            ContentInfo certsInfo;
+            if (password == null)
+            {
+                certsInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(certBagsEncoding));
+            }
+            else
+            {
+                byte[] certBytes = CryptPbeData(true, cAlgId, password, false, certBagsEncoding);
             EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes));
+                certsInfo = new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object());
+            }
 
-            ContentInfo[] info = new ContentInfo[]
-            {
-                new ContentInfo(PkcsObjectIdentifiers.Data, keyString),
-                new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object())
-            };
+            ContentInfo[] info = new ContentInfo[]{ keysInfo, certsInfo };
 
             byte[] data = new AuthenticatedSafe(info).GetEncoded(
                 useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);
@@ -1088,6 +957,9 @@ namespace Org.BouncyCastle.Pkcs
             //
             // create the mac
             //
+            MacData macData = null;
+            if (password != null)
+            {
             byte[] mSalt = new byte[20];
             random.NextBytes(mSalt);
 
@@ -1098,12 +970,13 @@ namespace Org.BouncyCastle.Pkcs
                 OiwObjectIdentifiers.IdSha1, DerNull.Instance);
             DigestInfo dInfo = new DigestInfo(algId, mac);
 
-            MacData mData = new MacData(dInfo, mSalt, MinIterations);
+                macData = new MacData(dInfo, mSalt, MinIterations);
+            }
 
             //
             // output the Pfx
             //
-            Pfx pfx = new Pfx(mainInfo, mData);
+            Pfx pfx = new Pfx(mainInfo, macData);
 
             DerOutputStream derOut;
             if (useDerEncoding)