2 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com)
10 // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
11 // See bouncycastle.txt for license.
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
36 using System.Security.Cryptography;
40 using Mono.Security.Cryptography;
42 namespace Mono.Security.X509 {
51 public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1";
52 public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3";
53 public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4";
54 public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6";
55 public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10";
56 public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11";
68 public const string friendlyName = "1.2.840.113549.1.9.20";
69 public const string localKeyId = "1.2.840.113549.1.9.21";
75 internal class SafeBag {
76 private string _bagOID;
79 public SafeBag(string bagOID, ASN1 asn1) {
84 public string BagOID {
85 get { return _bagOID; }
99 class PKCS12 : ICloneable {
101 public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1";
102 public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2";
103 public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3";
104 public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4";
105 public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5";
106 public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6";
109 public const string keyBag = "1.2.840.113549.1.12.10.1.1";
110 public const string pkcs8ShroudedKeyBag = "1.2.840.113549.1.12.10.1.2";
111 public const string certBag = "1.2.840.113549.1.12.10.1.3";
112 public const string crlBag = "1.2.840.113549.1.12.10.1.4";
113 public const string secretBag = "1.2.840.113549.1.12.10.1.5";
114 public const string safeContentsBag = "1.2.840.113549.1.12.10.1.6";
117 public const string x509Certificate = "1.2.840.113549.1.9.22.1";
118 public const string sdsiCertificate = "1.2.840.113549.1.9.22.2";
119 public const string x509Crl = "1.2.840.113549.1.9.23.1";
121 // Adapted from BouncyCastle PKCS12ParametersGenerator.java
122 public class DeriveBytes {
124 public enum Purpose {
130 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 };
131 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 };
132 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 };
134 private string _hashName;
135 private int _iterations;
136 private byte[] _password;
137 private byte[] _salt;
139 public DeriveBytes () {}
141 public string HashName {
142 get { return _hashName; }
143 set { _hashName = value; }
146 public int IterationCount {
147 get { return _iterations; }
148 set { _iterations = value; }
151 public byte[] Password {
152 get { return (byte[]) _password.Clone (); }
155 _password = new byte [0];
157 _password = (byte[]) value.Clone ();
162 get { return (byte[]) _salt.Clone (); }
165 _salt = (byte[]) value.Clone ();
171 private void Adjust (byte[] a, int aOff, byte[] b)
173 int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1;
175 a [aOff + b.Length - 1] = (byte) x;
178 for (int i = b.Length - 2; i >= 0; i--) {
179 x += (b [i] & 0xff) + (a [aOff + i] & 0xff);
180 a [aOff + i] = (byte) x;
185 private byte[] Derive (byte[] diversifier, int n)
187 HashAlgorithm digest = HashAlgorithm.Create (_hashName);
188 int u = (digest.HashSize >> 3); // div 8
190 byte[] dKey = new byte [n];
193 if ((_salt != null) && (_salt.Length != 0)) {
194 S = new byte[v * ((_salt.Length + v - 1) / v)];
196 for (int i = 0; i != S.Length; i++) {
197 S[i] = _salt[i % _salt.Length];
205 if ((_password != null) && (_password.Length != 0)) {
206 P = new byte[v * ((_password.Length + v - 1) / v)];
208 for (int i = 0; i != P.Length; i++) {
209 P[i] = _password[i % _password.Length];
216 byte[] I = new byte [S.Length + P.Length];
218 Buffer.BlockCopy (S, 0, I, 0, S.Length);
219 Buffer.BlockCopy (P, 0, I, S.Length, P.Length);
221 byte[] B = new byte[v];
222 int c = (n + u - 1) / u;
224 for (int i = 1; i <= c; i++) {
225 digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0);
226 digest.TransformFinalBlock (I, 0, I.Length);
227 byte[] A = digest.Hash;
228 digest.Initialize ();
229 for (int j = 1; j != _iterations; j++) {
230 A = digest.ComputeHash (A, 0, A.Length);
233 for (int j = 0; j != B.Length; j++) {
234 B [j] = A [j % A.Length];
237 for (int j = 0; j != I.Length / v; j++) {
238 Adjust (I, j * v, B);
242 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
245 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length);
252 public byte[] DeriveKey (int size)
254 return Derive (keyDiversifier, size);
257 public byte[] DeriveIV (int size)
259 return Derive (ivDiversifier, size);
262 public byte[] DeriveMAC (int size)
264 return Derive (macDiversifier, size);
268 static private int recommendedIterationCount = 2000;
270 //private int _version;
271 private byte[] _password;
272 private ArrayList _keyBags;
273 private X509CertificateCollection _certs;
274 private bool _keyBagsChanged;
275 private bool _certsChanged;
276 private int _iterations;
277 private ArrayList _safeBags;
278 private RandomNumberGenerator _rng;
284 _iterations = recommendedIterationCount;
285 _keyBags = new ArrayList ();
286 _certs = new X509CertificateCollection ();
287 _keyBagsChanged = false;
288 _certsChanged = false;
289 _safeBags = new ArrayList ();
292 public PKCS12 (byte[] data)
301 * version INTEGER {v3(3)}(v3,...),
302 * authSafe ContentInfo,
303 * macData MacData OPTIONAL
306 * MacData ::= SEQUENCE {
308 * macSalt OCTET STRING,
309 * iterations INTEGER DEFAULT 1
310 * -- Note: The default is for historical reasons and its use is deprecated. A higher
311 * -- value, like 1024 is recommended.
314 * SafeContents ::= SEQUENCE OF SafeBag
316 * SafeBag ::= SEQUENCE {
317 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
318 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
319 * bagAttributes SET OF PKCS12Attribute OPTIONAL
322 public PKCS12 (byte[] data, string password)
329 public PKCS12 (byte[] data, byte[] password)
332 _password = password;
336 private void Decode (byte[] data)
338 ASN1 pfx = new ASN1 (data);
340 throw new ArgumentException ("invalid data");
342 ASN1 version = pfx [0];
343 if (version.Tag != 0x02)
344 throw new ArgumentException ("invalid PFX version");
345 //_version = version.Value [0];
347 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
348 if (authSafe.ContentType != PKCS7.Oid.data)
349 throw new ArgumentException ("invalid authenticated safe");
351 // now that we know it's a PKCS#12 file, check the (optional) MAC
352 // before decoding anything else in the file
354 ASN1 macData = pfx [2];
355 if (macData.Tag != 0x30)
356 throw new ArgumentException ("invalid MAC");
358 ASN1 mac = macData [0];
360 throw new ArgumentException ("invalid MAC");
361 ASN1 macAlgorithm = mac [0];
362 string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
363 if (macOid != "1.3.14.3.2.26")
364 throw new ArgumentException ("unsupported HMAC");
365 byte[] macValue = mac [1].Value;
367 ASN1 macSalt = macData [1];
368 if (macSalt.Tag != 0x04)
369 throw new ArgumentException ("missing MAC salt");
371 _iterations = 1; // default value
372 if (macData.Count > 2) {
373 ASN1 iters = macData [2];
374 if (iters.Tag != 0x02)
375 throw new ArgumentException ("invalid MAC iteration");
376 _iterations = ASN1Convert.ToInt32 (iters);
379 byte[] authSafeData = authSafe.Content [0].Value;
380 byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
381 if (!Compare (macValue, calculatedMac))
382 throw new CryptographicException ("Invalid MAC - file may have been tampered!");
385 // we now returns to our original presentation - PFX
386 ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
387 for (int i=0; i < authenticatedSafe.Count; i++) {
388 PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
389 switch (ci.ContentType) {
391 // unencrypted (by PKCS#12)
392 ASN1 safeContents = new ASN1 (ci.Content [0].Value);
393 for (int j=0; j < safeContents.Count; j++) {
394 ASN1 safeBag = safeContents [j];
395 ReadSafeBag (safeBag);
398 case PKCS7.Oid.encryptedData:
399 // password encrypted
400 PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
401 ASN1 decrypted = new ASN1 (Decrypt (ed));
402 for (int j=0; j < decrypted.Count; j++) {
403 ASN1 safeBag = decrypted [j];
404 ReadSafeBag (safeBag);
407 case PKCS7.Oid.envelopedData:
408 // public key encrypted
409 throw new NotImplementedException ("public key encrypted");
411 throw new ArgumentException ("unknown authenticatedSafe");
418 if (_password != null) {
419 Array.Clear (_password, 0, _password.Length);
426 public string Password {
428 if ((value != null) && (value.Length > 0)) {
429 int size = value.Length;
431 if (size < MaximumPasswordLength) {
432 // if not present, add space for a NULL (0x00) character
433 if (value[size - 1] != 0x00)
436 size = MaximumPasswordLength;
438 _password = new byte[(size + nul) << 1]; // double for unicode
439 Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0);
447 public int IterationCount {
448 get { return _iterations; }
449 set { _iterations = value; }
452 public ArrayList Keys {
454 if (_keyBagsChanged) {
456 foreach (SafeBag sb in _safeBags) {
457 if (sb.BagOID.Equals (keyBag)) {
458 ASN1 safeBag = sb.ASN1;
459 ASN1 bagValue = safeBag [1];
460 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
461 byte[] privateKey = pki.PrivateKey;
462 switch (privateKey [0]) {
464 DSAParameters p = new DSAParameters (); // FIXME
465 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
468 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
473 Array.Clear (privateKey, 0, privateKey.Length);
475 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
476 ASN1 safeBag = sb.ASN1;
477 ASN1 bagValue = safeBag [1];
478 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
479 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
480 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
481 byte[] privateKey = pki.PrivateKey;
482 switch (privateKey [0]) {
484 DSAParameters p = new DSAParameters (); // FIXME
485 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
488 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
493 Array.Clear (privateKey, 0, privateKey.Length);
494 Array.Clear (decrypted, 0, decrypted.Length);
497 _keyBagsChanged = false;
499 return ArrayList.ReadOnly(_keyBags);
503 public X509CertificateCollection Certificates {
507 foreach (SafeBag sb in _safeBags) {
508 if (sb.BagOID.Equals (certBag)) {
509 ASN1 safeBag = sb.ASN1;
510 ASN1 bagValue = safeBag [1];
511 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
512 _certs.Add (new X509Certificate (cert.Content [0].Value));
515 _certsChanged = false;
521 internal RandomNumberGenerator RNG {
524 _rng = RandomNumberGenerator.Create ();
531 private bool Compare (byte[] expected, byte[] actual)
533 bool compare = false;
534 if (expected.Length == actual.Length) {
535 for (int i=0; i < expected.Length; i++) {
536 if (expected [i] != actual [i])
544 private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
546 string algorithm = null;
547 int keyLength = 8; // 64 bits (default)
548 int ivLength = 8; // 64 bits (default)
550 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
551 pd.Password = _password;
553 pd.IterationCount = iterationCount;
555 switch (algorithmOid) {
556 case PKCS5.pbeWithMD2AndDESCBC: // no unit test available
560 case PKCS5.pbeWithMD5AndDESCBC: // no unit test available
564 case PKCS5.pbeWithMD2AndRC2CBC: // no unit test available
565 // TODO - RC2-CBC-Parameter (PKCS5)
566 // if missing default to 32 bits !!!
569 keyLength = 4; // default
571 case PKCS5.pbeWithMD5AndRC2CBC: // no unit test available
572 // TODO - RC2-CBC-Parameter (PKCS5)
573 // if missing default to 32 bits !!!
576 keyLength = 4; // default
578 case PKCS5.pbeWithSHA1AndDESCBC: // no unit test available
579 pd.HashName = "SHA1";
582 case PKCS5.pbeWithSHA1AndRC2CBC: // no unit test available
583 // TODO - RC2-CBC-Parameter (PKCS5)
584 // if missing default to 32 bits !!!
585 pd.HashName = "SHA1";
587 keyLength = 4; // default
589 case PKCS12.pbeWithSHAAnd128BitRC4: // no unit test available
590 pd.HashName = "SHA1";
595 case PKCS12.pbeWithSHAAnd40BitRC4: // no unit test available
596 pd.HashName = "SHA1";
601 case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC:
602 pd.HashName = "SHA1";
603 algorithm = "TripleDES";
606 case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC: // no unit test available
607 pd.HashName = "SHA1";
608 algorithm = "TripleDES";
611 case PKCS12.pbeWithSHAAnd128BitRC2CBC: // no unit test available
612 pd.HashName = "SHA1";
616 case PKCS12.pbeWithSHAAnd40BitRC2CBC:
617 pd.HashName = "SHA1";
622 throw new NotSupportedException ("unknown oid " + algorithm);
625 SymmetricAlgorithm sa = SymmetricAlgorithm.Create (algorithm);
626 sa.Key = pd.DeriveKey (keyLength);
627 // IV required only for block ciphers (not stream ciphers)
629 sa.IV = pd.DeriveIV (ivLength);
630 sa.Mode = CipherMode.CBC;
635 public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData)
637 SymmetricAlgorithm sa = null;
638 byte[] result = null;
640 sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
641 ICryptoTransform ct = sa.CreateDecryptor ();
642 result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
651 public byte[] Decrypt (PKCS7.EncryptedData ed)
653 return Decrypt (ed.EncryptionAlgorithm.ContentType,
654 ed.EncryptionAlgorithm.Content [0].Value,
655 ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
656 ed.EncryptedContent);
659 public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data)
661 byte[] result = null;
662 using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
663 ICryptoTransform ct = sa.CreateEncryptor ();
664 result = ct.TransformFinalBlock (data, 0, data.Length);
669 private void AddPrivateKey (PKCS8.PrivateKeyInfo pki)
671 byte[] privateKey = pki.PrivateKey;
672 switch (privateKey [0]) {
674 DSAParameters p = new DSAParameters (); // FIXME
675 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
678 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
681 Array.Clear (privateKey, 0, privateKey.Length);
682 throw new CryptographicException ("Unknown private key format");
684 Array.Clear (privateKey, 0, privateKey.Length);
687 private void ReadSafeBag (ASN1 safeBag)
689 if (safeBag.Tag != 0x30)
690 throw new ArgumentException ("invalid safeBag");
692 ASN1 bagId = safeBag [0];
693 if (bagId.Tag != 0x06)
694 throw new ArgumentException ("invalid safeBag id");
696 ASN1 bagValue = safeBag [1];
697 string oid = ASN1Convert.ToOid (bagId);
701 AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
703 case pkcs8ShroudedKeyBag:
704 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
705 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
706 AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
707 Array.Clear (decrypted, 0, decrypted.Length);
710 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
711 if (cert.ContentType != x509Certificate)
712 throw new NotSupportedException ("unsupport certificate type");
713 X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
722 case safeContentsBag:
723 // TODO - ? recurse ?
726 throw new ArgumentException ("unknown safeBag oid");
729 if (safeBag.Count > 2) {
730 ASN1 bagAttributes = safeBag [2];
731 if (bagAttributes.Tag != 0x31)
732 throw new ArgumentException ("invalid safeBag attributes id");
734 for (int i = 0; i < bagAttributes.Count; i++) {
735 ASN1 pkcs12Attribute = bagAttributes[i];
737 if (pkcs12Attribute.Tag != 0x30)
738 throw new ArgumentException ("invalid PKCS12 attributes id");
740 ASN1 attrId = pkcs12Attribute [0];
741 if (attrId.Tag != 0x06)
742 throw new ArgumentException ("invalid attribute id");
744 string attrOid = ASN1Convert.ToOid (attrId);
746 ASN1 attrValues = pkcs12Attribute[1];
747 for (int j = 0; j < attrValues.Count; j++) {
748 ASN1 attrValue = attrValues[j];
751 case PKCS9.friendlyName:
752 if (attrValue.Tag != 0x1e)
753 throw new ArgumentException ("invalid attribute value id");
755 case PKCS9.localKeyId:
756 if (attrValue.Tag != 0x04)
757 throw new ArgumentException ("invalid attribute value id");
760 // Unknown OID -- don't check Tag
767 _safeBags.Add (new SafeBag(oid, safeBag));
770 private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
772 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
774 pki.Algorithm = "1.2.840.113549.1.1.1";
775 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
777 else if (aa is DSA) {
778 pki.Algorithm = null;
779 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
782 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
784 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
785 epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
786 epki.IterationCount = _iterations;
787 epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
789 ASN1 safeBag = new ASN1 (0x30);
790 safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
791 ASN1 bagValue = new ASN1 (0xA0);
792 bagValue.Add (new ASN1 (epki.GetBytes ()));
793 safeBag.Add (bagValue);
795 if (attributes != null) {
796 ASN1 bagAttributes = new ASN1 (0x31);
797 IDictionaryEnumerator de = attributes.GetEnumerator ();
799 while (de.MoveNext ()) {
800 string oid = (string)de.Key;
802 case PKCS9.friendlyName:
803 ArrayList names = (ArrayList)de.Value;
804 if (names.Count > 0) {
805 ASN1 pkcs12Attribute = new ASN1 (0x30);
806 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
807 ASN1 attrValues = new ASN1 (0x31);
808 foreach (byte[] name in names) {
809 ASN1 attrValue = new ASN1 (0x1e);
810 attrValue.Value = name;
811 attrValues.Add (attrValue);
813 pkcs12Attribute.Add (attrValues);
814 bagAttributes.Add (pkcs12Attribute);
817 case PKCS9.localKeyId:
818 ArrayList keys = (ArrayList)de.Value;
819 if (keys.Count > 0) {
820 ASN1 pkcs12Attribute = new ASN1 (0x30);
821 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
822 ASN1 attrValues = new ASN1 (0x31);
823 foreach (byte[] key in keys) {
824 ASN1 attrValue = new ASN1 (0x04);
825 attrValue.Value = key;
826 attrValues.Add (attrValue);
828 pkcs12Attribute.Add (attrValues);
829 bagAttributes.Add (pkcs12Attribute);
837 if (bagAttributes.Count > 0) {
838 safeBag.Add (bagAttributes);
845 private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
847 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
849 pki.Algorithm = "1.2.840.113549.1.1.1";
850 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
852 else if (aa is DSA) {
853 pki.Algorithm = null;
854 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
857 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
859 ASN1 safeBag = new ASN1 (0x30);
860 safeBag.Add (ASN1Convert.FromOid (keyBag));
861 ASN1 bagValue = new ASN1 (0xA0);
862 bagValue.Add (new ASN1 (pki.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 CertificateSafeBag (X509Certificate x509, IDictionary attributes)
917 ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
919 PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
920 ci.ContentType = x509Certificate;
921 ci.Content.Add (encapsulatedCertificate);
923 ASN1 bagValue = new ASN1 (0xA0);
924 bagValue.Add (ci.ASN1);
926 ASN1 safeBag = new ASN1 (0x30);
927 safeBag.Add (ASN1Convert.FromOid (certBag));
928 safeBag.Add (bagValue);
930 if (attributes != null) {
931 ASN1 bagAttributes = new ASN1 (0x31);
932 IDictionaryEnumerator de = attributes.GetEnumerator ();
934 while (de.MoveNext ()) {
935 string oid = (string)de.Key;
937 case PKCS9.friendlyName:
938 ArrayList names = (ArrayList)de.Value;
939 if (names.Count > 0) {
940 ASN1 pkcs12Attribute = new ASN1 (0x30);
941 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
942 ASN1 attrValues = new ASN1 (0x31);
943 foreach (byte[] name in names) {
944 ASN1 attrValue = new ASN1 (0x1e);
945 attrValue.Value = name;
946 attrValues.Add (attrValue);
948 pkcs12Attribute.Add (attrValues);
949 bagAttributes.Add (pkcs12Attribute);
952 case PKCS9.localKeyId:
953 ArrayList keys = (ArrayList)de.Value;
954 if (keys.Count > 0) {
955 ASN1 pkcs12Attribute = new ASN1 (0x30);
956 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
957 ASN1 attrValues = new ASN1 (0x31);
958 foreach (byte[] key in keys) {
959 ASN1 attrValue = new ASN1 (0x04);
960 attrValue.Value = key;
961 attrValues.Add (attrValue);
963 pkcs12Attribute.Add (attrValues);
964 bagAttributes.Add (pkcs12Attribute);
972 if (bagAttributes.Count > 0) {
973 safeBag.Add (bagAttributes);
980 private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data)
982 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
983 pd.HashName = "SHA1";
984 pd.Password = password;
986 pd.IterationCount = iterations;
988 HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
989 hmac.Key = pd.DeriveMAC (20);
990 return hmac.ComputeHash (data, 0, data.Length);
994 * SafeContents ::= SEQUENCE OF SafeBag
996 * SafeBag ::= SEQUENCE {
997 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
998 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
999 * bagAttributes SET OF PKCS12Attribute OPTIONAL
1002 public byte[] GetBytes ()
1004 // TODO (incomplete)
1005 ASN1 safeBagSequence = new ASN1 (0x30);
1007 // Sync Safe Bag list since X509CertificateCollection may be updated
1008 ArrayList scs = new ArrayList ();
1009 foreach (SafeBag sb in _safeBags) {
1010 if (sb.BagOID.Equals (certBag)) {
1011 ASN1 safeBag = sb.ASN1;
1012 ASN1 bagValue = safeBag [1];
1013 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
1014 scs.Add (new X509Certificate (cert.Content [0].Value));
1018 ArrayList addcerts = new ArrayList ();
1019 ArrayList removecerts = new ArrayList ();
1021 foreach (X509Certificate c in Certificates) {
1024 foreach (X509Certificate lc in scs) {
1025 if (Compare (c.RawData, lc.RawData)) {
1034 foreach (X509Certificate c in scs) {
1037 foreach (X509Certificate lc in Certificates) {
1038 if (Compare (c.RawData, lc.RawData)) {
1044 removecerts.Add (c);
1048 foreach (X509Certificate c in removecerts) {
1049 RemoveCertificate (c);
1052 foreach (X509Certificate c in addcerts) {
1057 if (_safeBags.Count > 0) {
1058 ASN1 certsSafeBag = new ASN1 (0x30);
1059 foreach (SafeBag sb in _safeBags) {
1060 if (sb.BagOID.Equals (certBag)) {
1061 certsSafeBag.Add (sb.ASN1);
1065 if (certsSafeBag.Count > 0) {
1066 byte[] certsSalt = new byte [8];
1067 RNG.GetBytes (certsSalt);
1069 ASN1 seqParams = new ASN1 (0x30);
1070 seqParams.Add (new ASN1 (0x04, certsSalt));
1071 seqParams.Add (ASN1Convert.FromInt32 (_iterations));
1073 ASN1 seqPbe = new ASN1 (0x30);
1074 seqPbe.Add (ASN1Convert.FromOid (pbeWithSHAAnd3KeyTripleDESCBC));
1075 seqPbe.Add (seqParams);
1077 byte[] encrypted = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, certsSalt, _iterations, certsSafeBag.GetBytes ());
1078 ASN1 encryptedCerts = new ASN1 (0x80, encrypted);
1080 ASN1 seq = new ASN1 (0x30);
1081 seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
1083 seq.Add (encryptedCerts);
1085 ASN1 certsVersion = new ASN1 (0x02, new byte [1] { 0x00 });
1086 ASN1 encData = new ASN1 (0x30);
1087 encData.Add (certsVersion);
1090 ASN1 certsContent = new ASN1 (0xA0);
1091 certsContent.Add (encData);
1093 PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
1094 bag.Content = certsContent;
1095 safeBagSequence.Add (bag.ASN1);
1099 if (_safeBags.Count > 0) {
1100 ASN1 safeContents = new ASN1 (0x30);
1101 foreach (SafeBag sb in _safeBags) {
1102 if (sb.BagOID.Equals (keyBag) ||
1103 sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1104 safeContents.Add (sb.ASN1);
1107 if (safeContents.Count > 0) {
1108 ASN1 content = new ASN1 (0xA0);
1109 content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
1111 PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
1112 keyBag.Content = content;
1113 safeBagSequence.Add (keyBag.ASN1);
1118 ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
1119 ASN1 ci = new ASN1 (0xA0);
1120 ci.Add (encapsulates);
1121 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
1122 authSafe.Content = ci;
1124 ASN1 macData = new ASN1 (0x30);
1125 if (_password != null) {
1126 // only for password based encryption
1127 byte[] salt = new byte [20];
1128 RNG.GetBytes (salt);
1129 byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
1130 ASN1 oidSeq = new ASN1 (0x30);
1131 oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1
1132 oidSeq.Add (new ASN1 (0x05));
1134 ASN1 mac = new ASN1 (0x30);
1136 mac.Add (new ASN1 (0x04, macValue));
1139 macData.Add (new ASN1 (0x04, salt));
1140 macData.Add (ASN1Convert.FromInt32 (_iterations));
1143 ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
1145 ASN1 pfx = new ASN1 (0x30);
1147 pfx.Add (authSafe.ASN1);
1148 if (macData.Count > 0) {
1149 // only for password based encryption
1153 return pfx.GetBytes ();
1156 public void AddCertificate (X509Certificate cert)
1158 AddCertificate (cert, null);
1161 public void AddCertificate (X509Certificate cert, IDictionary attributes)
1165 for (int i = 0; !found && i < _safeBags.Count; i++) {
1166 SafeBag sb = (SafeBag)_safeBags [i];
1168 if (sb.BagOID.Equals (certBag)) {
1169 ASN1 safeBag = sb.ASN1;
1170 ASN1 bagValue = safeBag [1];
1171 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1172 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1173 if (Compare (cert.RawData, c.RawData)) {
1180 _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
1181 _certsChanged = true;
1185 public void RemoveCertificate (X509Certificate cert)
1187 RemoveCertificate (cert, null);
1190 public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
1194 for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
1195 SafeBag sb = (SafeBag)_safeBags [i];
1197 if (sb.BagOID.Equals (certBag)) {
1198 ASN1 safeBag = sb.ASN1;
1199 ASN1 bagValue = safeBag [1];
1200 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1201 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1202 if (Compare (cert.RawData, c.RawData)) {
1203 if (attrs != null) {
1204 if (safeBag.Count == 3) {
1205 ASN1 bagAttributes = safeBag [2];
1206 int bagAttributesFound = 0;
1207 for (int j = 0; j < bagAttributes.Count; j++) {
1208 ASN1 pkcs12Attribute = bagAttributes [j];
1209 ASN1 attrId = pkcs12Attribute [0];
1210 string ao = ASN1Convert.ToOid (attrId);
1211 ArrayList dattrValues = (ArrayList)attrs [ao];
1213 if (dattrValues != null) {
1214 ASN1 attrValues = pkcs12Attribute [1];
1216 if (dattrValues.Count == attrValues.Count) {
1217 int attrValuesFound = 0;
1218 for (int k = 0; k < attrValues.Count; k++) {
1219 ASN1 attrValue = attrValues [k];
1220 byte[] value = (byte[])dattrValues [k];
1222 if (Compare (value, attrValue.Value)) {
1223 attrValuesFound += 1;
1226 if (attrValuesFound == attrValues.Count) {
1227 bagAttributesFound += 1;
1232 if (bagAttributesFound == bagAttributes.Count) {
1243 if (certIndex != -1) {
1244 _safeBags.RemoveAt (certIndex);
1245 _certsChanged = true;
1249 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
1252 if (a1.KeySize != a2.KeySize)
1254 // compare public keys - if they match we can assume the private match too
1255 return (a1.ToXmlString (false) == a2.ToXmlString (false));
1258 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1260 AddPkcs8ShroudedKeyBag (aa, null);
1263 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1267 for (int i = 0; !found && i < _safeBags.Count; i++) {
1268 SafeBag sb = (SafeBag)_safeBags [i];
1270 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1271 ASN1 bagValue = sb.ASN1 [1];
1272 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1273 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1274 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1275 byte[] privateKey = pki.PrivateKey;
1277 AsymmetricAlgorithm saa = null;
1278 switch (privateKey [0]) {
1280 DSAParameters p = new DSAParameters (); // FIXME
1281 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1284 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1287 Array.Clear (decrypted, 0, decrypted.Length);
1288 Array.Clear (privateKey, 0, privateKey.Length);
1289 throw new CryptographicException ("Unknown private key format");
1292 Array.Clear (decrypted, 0, decrypted.Length);
1293 Array.Clear (privateKey, 0, privateKey.Length);
1295 if (CompareAsymmetricAlgorithm (aa , saa)) {
1302 _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
1303 _keyBagsChanged = true;
1307 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1311 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1312 SafeBag sb = (SafeBag)_safeBags [i];
1314 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1315 ASN1 bagValue = sb.ASN1 [1];
1316 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1317 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1318 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1319 byte[] privateKey = pki.PrivateKey;
1321 AsymmetricAlgorithm saa = null;
1322 switch (privateKey [0]) {
1324 DSAParameters p = new DSAParameters (); // FIXME
1325 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1328 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1331 Array.Clear (decrypted, 0, decrypted.Length);
1332 Array.Clear (privateKey, 0, privateKey.Length);
1333 throw new CryptographicException ("Unknown private key format");
1336 Array.Clear (decrypted, 0, decrypted.Length);
1337 Array.Clear (privateKey, 0, privateKey.Length);
1339 if (CompareAsymmetricAlgorithm (aa, saa)) {
1345 if (aaIndex != -1) {
1346 _safeBags.RemoveAt (aaIndex);
1347 _keyBagsChanged = true;
1351 public void AddKeyBag (AsymmetricAlgorithm aa)
1353 AddKeyBag (aa, null);
1356 public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1360 for (int i = 0; !found && i < _safeBags.Count; i++) {
1361 SafeBag sb = (SafeBag)_safeBags [i];
1363 if (sb.BagOID.Equals (keyBag)) {
1364 ASN1 bagValue = sb.ASN1 [1];
1365 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1366 byte[] privateKey = pki.PrivateKey;
1368 AsymmetricAlgorithm saa = null;
1369 switch (privateKey [0]) {
1371 DSAParameters p = new DSAParameters (); // FIXME
1372 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1375 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1378 Array.Clear (privateKey, 0, privateKey.Length);
1379 throw new CryptographicException ("Unknown private key format");
1382 Array.Clear (privateKey, 0, privateKey.Length);
1384 if (CompareAsymmetricAlgorithm (aa, saa)) {
1391 _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
1392 _keyBagsChanged = true;
1396 public void RemoveKeyBag (AsymmetricAlgorithm aa)
1400 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1401 SafeBag sb = (SafeBag)_safeBags [i];
1403 if (sb.BagOID.Equals (keyBag)) {
1404 ASN1 bagValue = sb.ASN1 [1];
1405 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1406 byte[] privateKey = pki.PrivateKey;
1408 AsymmetricAlgorithm saa = null;
1409 switch (privateKey [0]) {
1411 DSAParameters p = new DSAParameters (); // FIXME
1412 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1415 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1418 Array.Clear (privateKey, 0, privateKey.Length);
1419 throw new CryptographicException ("Unknown private key format");
1422 Array.Clear (privateKey, 0, privateKey.Length);
1424 if (CompareAsymmetricAlgorithm (aa, saa)) {
1430 if (aaIndex != -1) {
1431 _safeBags.RemoveAt (aaIndex);
1432 _keyBagsChanged = true;
1437 public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
1439 foreach (SafeBag sb in _safeBags) {
1440 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1441 ASN1 safeBag = sb.ASN1;
1443 if (safeBag.Count == 3) {
1444 ASN1 bagAttributes = safeBag [2];
1446 int bagAttributesFound = 0;
1447 for (int i = 0; i < bagAttributes.Count; i++) {
1448 ASN1 pkcs12Attribute = bagAttributes [i];
1449 ASN1 attrId = pkcs12Attribute [0];
1450 string ao = ASN1Convert.ToOid (attrId);
1451 ArrayList dattrValues = (ArrayList)attrs [ao];
1453 if (dattrValues != null) {
1454 ASN1 attrValues = pkcs12Attribute [1];
1456 if (dattrValues.Count == attrValues.Count) {
1457 int attrValuesFound = 0;
1458 for (int j = 0; j < attrValues.Count; j++) {
1459 ASN1 attrValue = attrValues [j];
1460 byte[] value = (byte[])dattrValues [j];
1462 if (Compare (value, attrValue.Value)) {
1463 attrValuesFound += 1;
1466 if (attrValuesFound == attrValues.Count) {
1467 bagAttributesFound += 1;
1472 if (bagAttributesFound == bagAttributes.Count) {
1473 ASN1 bagValue = safeBag [1];
1474 AsymmetricAlgorithm aa = null;
1475 if (sb.BagOID.Equals (keyBag)) {
1476 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1477 byte[] privateKey = pki.PrivateKey;
1478 switch (privateKey [0]) {
1480 DSAParameters p = new DSAParameters (); // FIXME
1481 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1484 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1489 Array.Clear (privateKey, 0, privateKey.Length);
1490 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1491 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1492 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1493 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1494 byte[] privateKey = pki.PrivateKey;
1495 switch (privateKey [0]) {
1497 DSAParameters p = new DSAParameters (); // FIXME
1498 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1501 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1506 Array.Clear (privateKey, 0, privateKey.Length);
1507 Array.Clear (decrypted, 0, decrypted.Length);
1518 public X509Certificate GetCertificate (IDictionary attrs)
1520 foreach (SafeBag sb in _safeBags) {
1521 if (sb.BagOID.Equals (certBag)) {
1522 ASN1 safeBag = sb.ASN1;
1524 if (safeBag.Count == 3) {
1525 ASN1 bagAttributes = safeBag [2];
1527 int bagAttributesFound = 0;
1528 for (int i = 0; i < bagAttributes.Count; i++) {
1529 ASN1 pkcs12Attribute = bagAttributes [i];
1530 ASN1 attrId = pkcs12Attribute [0];
1531 string ao = ASN1Convert.ToOid (attrId);
1532 ArrayList dattrValues = (ArrayList)attrs [ao];
1534 if (dattrValues != null) {
1535 ASN1 attrValues = pkcs12Attribute [1];
1537 if (dattrValues.Count == attrValues.Count) {
1538 int attrValuesFound = 0;
1539 for (int j = 0; j < attrValues.Count; j++) {
1540 ASN1 attrValue = attrValues [j];
1541 byte[] value = (byte[])dattrValues [j];
1543 if (Compare (value, attrValue.Value)) {
1544 attrValuesFound += 1;
1547 if (attrValuesFound == attrValues.Count) {
1548 bagAttributesFound += 1;
1553 if (bagAttributesFound == bagAttributes.Count) {
1554 ASN1 bagValue = safeBag [1];
1555 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1556 return new X509Certificate (crt.Content [0].Value);
1565 public IDictionary GetAttributes (AsymmetricAlgorithm aa)
1567 IDictionary result = new Hashtable ();
1569 foreach (SafeBag sb in _safeBags) {
1570 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1571 ASN1 safeBag = sb.ASN1;
1573 ASN1 bagValue = safeBag [1];
1574 AsymmetricAlgorithm saa = null;
1575 if (sb.BagOID.Equals (keyBag)) {
1576 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1577 byte[] privateKey = pki.PrivateKey;
1578 switch (privateKey [0]) {
1580 DSAParameters p = new DSAParameters (); // FIXME
1581 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1584 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1589 Array.Clear (privateKey, 0, privateKey.Length);
1590 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1591 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1592 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1593 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1594 byte[] privateKey = pki.PrivateKey;
1595 switch (privateKey [0]) {
1597 DSAParameters p = new DSAParameters (); // FIXME
1598 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1601 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1606 Array.Clear (privateKey, 0, privateKey.Length);
1607 Array.Clear (decrypted, 0, decrypted.Length);
1610 if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
1611 if (safeBag.Count == 3) {
1612 ASN1 bagAttributes = safeBag [2];
1614 for (int i = 0; i < bagAttributes.Count; i++) {
1615 ASN1 pkcs12Attribute = bagAttributes [i];
1616 ASN1 attrId = pkcs12Attribute [0];
1617 string aOid = ASN1Convert.ToOid (attrId);
1618 ArrayList aValues = new ArrayList ();
1620 ASN1 attrValues = pkcs12Attribute [1];
1622 for (int j = 0; j < attrValues.Count; j++) {
1623 ASN1 attrValue = attrValues [j];
1624 aValues.Add (attrValue.Value);
1626 result.Add (aOid, aValues);
1636 public IDictionary GetAttributes (X509Certificate cert)
1638 IDictionary result = new Hashtable ();
1640 foreach (SafeBag sb in _safeBags) {
1641 if (sb.BagOID.Equals (certBag)) {
1642 ASN1 safeBag = sb.ASN1;
1643 ASN1 bagValue = safeBag [1];
1644 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1645 X509Certificate xc = new X509Certificate (crt.Content [0].Value);
1647 if (Compare (cert.RawData, xc.RawData)) {
1648 if (safeBag.Count == 3) {
1649 ASN1 bagAttributes = safeBag [2];
1651 for (int i = 0; i < bagAttributes.Count; i++) {
1652 ASN1 pkcs12Attribute = bagAttributes [i];
1653 ASN1 attrId = pkcs12Attribute [0];
1655 string aOid = ASN1Convert.ToOid (attrId);
1656 ArrayList aValues = new ArrayList ();
1658 ASN1 attrValues = pkcs12Attribute [1];
1660 for (int j = 0; j < attrValues.Count; j++) {
1661 ASN1 attrValue = attrValues [j];
1662 aValues.Add (attrValue.Value);
1664 result.Add (aOid, aValues);
1674 public void SaveToFile (string filename)
1676 if (filename == null)
1677 throw new ArgumentNullException ("filename");
1679 using (FileStream fs = File.OpenWrite (filename)) {
1680 byte[] data = GetBytes ();
1681 fs.Write (data, 0, data.Length);
1687 public object Clone ()
1689 PKCS12 clone = null;
1690 if (_password != null) {
1691 clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
1693 clone = new PKCS12 (GetBytes ());
1695 clone.IterationCount = this.IterationCount;
1702 public const int CryptoApiPasswordLimit = 32;
1704 static private int password_max_length = Int32.MaxValue;
1706 // static properties
1708 // MS CryptoAPI limits the password to a maximum of 31 characters
1709 // other implementations, like OpenSSL, have no such limitation.
1710 // Setting a maximum value will truncate the password length to
1711 // ensure compatibility with MS's PFXImportCertStore API.
1712 static public int MaximumPasswordLength {
1713 get { return password_max_length; }
1715 if (value < CryptoApiPasswordLimit) {
1716 string msg = Locale.GetText ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit);
1717 throw new ArgumentOutOfRangeException (msg);
1719 password_max_length = value;
1725 static private byte[] LoadFile (string filename)
1728 using (FileStream fs = File.OpenRead (filename)) {
1729 data = new byte [fs.Length];
1730 fs.Read (data, 0, data.Length);
1736 static public PKCS12 LoadFromFile (string filename)
1738 if (filename == null)
1739 throw new ArgumentNullException ("filename");
1741 return new PKCS12 (LoadFile (filename));
1744 static public PKCS12 LoadFromFile (string filename, string password)
1746 if (filename == null)
1747 throw new ArgumentNullException ("filename");
1749 return new PKCS12 (LoadFile (filename), password);