public void Save(Stream stream, char[] password, SecureRandom random) { if (stream == null) { throw new ArgumentNullException("stream"); } if (password == null) { throw new ArgumentNullException("password"); } if (random == null) { throw new ArgumentNullException("random"); } // // handle the key // var keyS = new Asn1EncodableVector(); foreach (string name in _keys.Keys) { var kSalt = new byte[SaltSize]; random.NextBytes(kSalt); var privKey = (AsymmetricKeyEntry) _keys[name]; EncryptedPrivateKeyInfo kInfo = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(_keyAlgorithm, password, kSalt, MinIterations, privKey.Key); var kName = new Asn1EncodableVector(); foreach (string oid in privKey.BagAttributeKeys) { Asn1Encodable entry = privKey[oid]; // NB: Ignore any existing FriendlyName if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) { continue; } kName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(entry))); } // // make sure we are using the local alias on store // // NB: We always set the FriendlyName based on 'name' //if (privKey[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) { kName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtFriendlyName, new DerSet(new DerBmpString(name)))); } // // make sure we have a local key-id // if (privKey[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null) { X509CertificateEntry ct = GetCertificate(name); AsymmetricKeyParameter pubKey = ct.Certificate.GetPublicKey(); SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey); kName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, new DerSet(subjectKeyID))); } var kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName)); keyS.Add(kBag); } byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded(); var keyString = new BerOctetString(derEncodedBytes); // // certificate processing // var cSalt = new byte[SaltSize]; random.NextBytes(cSalt); var certSeq = new Asn1EncodableVector(); var cParams = new Pkcs12PbeParams(cSalt, MinIterations); var cAlgId = new AlgorithmIdentifier(_certAlgorithm, cParams.ToAsn1Object()); ISet doneCerts = new HashSet(); foreach (string name in _keys.Keys) { X509CertificateEntry certEntry = GetCertificate(name); var cBag = new CertBag(PkcsObjectIdentifiers.X509Certificate, new DerOctetString(certEntry.Certificate.GetEncoded())); var fName = new Asn1EncodableVector(); foreach (string oid in certEntry.BagAttributeKeys) { Asn1Encodable entry = certEntry[oid]; // NB: Ignore any existing FriendlyName if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) { continue; } fName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(entry))); } // // make sure we are using the local alias on store // // NB: We always set the FriendlyName based on 'name' //if (certEntry[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) { fName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtFriendlyName, new DerSet(new DerBmpString(name)))); } // // make sure we have a local key-id // if (certEntry[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null) { AsymmetricKeyParameter pubKey = certEntry.Certificate.GetPublicKey(); SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey); fName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, new DerSet(subjectKeyID))); } var sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); certSeq.Add(sBag); doneCerts.Add(certEntry.Certificate); } foreach (string certId in _certs.Keys) { var cert = (X509CertificateEntry) _certs[certId]; if (_keys[certId] != null) { continue; } var cBag = new CertBag(PkcsObjectIdentifiers.X509Certificate, new DerOctetString(cert.Certificate.GetEncoded())); var fName = new Asn1EncodableVector(); foreach (string oid in cert.BagAttributeKeys) { // a certificate not immediately linked to a key doesn't require // a localKeyID and will confuse some PKCS12 implementations. // // If we find one, we'll prune it out. if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id)) { continue; } Asn1Encodable entry = cert[oid]; // NB: Ignore any existing FriendlyName if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) { continue; } fName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(entry))); } // // make sure we are using the local alias on store // // NB: We always set the FriendlyName based on 'certId' //if (cert[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) { fName.Add(new DerSequence(PkcsObjectIdentifiers.Pkcs9AtFriendlyName, new DerSet(new DerBmpString(certId)))); } var sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); certSeq.Add(sBag); doneCerts.Add(cert.Certificate); } foreach (CertId certId in _chainCerts.Keys) { var cert = (X509CertificateEntry) _chainCerts[certId]; if (doneCerts.Contains(cert.Certificate)) { continue; } var cBag = new CertBag(PkcsObjectIdentifiers.X509Certificate, new DerOctetString(cert.Certificate.GetEncoded())); var fName = new Asn1EncodableVector(); foreach (string oid in cert.BagAttributeKeys) { // a certificate not immediately linked to a key doesn't require // a localKeyID and will confuse some PKCS12 implementations. // // If we find one, we'll prune it out. if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id)) { continue; } fName.Add(new DerSequence(new DerObjectIdentifier(oid), new DerSet(cert[oid]))); } var sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); certSeq.Add(sBag); } derEncodedBytes = new DerSequence(certSeq).GetDerEncoded(); byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes); var cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes)); ContentInfo[] info = {new ContentInfo(PkcsObjectIdentifiers.Data, keyString), new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object())}; byte[] data = new AuthenticatedSafe(info).GetEncoded(_useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber); var mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data)); // // create the mac // var mSalt = new byte[20]; random.NextBytes(mSalt); byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1, mSalt, MinIterations, password, false, data); var algId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); var dInfo = new DigestInfo(algId, mac); var mData = new MacData(dInfo, mSalt, MinIterations); // // output the Pfx // var pfx = new Pfx(mainInfo, mData); DerOutputStream derOut; if (_useDerEncoding) { derOut = new DerOutputStream(stream); } else { derOut = new BerOutputStream(stream); } derOut.WriteObject(pfx); }
public void Load(Stream input, char[] password) { if (input == null) { throw new ArgumentNullException("input"); } if (password == null) { throw new ArgumentNullException("password"); } var obj = (Asn1Sequence) Asn1Object.FromStream(input); var 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(); var authSafe = new AuthenticatedSafe((Asn1Sequence) Asn1Object.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(); var seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); foreach (Asn1Sequence subSeq in seq) { var 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(); var pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); string alias = null; Asn1OctetString localId = null; if (b.BagAttributes != null) { foreach (Asn1Sequence sq in b.BagAttributes) { var aOid = (DerObjectIdentifier) sq[0]; var attrSet = (Asn1Set) sq[1]; Asn1Encodable attr = null; if (attrSet.Count > 0) { // 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)) { // 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); } if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) { 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; } } } } if (localId != null) { string name = Hex.ToHexString(localId.GetOctets()); if (alias == null) { _keys[name] = pkcs12Key; } else { // TODO There may have been more than one alias _localIds[alias] = name; } } else { unmarkedKey = true; _keys["unmarked"] = pkcs12Key; } } else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) { chain.Add(b); } else { Debug.WriteLine("extra " + b.BagID); Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b)); } } } else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) { EncryptedData d = EncryptedData.GetInstance(ci.Content); byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm, password, wrongPkcs12Zero, d.Content.GetOctets()); var seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); foreach (Asn1Sequence subSeq in seq) { var b = new SafeBag(subSeq); if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) { chain.Add(b); } else 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(); var pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); string alias = null; Asn1OctetString localId = null; foreach (Asn1Sequence sq in b.BagAttributes) { var aOid = (DerObjectIdentifier) sq[0]; var attrSet = (Asn1Set) sq[1]; Asn1Encodable attr = null; if (attrSet.Count > 0) { // 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)) { // 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); } if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) { 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? string name = Hex.ToHexString(localId.GetOctets()); 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)) { 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(); var pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); foreach (Asn1Sequence sq in b.BagAttributes) { var aOid = (DerObjectIdentifier) sq[0]; var attrSet = (Asn1Set) sq[1]; Asn1Encodable attr = null; if (attrSet.Count > 0) { // 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)) { // 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); } if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) { 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? string name = Hex.ToHexString(localId.GetOctets()); if (alias == null) { _keys[name] = pkcs12Key; } else { // TODO There may have been more than one alias _localIds[alias] = name; } } else { Debug.WriteLine("extra " + b.BagID); Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b)); } } } else { Debug.WriteLine("extra " + oid); Debug.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content)); } } } _certs.Clear(); _chainCerts.Clear(); _keyCerts.Clear(); foreach (SafeBag b in chain) { var cb = new CertBag((Asn1Sequence) b.BagValue); byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets(); X509Certificate cert = new X509CertificateParser().ReadCertificate(octets); // // set the attributes // IDictionary attributes = Platform.CreateHashtable(); Asn1OctetString localId = null; string alias = null; if (b.BagAttributes != null) { foreach (Asn1Sequence sq in b.BagAttributes) { var aOid = (DerObjectIdentifier) sq[0]; var attrSet = (Asn1Set) sq[1]; if (attrSet.Count > 0) { // TODO We should be adding all attributes in the set Asn1Encodable 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)) { // 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); } if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) { alias = ((DerBmpString) attr).GetString(); } else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) { localId = (Asn1OctetString) attr; } } } } var certId = new CertId(cert.GetPublicKey()); var pkcs12Cert = new X509CertificateEntry(cert, attributes); _chainCerts[certId] = pkcs12Cert; if (unmarkedKey) { if (_keyCerts.Count == 0) { string name = Hex.ToHexString(certId.Id); _keyCerts[name] = pkcs12Cert; object temp = _keys["unmarked"]; _keys.Remove("unmarked"); _keys[name] = temp; } } else { if (localId != null) { string name = Hex.ToHexString(localId.GetOctets()); _keyCerts[name] = pkcs12Cert; } if (alias != null) { // TODO There may have been more than one alias _certs[alias] = pkcs12Cert; } } } }