2 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
5 // Sebastien Pouliot <sebastien@xamarin.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com)
9 // Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
11 // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
12 // See bouncycastle.txt for license.
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
37 using System.Security.Cryptography;
41 using Mono.Security.Cryptography;
43 namespace Mono.Security.X509 {
52 public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1";
53 public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3";
54 public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4";
55 public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6";
56 public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10";
57 public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11";
69 public const string friendlyName = "1.2.840.113549.1.9.20";
70 public const string localKeyId = "1.2.840.113549.1.9.21";
76 internal class SafeBag {
77 private string _bagOID;
80 public SafeBag(string bagOID, ASN1 asn1) {
85 public string BagOID {
86 get { return _bagOID; }
100 class PKCS12 : ICloneable {
102 public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1";
103 public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2";
104 public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3";
105 public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4";
106 public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5";
107 public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6";
110 public const string keyBag = "1.2.840.113549.1.12.10.1.1";
111 public const string pkcs8ShroudedKeyBag = "1.2.840.113549.1.12.10.1.2";
112 public const string certBag = "1.2.840.113549.1.12.10.1.3";
113 public const string crlBag = "1.2.840.113549.1.12.10.1.4";
114 public const string secretBag = "1.2.840.113549.1.12.10.1.5";
115 public const string safeContentsBag = "1.2.840.113549.1.12.10.1.6";
118 public const string x509Certificate = "1.2.840.113549.1.9.22.1";
119 public const string sdsiCertificate = "1.2.840.113549.1.9.22.2";
120 public const string x509Crl = "1.2.840.113549.1.9.23.1";
122 // Adapted from BouncyCastle PKCS12ParametersGenerator.java
123 public class DeriveBytes {
125 public enum Purpose {
131 static private byte[] keyDiversifier = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
132 static private byte[] ivDiversifier = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 };
133 static private byte[] macDiversifier = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 };
135 private string _hashName;
136 private int _iterations;
137 private byte[] _password;
138 private byte[] _salt;
140 public DeriveBytes () {}
142 public string HashName {
143 get { return _hashName; }
144 set { _hashName = value; }
147 public int IterationCount {
148 get { return _iterations; }
149 set { _iterations = value; }
152 public byte[] Password {
153 get { return (byte[]) _password.Clone (); }
156 _password = new byte [0];
158 _password = (byte[]) value.Clone ();
163 get { return (byte[]) _salt.Clone (); }
166 _salt = (byte[]) value.Clone ();
172 private void Adjust (byte[] a, int aOff, byte[] b)
174 int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1;
176 a [aOff + b.Length - 1] = (byte) x;
179 for (int i = b.Length - 2; i >= 0; i--) {
180 x += (b [i] & 0xff) + (a [aOff + i] & 0xff);
181 a [aOff + i] = (byte) x;
186 private byte[] Derive (byte[] diversifier, int n)
188 HashAlgorithm digest = PKCS1.CreateFromName (_hashName);
189 int u = (digest.HashSize >> 3); // div 8
191 byte[] dKey = new byte [n];
194 if ((_salt != null) && (_salt.Length != 0)) {
195 S = new byte[v * ((_salt.Length + v - 1) / v)];
197 for (int i = 0; i != S.Length; i++) {
198 S[i] = _salt[i % _salt.Length];
206 if ((_password != null) && (_password.Length != 0)) {
207 P = new byte[v * ((_password.Length + v - 1) / v)];
209 for (int i = 0; i != P.Length; i++) {
210 P[i] = _password[i % _password.Length];
217 byte[] I = new byte [S.Length + P.Length];
219 Buffer.BlockCopy (S, 0, I, 0, S.Length);
220 Buffer.BlockCopy (P, 0, I, S.Length, P.Length);
222 byte[] B = new byte[v];
223 int c = (n + u - 1) / u;
225 for (int i = 1; i <= c; i++) {
226 digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0);
227 digest.TransformFinalBlock (I, 0, I.Length);
228 byte[] A = digest.Hash;
229 digest.Initialize ();
230 for (int j = 1; j != _iterations; j++) {
231 A = digest.ComputeHash (A, 0, A.Length);
234 for (int j = 0; j != B.Length; j++) {
235 B [j] = A [j % A.Length];
238 for (int j = 0; j != I.Length / v; j++) {
239 Adjust (I, j * v, B);
243 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
246 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length);
253 public byte[] DeriveKey (int size)
255 return Derive (keyDiversifier, size);
258 public byte[] DeriveIV (int size)
260 return Derive (ivDiversifier, size);
263 public byte[] DeriveMAC (int size)
265 return Derive (macDiversifier, size);
269 const int recommendedIterationCount = 2000;
271 //private int _version;
272 private byte[] _password;
273 private ArrayList _keyBags;
274 private ArrayList _secretBags;
275 private X509CertificateCollection _certs;
276 private bool _keyBagsChanged;
277 private bool _secretBagsChanged;
278 private bool _certsChanged;
279 private int _iterations;
280 private ArrayList _safeBags;
281 private RandomNumberGenerator _rng;
287 _iterations = recommendedIterationCount;
288 _keyBags = new ArrayList ();
289 _secretBags = new ArrayList ();
290 _certs = new X509CertificateCollection ();
291 _keyBagsChanged = false;
292 _secretBagsChanged = false;
293 _certsChanged = false;
294 _safeBags = new ArrayList ();
297 public PKCS12 (byte[] data)
306 * version INTEGER {v3(3)}(v3,...),
307 * authSafe ContentInfo,
308 * macData MacData OPTIONAL
311 * MacData ::= SEQUENCE {
313 * macSalt OCTET STRING,
314 * iterations INTEGER DEFAULT 1
315 * -- Note: The default is for historical reasons and its use is deprecated. A higher
316 * -- value, like 1024 is recommended.
319 * SafeContents ::= SEQUENCE OF SafeBag
321 * SafeBag ::= SEQUENCE {
322 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
323 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
324 * bagAttributes SET OF PKCS12Attribute OPTIONAL
327 public PKCS12 (byte[] data, string password)
334 public PKCS12 (byte[] data, byte[] password)
337 _password = password;
341 private void Decode (byte[] data)
343 ASN1 pfx = new ASN1 (data);
345 throw new ArgumentException ("invalid data");
347 ASN1 version = pfx [0];
348 if (version.Tag != 0x02)
349 throw new ArgumentException ("invalid PFX version");
350 //_version = version.Value [0];
352 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
353 if (authSafe.ContentType != PKCS7.Oid.data)
354 throw new ArgumentException ("invalid authenticated safe");
356 // now that we know it's a PKCS#12 file, check the (optional) MAC
357 // before decoding anything else in the file
359 ASN1 macData = pfx [2];
360 if (macData.Tag != 0x30)
361 throw new ArgumentException ("invalid MAC");
363 ASN1 mac = macData [0];
365 throw new ArgumentException ("invalid MAC");
366 ASN1 macAlgorithm = mac [0];
367 string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
368 if (macOid != "1.3.14.3.2.26")
369 throw new ArgumentException ("unsupported HMAC");
370 byte[] macValue = mac [1].Value;
372 ASN1 macSalt = macData [1];
373 if (macSalt.Tag != 0x04)
374 throw new ArgumentException ("missing MAC salt");
376 _iterations = 1; // default value
377 if (macData.Count > 2) {
378 ASN1 iters = macData [2];
379 if (iters.Tag != 0x02)
380 throw new ArgumentException ("invalid MAC iteration");
381 _iterations = ASN1Convert.ToInt32 (iters);
384 byte[] authSafeData = authSafe.Content [0].Value;
385 byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
386 if (!Compare (macValue, calculatedMac))
387 throw new CryptographicException ("Invalid MAC - file may have been tampered!");
390 // we now returns to our original presentation - PFX
391 ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
392 for (int i=0; i < authenticatedSafe.Count; i++) {
393 PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
394 switch (ci.ContentType) {
396 // unencrypted (by PKCS#12)
397 ASN1 safeContents = new ASN1 (ci.Content [0].Value);
398 for (int j=0; j < safeContents.Count; j++) {
399 ASN1 safeBag = safeContents [j];
400 ReadSafeBag (safeBag);
403 case PKCS7.Oid.encryptedData:
404 // password encrypted
405 PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
406 ASN1 decrypted = new ASN1 (Decrypt (ed));
407 for (int j=0; j < decrypted.Count; j++) {
408 ASN1 safeBag = decrypted [j];
409 ReadSafeBag (safeBag);
412 case PKCS7.Oid.envelopedData:
413 // public key encrypted
414 throw new NotImplementedException ("public key encrypted");
416 throw new ArgumentException ("unknown authenticatedSafe");
423 if (_password != null) {
424 Array.Clear (_password, 0, _password.Length);
431 public string Password {
434 if (value.Length > 0) {
435 int size = value.Length;
437 if (size < MaximumPasswordLength) {
438 // if not present, add space for a NULL (0x00) character
439 if (value[size - 1] != 0x00)
442 size = MaximumPasswordLength;
444 _password = new byte[(size + nul) << 1]; // double for unicode
445 Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0);
447 // double-byte (Unicode) NULL (0x00) - see bug #79617
448 _password = new byte[2];
457 public int IterationCount {
458 get { return _iterations; }
459 set { _iterations = value; }
462 public ArrayList Keys {
464 if (_keyBagsChanged) {
466 foreach (SafeBag sb in _safeBags) {
467 if (sb.BagOID.Equals (keyBag)) {
468 ASN1 safeBag = sb.ASN1;
469 ASN1 bagValue = safeBag [1];
470 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
471 byte[] privateKey = pki.PrivateKey;
472 switch (privateKey [0]) {
474 DSAParameters p = new DSAParameters (); // FIXME
475 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
478 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
483 Array.Clear (privateKey, 0, privateKey.Length);
485 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
486 ASN1 safeBag = sb.ASN1;
487 ASN1 bagValue = safeBag [1];
488 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
489 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
490 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
491 byte[] privateKey = pki.PrivateKey;
492 switch (privateKey [0]) {
494 DSAParameters p = new DSAParameters (); // FIXME
495 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
498 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
503 Array.Clear (privateKey, 0, privateKey.Length);
504 Array.Clear (decrypted, 0, decrypted.Length);
507 _keyBagsChanged = false;
509 return ArrayList.ReadOnly(_keyBags);
513 public ArrayList Secrets {
515 if (_secretBagsChanged) {
516 _secretBags.Clear ();
517 foreach (SafeBag sb in _safeBags) {
518 if (sb.BagOID.Equals (secretBag)) {
519 ASN1 safeBag = sb.ASN1;
520 ASN1 bagValue = safeBag [1];
521 byte[] secret = bagValue.Value;
522 _secretBags.Add(secret);
525 _secretBagsChanged = false;
527 return ArrayList.ReadOnly(_secretBags);
531 public X509CertificateCollection Certificates {
535 foreach (SafeBag sb in _safeBags) {
536 if (sb.BagOID.Equals (certBag)) {
537 ASN1 safeBag = sb.ASN1;
538 ASN1 bagValue = safeBag [1];
539 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
540 _certs.Add (new X509Certificate (cert.Content [0].Value));
543 _certsChanged = false;
549 internal RandomNumberGenerator RNG {
552 _rng = RandomNumberGenerator.Create ();
559 private bool Compare (byte[] expected, byte[] actual)
561 bool compare = false;
562 if (expected.Length == actual.Length) {
563 for (int i=0; i < expected.Length; i++) {
564 if (expected [i] != actual [i])
572 private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
574 string algorithm = null;
575 int keyLength = 8; // 64 bits (default)
576 int ivLength = 8; // 64 bits (default)
578 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
579 pd.Password = _password;
581 pd.IterationCount = iterationCount;
583 switch (algorithmOid) {
584 case PKCS5.pbeWithMD2AndDESCBC: // no unit test available
588 case PKCS5.pbeWithMD5AndDESCBC: // no unit test available
592 case PKCS5.pbeWithMD2AndRC2CBC: // no unit test available
593 // TODO - RC2-CBC-Parameter (PKCS5)
594 // if missing default to 32 bits !!!
597 keyLength = 4; // default
599 case PKCS5.pbeWithMD5AndRC2CBC: // no unit test available
600 // TODO - RC2-CBC-Parameter (PKCS5)
601 // if missing default to 32 bits !!!
604 keyLength = 4; // default
606 case PKCS5.pbeWithSHA1AndDESCBC: // no unit test available
607 pd.HashName = "SHA1";
610 case PKCS5.pbeWithSHA1AndRC2CBC: // no unit test available
611 // TODO - RC2-CBC-Parameter (PKCS5)
612 // if missing default to 32 bits !!!
613 pd.HashName = "SHA1";
615 keyLength = 4; // default
617 case PKCS12.pbeWithSHAAnd128BitRC4: // no unit test available
618 pd.HashName = "SHA1";
623 case PKCS12.pbeWithSHAAnd40BitRC4: // no unit test available
624 pd.HashName = "SHA1";
629 case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC:
630 pd.HashName = "SHA1";
631 algorithm = "TripleDES";
634 case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC: // no unit test available
635 pd.HashName = "SHA1";
636 algorithm = "TripleDES";
639 case PKCS12.pbeWithSHAAnd128BitRC2CBC: // no unit test available
640 pd.HashName = "SHA1";
644 case PKCS12.pbeWithSHAAnd40BitRC2CBC:
645 pd.HashName = "SHA1";
650 throw new NotSupportedException ("unknown oid " + algorithm);
653 SymmetricAlgorithm sa = null;
654 #if INSIDE_CORLIB && FULL_AOT_RUNTIME
655 // we do not want CryptoConfig to bring the whole crypto stack
656 // in particular Rijndael which is not supported by CommonCrypto
665 sa = TripleDES.Create ();
671 throw new NotSupportedException (algorithm);
674 sa = SymmetricAlgorithm.Create (algorithm);
676 sa.Key = pd.DeriveKey (keyLength);
677 // IV required only for block ciphers (not stream ciphers)
679 sa.IV = pd.DeriveIV (ivLength);
680 sa.Mode = CipherMode.CBC;
685 public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData)
687 SymmetricAlgorithm sa = null;
688 byte[] result = null;
690 sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
691 ICryptoTransform ct = sa.CreateDecryptor ();
692 result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
701 public byte[] Decrypt (PKCS7.EncryptedData ed)
703 return Decrypt (ed.EncryptionAlgorithm.ContentType,
704 ed.EncryptionAlgorithm.Content [0].Value,
705 ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
706 ed.EncryptedContent);
709 public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data)
711 byte[] result = null;
712 using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
713 ICryptoTransform ct = sa.CreateEncryptor ();
714 result = ct.TransformFinalBlock (data, 0, data.Length);
719 private DSAParameters GetExistingParameters (out bool found)
721 foreach (X509Certificate cert in Certificates) {
722 // FIXME: that won't work if parts of the parameters are missing
723 if (cert.KeyAlgorithmParameters != null) {
727 return dsa.ExportParameters (false);
732 return new DSAParameters ();
735 private void AddPrivateKey (PKCS8.PrivateKeyInfo pki)
737 byte[] privateKey = pki.PrivateKey;
738 switch (privateKey [0]) {
741 DSAParameters p = GetExistingParameters (out found);
743 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
747 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
750 Array.Clear (privateKey, 0, privateKey.Length);
751 throw new CryptographicException ("Unknown private key format");
753 Array.Clear (privateKey, 0, privateKey.Length);
756 private void ReadSafeBag (ASN1 safeBag)
758 if (safeBag.Tag != 0x30)
759 throw new ArgumentException ("invalid safeBag");
761 ASN1 bagId = safeBag [0];
762 if (bagId.Tag != 0x06)
763 throw new ArgumentException ("invalid safeBag id");
765 ASN1 bagValue = safeBag [1];
766 string oid = ASN1Convert.ToOid (bagId);
770 AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
772 case pkcs8ShroudedKeyBag:
773 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
774 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
775 AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
776 Array.Clear (decrypted, 0, decrypted.Length);
779 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
780 if (cert.ContentType != x509Certificate)
781 throw new NotSupportedException ("unsupport certificate type");
782 X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
789 byte[] secret = bagValue.Value;
790 _secretBags.Add(secret);
792 case safeContentsBag:
793 // TODO - ? recurse ?
796 throw new ArgumentException ("unknown safeBag oid");
799 if (safeBag.Count > 2) {
800 ASN1 bagAttributes = safeBag [2];
801 if (bagAttributes.Tag != 0x31)
802 throw new ArgumentException ("invalid safeBag attributes id");
804 for (int i = 0; i < bagAttributes.Count; i++) {
805 ASN1 pkcs12Attribute = bagAttributes[i];
807 if (pkcs12Attribute.Tag != 0x30)
808 throw new ArgumentException ("invalid PKCS12 attributes id");
810 ASN1 attrId = pkcs12Attribute [0];
811 if (attrId.Tag != 0x06)
812 throw new ArgumentException ("invalid attribute id");
814 string attrOid = ASN1Convert.ToOid (attrId);
816 ASN1 attrValues = pkcs12Attribute[1];
817 for (int j = 0; j < attrValues.Count; j++) {
818 ASN1 attrValue = attrValues[j];
821 case PKCS9.friendlyName:
822 if (attrValue.Tag != 0x1e)
823 throw new ArgumentException ("invalid attribute value id");
825 case PKCS9.localKeyId:
826 if (attrValue.Tag != 0x04)
827 throw new ArgumentException ("invalid attribute value id");
830 // Unknown OID -- don't check Tag
837 _safeBags.Add (new SafeBag(oid, safeBag));
840 private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
842 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
844 pki.Algorithm = "1.2.840.113549.1.1.1";
845 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
847 else if (aa is DSA) {
848 pki.Algorithm = null;
849 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
852 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
854 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
855 epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
856 epki.IterationCount = _iterations;
857 epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
859 ASN1 safeBag = new ASN1 (0x30);
860 safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
861 ASN1 bagValue = new ASN1 (0xA0);
862 bagValue.Add (new ASN1 (epki.GetBytes ()));
863 safeBag.Add (bagValue);
865 if (attributes != null) {
866 ASN1 bagAttributes = new ASN1 (0x31);
867 IDictionaryEnumerator de = attributes.GetEnumerator ();
869 while (de.MoveNext ()) {
870 string oid = (string)de.Key;
872 case PKCS9.friendlyName:
873 ArrayList names = (ArrayList)de.Value;
874 if (names.Count > 0) {
875 ASN1 pkcs12Attribute = new ASN1 (0x30);
876 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
877 ASN1 attrValues = new ASN1 (0x31);
878 foreach (byte[] name in names) {
879 ASN1 attrValue = new ASN1 (0x1e);
880 attrValue.Value = name;
881 attrValues.Add (attrValue);
883 pkcs12Attribute.Add (attrValues);
884 bagAttributes.Add (pkcs12Attribute);
887 case PKCS9.localKeyId:
888 ArrayList keys = (ArrayList)de.Value;
889 if (keys.Count > 0) {
890 ASN1 pkcs12Attribute = new ASN1 (0x30);
891 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
892 ASN1 attrValues = new ASN1 (0x31);
893 foreach (byte[] key in keys) {
894 ASN1 attrValue = new ASN1 (0x04);
895 attrValue.Value = key;
896 attrValues.Add (attrValue);
898 pkcs12Attribute.Add (attrValues);
899 bagAttributes.Add (pkcs12Attribute);
907 if (bagAttributes.Count > 0) {
908 safeBag.Add (bagAttributes);
915 private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
917 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
919 pki.Algorithm = "1.2.840.113549.1.1.1";
920 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
922 else if (aa is DSA) {
923 pki.Algorithm = null;
924 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
927 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
929 ASN1 safeBag = new ASN1 (0x30);
930 safeBag.Add (ASN1Convert.FromOid (keyBag));
931 ASN1 bagValue = new ASN1 (0xA0);
932 bagValue.Add (new ASN1 (pki.GetBytes ()));
933 safeBag.Add (bagValue);
935 if (attributes != null) {
936 ASN1 bagAttributes = new ASN1 (0x31);
937 IDictionaryEnumerator de = attributes.GetEnumerator ();
939 while (de.MoveNext ()) {
940 string oid = (string)de.Key;
942 case PKCS9.friendlyName:
943 ArrayList names = (ArrayList)de.Value;
944 if (names.Count > 0) {
945 ASN1 pkcs12Attribute = new ASN1 (0x30);
946 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
947 ASN1 attrValues = new ASN1 (0x31);
948 foreach (byte[] name in names) {
949 ASN1 attrValue = new ASN1 (0x1e);
950 attrValue.Value = name;
951 attrValues.Add (attrValue);
953 pkcs12Attribute.Add (attrValues);
954 bagAttributes.Add (pkcs12Attribute);
957 case PKCS9.localKeyId:
958 ArrayList keys = (ArrayList)de.Value;
959 if (keys.Count > 0) {
960 ASN1 pkcs12Attribute = new ASN1 (0x30);
961 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
962 ASN1 attrValues = new ASN1 (0x31);
963 foreach (byte[] key in keys) {
964 ASN1 attrValue = new ASN1 (0x04);
965 attrValue.Value = key;
966 attrValues.Add (attrValue);
968 pkcs12Attribute.Add (attrValues);
969 bagAttributes.Add (pkcs12Attribute);
977 if (bagAttributes.Count > 0) {
978 safeBag.Add (bagAttributes);
985 private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes)
987 ASN1 safeBag = new ASN1 (0x30);
988 safeBag.Add (ASN1Convert.FromOid (secretBag));
989 ASN1 bagValue = new ASN1 (0x80, secret);
990 safeBag.Add (bagValue);
992 if (attributes != null) {
993 ASN1 bagAttributes = new ASN1 (0x31);
994 IDictionaryEnumerator de = attributes.GetEnumerator ();
996 while (de.MoveNext ()) {
997 string oid = (string)de.Key;
999 case PKCS9.friendlyName:
1000 ArrayList names = (ArrayList)de.Value;
1001 if (names.Count > 0) {
1002 ASN1 pkcs12Attribute = new ASN1 (0x30);
1003 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
1004 ASN1 attrValues = new ASN1 (0x31);
1005 foreach (byte[] name in names) {
1006 ASN1 attrValue = new ASN1 (0x1e);
1007 attrValue.Value = name;
1008 attrValues.Add (attrValue);
1010 pkcs12Attribute.Add (attrValues);
1011 bagAttributes.Add (pkcs12Attribute);
1014 case PKCS9.localKeyId:
1015 ArrayList keys = (ArrayList)de.Value;
1016 if (keys.Count > 0) {
1017 ASN1 pkcs12Attribute = new ASN1 (0x30);
1018 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
1019 ASN1 attrValues = new ASN1 (0x31);
1020 foreach (byte[] key in keys) {
1021 ASN1 attrValue = new ASN1 (0x04);
1022 attrValue.Value = key;
1023 attrValues.Add (attrValue);
1025 pkcs12Attribute.Add (attrValues);
1026 bagAttributes.Add (pkcs12Attribute);
1034 if (bagAttributes.Count > 0) {
1035 safeBag.Add (bagAttributes);
1042 private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes)
1044 ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
1046 PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
1047 ci.ContentType = x509Certificate;
1048 ci.Content.Add (encapsulatedCertificate);
1050 ASN1 bagValue = new ASN1 (0xA0);
1051 bagValue.Add (ci.ASN1);
1053 ASN1 safeBag = new ASN1 (0x30);
1054 safeBag.Add (ASN1Convert.FromOid (certBag));
1055 safeBag.Add (bagValue);
1057 if (attributes != null) {
1058 ASN1 bagAttributes = new ASN1 (0x31);
1059 IDictionaryEnumerator de = attributes.GetEnumerator ();
1061 while (de.MoveNext ()) {
1062 string oid = (string)de.Key;
1064 case PKCS9.friendlyName:
1065 ArrayList names = (ArrayList)de.Value;
1066 if (names.Count > 0) {
1067 ASN1 pkcs12Attribute = new ASN1 (0x30);
1068 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
1069 ASN1 attrValues = new ASN1 (0x31);
1070 foreach (byte[] name in names) {
1071 ASN1 attrValue = new ASN1 (0x1e);
1072 attrValue.Value = name;
1073 attrValues.Add (attrValue);
1075 pkcs12Attribute.Add (attrValues);
1076 bagAttributes.Add (pkcs12Attribute);
1079 case PKCS9.localKeyId:
1080 ArrayList keys = (ArrayList)de.Value;
1081 if (keys.Count > 0) {
1082 ASN1 pkcs12Attribute = new ASN1 (0x30);
1083 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
1084 ASN1 attrValues = new ASN1 (0x31);
1085 foreach (byte[] key in keys) {
1086 ASN1 attrValue = new ASN1 (0x04);
1087 attrValue.Value = key;
1088 attrValues.Add (attrValue);
1090 pkcs12Attribute.Add (attrValues);
1091 bagAttributes.Add (pkcs12Attribute);
1099 if (bagAttributes.Count > 0) {
1100 safeBag.Add (bagAttributes);
1107 private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data)
1109 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
1110 pd.HashName = "SHA1";
1111 pd.Password = password;
1113 pd.IterationCount = iterations;
1115 HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
1116 hmac.Key = pd.DeriveMAC (20);
1117 return hmac.ComputeHash (data, 0, data.Length);
1121 * SafeContents ::= SEQUENCE OF SafeBag
1123 * SafeBag ::= SEQUENCE {
1124 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
1125 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
1126 * bagAttributes SET OF PKCS12Attribute OPTIONAL
1129 public byte[] GetBytes ()
1131 // TODO (incomplete)
1132 ASN1 safeBagSequence = new ASN1 (0x30);
1134 // Sync Safe Bag list since X509CertificateCollection may be updated
1135 ArrayList scs = new ArrayList ();
1136 foreach (SafeBag sb in _safeBags) {
1137 if (sb.BagOID.Equals (certBag)) {
1138 ASN1 safeBag = sb.ASN1;
1139 ASN1 bagValue = safeBag [1];
1140 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
1141 scs.Add (new X509Certificate (cert.Content [0].Value));
1145 ArrayList addcerts = new ArrayList ();
1146 ArrayList removecerts = new ArrayList ();
1148 foreach (X509Certificate c in Certificates) {
1151 foreach (X509Certificate lc in scs) {
1152 if (Compare (c.RawData, lc.RawData)) {
1161 foreach (X509Certificate c in scs) {
1164 foreach (X509Certificate lc in Certificates) {
1165 if (Compare (c.RawData, lc.RawData)) {
1171 removecerts.Add (c);
1175 foreach (X509Certificate c in removecerts) {
1176 RemoveCertificate (c);
1179 foreach (X509Certificate c in addcerts) {
1184 if (_safeBags.Count > 0) {
1185 ASN1 certsSafeBag = new ASN1 (0x30);
1186 foreach (SafeBag sb in _safeBags) {
1187 if (sb.BagOID.Equals (certBag)) {
1188 certsSafeBag.Add (sb.ASN1);
1192 if (certsSafeBag.Count > 0) {
1193 PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
1194 safeBagSequence.Add (contentInfo.ASN1);
1198 if (_safeBags.Count > 0) {
1199 ASN1 safeContents = new ASN1 (0x30);
1200 foreach (SafeBag sb in _safeBags) {
1201 if (sb.BagOID.Equals (keyBag) ||
1202 sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1203 safeContents.Add (sb.ASN1);
1206 if (safeContents.Count > 0) {
1207 ASN1 content = new ASN1 (0xA0);
1208 content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
1210 PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
1211 keyBag.Content = content;
1212 safeBagSequence.Add (keyBag.ASN1);
1216 // Doing SecretBags separately in case we want to change their encryption independently.
1217 if (_safeBags.Count > 0) {
1218 ASN1 secretsSafeBag = new ASN1 (0x30);
1219 foreach (SafeBag sb in _safeBags) {
1220 if (sb.BagOID.Equals (secretBag)) {
1221 secretsSafeBag.Add (sb.ASN1);
1225 if (secretsSafeBag.Count > 0) {
1226 PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
1227 safeBagSequence.Add (contentInfo.ASN1);
1232 ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
1233 ASN1 ci = new ASN1 (0xA0);
1234 ci.Add (encapsulates);
1235 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
1236 authSafe.Content = ci;
1238 ASN1 macData = new ASN1 (0x30);
1239 if (_password != null) {
1240 // only for password based encryption
1241 byte[] salt = new byte [20];
1242 RNG.GetBytes (salt);
1243 byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
1244 ASN1 oidSeq = new ASN1 (0x30);
1245 oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1
1246 oidSeq.Add (new ASN1 (0x05));
1248 ASN1 mac = new ASN1 (0x30);
1250 mac.Add (new ASN1 (0x04, macValue));
1253 macData.Add (new ASN1 (0x04, salt));
1254 macData.Add (ASN1Convert.FromInt32 (_iterations));
1257 ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
1259 ASN1 pfx = new ASN1 (0x30);
1261 pfx.Add (authSafe.ASN1);
1262 if (macData.Count > 0) {
1263 // only for password based encryption
1267 return pfx.GetBytes ();
1270 // Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents. Used in GetBytes(), above.
1271 private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid)
1273 byte[] salt = new byte [8];
1274 RNG.GetBytes (salt);
1276 ASN1 seqParams = new ASN1 (0x30);
1277 seqParams.Add (new ASN1 (0x04, salt));
1278 seqParams.Add (ASN1Convert.FromInt32 (_iterations));
1280 ASN1 seqPbe = new ASN1 (0x30);
1281 seqPbe.Add (ASN1Convert.FromOid (algorithmOid));
1282 seqPbe.Add (seqParams);
1284 byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ());
1285 ASN1 encryptedContent = new ASN1 (0x80, encrypted);
1287 ASN1 seq = new ASN1 (0x30);
1288 seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
1290 seq.Add (encryptedContent);
1292 ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 });
1293 ASN1 encData = new ASN1 (0x30);
1294 encData.Add (version);
1297 ASN1 finalContent = new ASN1 (0xA0);
1298 finalContent.Add (encData);
1300 PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
1301 bag.Content = finalContent;
1305 public void AddCertificate (X509Certificate cert)
1307 AddCertificate (cert, null);
1310 public void AddCertificate (X509Certificate cert, IDictionary attributes)
1314 for (int i = 0; !found && i < _safeBags.Count; i++) {
1315 SafeBag sb = (SafeBag)_safeBags [i];
1317 if (sb.BagOID.Equals (certBag)) {
1318 ASN1 safeBag = sb.ASN1;
1319 ASN1 bagValue = safeBag [1];
1320 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1321 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1322 if (Compare (cert.RawData, c.RawData)) {
1329 _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
1330 _certsChanged = true;
1334 public void RemoveCertificate (X509Certificate cert)
1336 RemoveCertificate (cert, null);
1339 public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
1343 for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
1344 SafeBag sb = (SafeBag)_safeBags [i];
1346 if (sb.BagOID.Equals (certBag)) {
1347 ASN1 safeBag = sb.ASN1;
1348 ASN1 bagValue = safeBag [1];
1349 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1350 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1351 if (Compare (cert.RawData, c.RawData)) {
1352 if (attrs != null) {
1353 if (safeBag.Count == 3) {
1354 ASN1 bagAttributes = safeBag [2];
1355 int bagAttributesFound = 0;
1356 for (int j = 0; j < bagAttributes.Count; j++) {
1357 ASN1 pkcs12Attribute = bagAttributes [j];
1358 ASN1 attrId = pkcs12Attribute [0];
1359 string ao = ASN1Convert.ToOid (attrId);
1360 ArrayList dattrValues = (ArrayList)attrs [ao];
1362 if (dattrValues != null) {
1363 ASN1 attrValues = pkcs12Attribute [1];
1365 if (dattrValues.Count == attrValues.Count) {
1366 int attrValuesFound = 0;
1367 for (int k = 0; k < attrValues.Count; k++) {
1368 ASN1 attrValue = attrValues [k];
1369 byte[] value = (byte[])dattrValues [k];
1371 if (Compare (value, attrValue.Value)) {
1372 attrValuesFound += 1;
1375 if (attrValuesFound == attrValues.Count) {
1376 bagAttributesFound += 1;
1381 if (bagAttributesFound == bagAttributes.Count) {
1392 if (certIndex != -1) {
1393 _safeBags.RemoveAt (certIndex);
1394 _certsChanged = true;
1398 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
1401 if (a1.KeySize != a2.KeySize)
1403 // compare public keys - if they match we can assume the private match too
1404 return (a1.ToXmlString (false) == a2.ToXmlString (false));
1407 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1409 AddPkcs8ShroudedKeyBag (aa, null);
1412 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1416 for (int i = 0; !found && i < _safeBags.Count; i++) {
1417 SafeBag sb = (SafeBag)_safeBags [i];
1419 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1420 ASN1 bagValue = sb.ASN1 [1];
1421 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1422 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1423 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1424 byte[] privateKey = pki.PrivateKey;
1426 AsymmetricAlgorithm saa = null;
1427 switch (privateKey [0]) {
1429 DSAParameters p = new DSAParameters (); // FIXME
1430 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1433 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1436 Array.Clear (decrypted, 0, decrypted.Length);
1437 Array.Clear (privateKey, 0, privateKey.Length);
1438 throw new CryptographicException ("Unknown private key format");
1441 Array.Clear (decrypted, 0, decrypted.Length);
1442 Array.Clear (privateKey, 0, privateKey.Length);
1444 if (CompareAsymmetricAlgorithm (aa , saa)) {
1451 _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
1452 _keyBagsChanged = true;
1456 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1460 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1461 SafeBag sb = (SafeBag)_safeBags [i];
1463 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1464 ASN1 bagValue = sb.ASN1 [1];
1465 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1466 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1467 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1468 byte[] privateKey = pki.PrivateKey;
1470 AsymmetricAlgorithm saa = null;
1471 switch (privateKey [0]) {
1473 DSAParameters p = new DSAParameters (); // FIXME
1474 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1477 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1480 Array.Clear (decrypted, 0, decrypted.Length);
1481 Array.Clear (privateKey, 0, privateKey.Length);
1482 throw new CryptographicException ("Unknown private key format");
1485 Array.Clear (decrypted, 0, decrypted.Length);
1486 Array.Clear (privateKey, 0, privateKey.Length);
1488 if (CompareAsymmetricAlgorithm (aa, saa)) {
1494 if (aaIndex != -1) {
1495 _safeBags.RemoveAt (aaIndex);
1496 _keyBagsChanged = true;
1500 public void AddKeyBag (AsymmetricAlgorithm aa)
1502 AddKeyBag (aa, null);
1505 public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1509 for (int i = 0; !found && i < _safeBags.Count; i++) {
1510 SafeBag sb = (SafeBag)_safeBags [i];
1512 if (sb.BagOID.Equals (keyBag)) {
1513 ASN1 bagValue = sb.ASN1 [1];
1514 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1515 byte[] privateKey = pki.PrivateKey;
1517 AsymmetricAlgorithm saa = null;
1518 switch (privateKey [0]) {
1520 DSAParameters p = new DSAParameters (); // FIXME
1521 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1524 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1527 Array.Clear (privateKey, 0, privateKey.Length);
1528 throw new CryptographicException ("Unknown private key format");
1531 Array.Clear (privateKey, 0, privateKey.Length);
1533 if (CompareAsymmetricAlgorithm (aa, saa)) {
1540 _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
1541 _keyBagsChanged = true;
1545 public void RemoveKeyBag (AsymmetricAlgorithm aa)
1549 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1550 SafeBag sb = (SafeBag)_safeBags [i];
1552 if (sb.BagOID.Equals (keyBag)) {
1553 ASN1 bagValue = sb.ASN1 [1];
1554 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1555 byte[] privateKey = pki.PrivateKey;
1557 AsymmetricAlgorithm saa = null;
1558 switch (privateKey [0]) {
1560 DSAParameters p = new DSAParameters (); // FIXME
1561 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1564 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1567 Array.Clear (privateKey, 0, privateKey.Length);
1568 throw new CryptographicException ("Unknown private key format");
1571 Array.Clear (privateKey, 0, privateKey.Length);
1573 if (CompareAsymmetricAlgorithm (aa, saa)) {
1579 if (aaIndex != -1) {
1580 _safeBags.RemoveAt (aaIndex);
1581 _keyBagsChanged = true;
1585 public void AddSecretBag (byte[] secret)
1587 AddSecretBag (secret, null);
1590 public void AddSecretBag (byte[] secret, IDictionary attributes)
1594 for (int i = 0; !found && i < _safeBags.Count; i++) {
1595 SafeBag sb = (SafeBag)_safeBags [i];
1597 if (sb.BagOID.Equals (secretBag)) {
1598 ASN1 bagValue = sb.ASN1 [1];
1599 byte[] ssecret = bagValue.Value;
1601 if (Compare (secret, ssecret)) {
1608 _safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes)));
1609 _secretBagsChanged = true;
1613 public void RemoveSecretBag (byte[] secret)
1617 for (int i = 0; sIndex == -1 && i < _safeBags.Count; i++) {
1618 SafeBag sb = (SafeBag)_safeBags [i];
1620 if (sb.BagOID.Equals (secretBag)) {
1621 ASN1 bagValue = sb.ASN1 [1];
1622 byte[] ssecret = bagValue.Value;
1624 if (Compare (secret, ssecret)) {
1631 _safeBags.RemoveAt (sIndex);
1632 _secretBagsChanged = true;
1636 public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
1638 foreach (SafeBag sb in _safeBags) {
1639 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1640 ASN1 safeBag = sb.ASN1;
1642 if (safeBag.Count == 3) {
1643 ASN1 bagAttributes = safeBag [2];
1645 int bagAttributesFound = 0;
1646 for (int i = 0; i < bagAttributes.Count; i++) {
1647 ASN1 pkcs12Attribute = bagAttributes [i];
1648 ASN1 attrId = pkcs12Attribute [0];
1649 string ao = ASN1Convert.ToOid (attrId);
1650 ArrayList dattrValues = (ArrayList)attrs [ao];
1652 if (dattrValues != null) {
1653 ASN1 attrValues = pkcs12Attribute [1];
1655 if (dattrValues.Count == attrValues.Count) {
1656 int attrValuesFound = 0;
1657 for (int j = 0; j < attrValues.Count; j++) {
1658 ASN1 attrValue = attrValues [j];
1659 byte[] value = (byte[])dattrValues [j];
1661 if (Compare (value, attrValue.Value)) {
1662 attrValuesFound += 1;
1665 if (attrValuesFound == attrValues.Count) {
1666 bagAttributesFound += 1;
1671 if (bagAttributesFound == bagAttributes.Count) {
1672 ASN1 bagValue = safeBag [1];
1673 AsymmetricAlgorithm aa = null;
1674 if (sb.BagOID.Equals (keyBag)) {
1675 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1676 byte[] privateKey = pki.PrivateKey;
1677 switch (privateKey [0]) {
1679 DSAParameters p = new DSAParameters (); // FIXME
1680 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1683 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1688 Array.Clear (privateKey, 0, privateKey.Length);
1689 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1690 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1691 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1692 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1693 byte[] privateKey = pki.PrivateKey;
1694 switch (privateKey [0]) {
1696 DSAParameters p = new DSAParameters (); // FIXME
1697 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1700 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1705 Array.Clear (privateKey, 0, privateKey.Length);
1706 Array.Clear (decrypted, 0, decrypted.Length);
1717 public byte[] GetSecret (IDictionary attrs)
1719 foreach (SafeBag sb in _safeBags) {
1720 if (sb.BagOID.Equals (secretBag)) {
1721 ASN1 safeBag = sb.ASN1;
1723 if (safeBag.Count == 3) {
1724 ASN1 bagAttributes = safeBag [2];
1726 int bagAttributesFound = 0;
1727 for (int i = 0; i < bagAttributes.Count; i++) {
1728 ASN1 pkcs12Attribute = bagAttributes [i];
1729 ASN1 attrId = pkcs12Attribute [0];
1730 string ao = ASN1Convert.ToOid (attrId);
1731 ArrayList dattrValues = (ArrayList)attrs [ao];
1733 if (dattrValues != null) {
1734 ASN1 attrValues = pkcs12Attribute [1];
1736 if (dattrValues.Count == attrValues.Count) {
1737 int attrValuesFound = 0;
1738 for (int j = 0; j < attrValues.Count; j++) {
1739 ASN1 attrValue = attrValues [j];
1740 byte[] value = (byte[])dattrValues [j];
1742 if (Compare (value, attrValue.Value)) {
1743 attrValuesFound += 1;
1746 if (attrValuesFound == attrValues.Count) {
1747 bagAttributesFound += 1;
1752 if (bagAttributesFound == bagAttributes.Count) {
1753 ASN1 bagValue = safeBag [1];
1754 return bagValue.Value;
1763 public X509Certificate GetCertificate (IDictionary attrs)
1765 foreach (SafeBag sb in _safeBags) {
1766 if (sb.BagOID.Equals (certBag)) {
1767 ASN1 safeBag = sb.ASN1;
1769 if (safeBag.Count == 3) {
1770 ASN1 bagAttributes = safeBag [2];
1772 int bagAttributesFound = 0;
1773 for (int i = 0; i < bagAttributes.Count; i++) {
1774 ASN1 pkcs12Attribute = bagAttributes [i];
1775 ASN1 attrId = pkcs12Attribute [0];
1776 string ao = ASN1Convert.ToOid (attrId);
1777 ArrayList dattrValues = (ArrayList)attrs [ao];
1779 if (dattrValues != null) {
1780 ASN1 attrValues = pkcs12Attribute [1];
1782 if (dattrValues.Count == attrValues.Count) {
1783 int attrValuesFound = 0;
1784 for (int j = 0; j < attrValues.Count; j++) {
1785 ASN1 attrValue = attrValues [j];
1786 byte[] value = (byte[])dattrValues [j];
1788 if (Compare (value, attrValue.Value)) {
1789 attrValuesFound += 1;
1792 if (attrValuesFound == attrValues.Count) {
1793 bagAttributesFound += 1;
1798 if (bagAttributesFound == bagAttributes.Count) {
1799 ASN1 bagValue = safeBag [1];
1800 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1801 return new X509Certificate (crt.Content [0].Value);
1810 public IDictionary GetAttributes (AsymmetricAlgorithm aa)
1812 IDictionary result = new Hashtable ();
1814 foreach (SafeBag sb in _safeBags) {
1815 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1816 ASN1 safeBag = sb.ASN1;
1818 ASN1 bagValue = safeBag [1];
1819 AsymmetricAlgorithm saa = null;
1820 if (sb.BagOID.Equals (keyBag)) {
1821 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1822 byte[] privateKey = pki.PrivateKey;
1823 switch (privateKey [0]) {
1825 DSAParameters p = new DSAParameters (); // FIXME
1826 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1829 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1834 Array.Clear (privateKey, 0, privateKey.Length);
1835 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1836 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1837 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1838 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1839 byte[] privateKey = pki.PrivateKey;
1840 switch (privateKey [0]) {
1842 DSAParameters p = new DSAParameters (); // FIXME
1843 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1846 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1851 Array.Clear (privateKey, 0, privateKey.Length);
1852 Array.Clear (decrypted, 0, decrypted.Length);
1855 if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
1856 if (safeBag.Count == 3) {
1857 ASN1 bagAttributes = safeBag [2];
1859 for (int i = 0; i < bagAttributes.Count; i++) {
1860 ASN1 pkcs12Attribute = bagAttributes [i];
1861 ASN1 attrId = pkcs12Attribute [0];
1862 string aOid = ASN1Convert.ToOid (attrId);
1863 ArrayList aValues = new ArrayList ();
1865 ASN1 attrValues = pkcs12Attribute [1];
1867 for (int j = 0; j < attrValues.Count; j++) {
1868 ASN1 attrValue = attrValues [j];
1869 aValues.Add (attrValue.Value);
1871 result.Add (aOid, aValues);
1881 public IDictionary GetAttributes (X509Certificate cert)
1883 IDictionary result = new Hashtable ();
1885 foreach (SafeBag sb in _safeBags) {
1886 if (sb.BagOID.Equals (certBag)) {
1887 ASN1 safeBag = sb.ASN1;
1888 ASN1 bagValue = safeBag [1];
1889 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1890 X509Certificate xc = new X509Certificate (crt.Content [0].Value);
1892 if (Compare (cert.RawData, xc.RawData)) {
1893 if (safeBag.Count == 3) {
1894 ASN1 bagAttributes = safeBag [2];
1896 for (int i = 0; i < bagAttributes.Count; i++) {
1897 ASN1 pkcs12Attribute = bagAttributes [i];
1898 ASN1 attrId = pkcs12Attribute [0];
1900 string aOid = ASN1Convert.ToOid (attrId);
1901 ArrayList aValues = new ArrayList ();
1903 ASN1 attrValues = pkcs12Attribute [1];
1905 for (int j = 0; j < attrValues.Count; j++) {
1906 ASN1 attrValue = attrValues [j];
1907 aValues.Add (attrValue.Value);
1909 result.Add (aOid, aValues);
1919 public void SaveToFile (string filename)
1921 if (filename == null)
1922 throw new ArgumentNullException ("filename");
1924 using (FileStream fs = File.Create (filename)) {
1925 byte[] data = GetBytes ();
1926 fs.Write (data, 0, data.Length);
1930 public object Clone ()
1932 PKCS12 clone = null;
1933 if (_password != null) {
1934 clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
1936 clone = new PKCS12 (GetBytes ());
1938 clone.IterationCount = this.IterationCount;
1945 public const int CryptoApiPasswordLimit = 32;
1947 static private int password_max_length = Int32.MaxValue;
1949 // static properties
1951 // MS CryptoAPI limits the password to a maximum of 31 characters
1952 // other implementations, like OpenSSL, have no such limitation.
1953 // Setting a maximum value will truncate the password length to
1954 // ensure compatibility with MS's PFXImportCertStore API.
1955 static public int MaximumPasswordLength {
1956 get { return password_max_length; }
1958 if (value < CryptoApiPasswordLimit) {
1959 string msg = Locale.GetText ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit);
1960 throw new ArgumentOutOfRangeException (msg);
1962 password_max_length = value;
1968 static private byte[] LoadFile (string filename)
1971 using (FileStream fs = File.OpenRead (filename)) {
1972 data = new byte [fs.Length];
1973 fs.Read (data, 0, data.Length);
1979 static public PKCS12 LoadFromFile (string filename)
1981 if (filename == null)
1982 throw new ArgumentNullException ("filename");
1984 return new PKCS12 (LoadFile (filename));
1987 static public PKCS12 LoadFromFile (string filename, string password)
1989 if (filename == null)
1990 throw new ArgumentNullException ("filename");
1992 return new PKCS12 (LoadFile (filename), password);