public void Save(
Stream stream,
char[] password,
SecureRandom random)
{
if (stream == null)
throw new ArgumentNullException("stream");
if (random == null)
throw new ArgumentNullException("random");
//
// handle the keys
//
Asn1EncodableVector keyBags = new Asn1EncodableVector();
foreach (string name in keys.Keys)
{
byte[] kSalt = new byte[SaltSize];
random.NextBytes(kSalt);
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();
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)));
}
keyBags.Add(new SafeBag(bagOid, bagData.ToAsn1Object(), new DerSet(kName)));
}
byte[] keyBagsEncoding = new DerSequence(keyBags).GetDerEncoded();
ContentInfo keysInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(keyBagsEncoding));
//
// certificate processing
//
byte[] cSalt = new byte[SaltSize];
random.NextBytes(cSalt);
Asn1EncodableVector certBags = new Asn1EncodableVector();
Pkcs12PbeParams cParams = new Pkcs12PbeParams(cSalt, MinIterations);
AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object());
ISet doneCerts = new HashSet();
foreach (string name in keys.Keys)
{
X509CertificateEntry certEntry = GetCertificate(name);
CertBag cBag = new CertBag(
PkcsObjectIdentifiers.X509Certificate,
new DerOctetString(certEntry.Certificate.GetEncoded()));
Asn1EncodableVector 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)));
}
certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
doneCerts.Add(certEntry.Certificate);
}
foreach (string certId in certs.Keys)
{
X509CertificateEntry cert = (X509CertificateEntry)certs[certId];
if (keys[certId] != null)
continue;
CertBag cBag = new CertBag(
PkcsObjectIdentifiers.X509Certificate,
new DerOctetString(cert.Certificate.GetEncoded()));
Asn1EncodableVector 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))));
}
certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
doneCerts.Add(cert.Certificate);
}
foreach (CertId certId in chainCerts.Keys)
{
X509CertificateEntry cert = (X509CertificateEntry)chainCerts[certId];
if (doneCerts.Contains(cert.Certificate))
continue;
CertBag cBag = new CertBag(
PkcsObjectIdentifiers.X509Certificate,
new DerOctetString(cert.Certificate.GetEncoded()));
Asn1EncodableVector 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])));
}
certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
}
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[]{ keysInfo, certsInfo };
byte[] data = new AuthenticatedSafe(info).GetEncoded(
useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);
ContentInfo mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data));
//
// create the mac
//
MacData macData = null;
if (password != null)
{
byte[] mSalt = new byte[20];
random.NextBytes(mSalt);
byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1,
mSalt, MinIterations, password, false, data);
AlgorithmIdentifier algId = new AlgorithmIdentifier(
OiwObjectIdentifiers.IdSha1, DerNull.Instance);
DigestInfo dInfo = new DigestInfo(algId, mac);
macData = new MacData(dInfo, mSalt, MinIterations);
}
//
// output the Pfx
//
Pfx pfx = new Pfx(mainInfo, macData);
DerOutputStream derOut;
if (useDerEncoding)
{
derOut = new DerOutputStream(stream);
}
else
{
derOut = new BerOutputStream(stream);
}
derOut.WriteObject(pfx);
}