disable broken tests on net_4_0
[mcs.git] / class / corlib / Mono.Security.X509 / PKCS12.cs
blob5ecce72b8db96d41dbc1aaa51cdfd990a99cb69f
1 //
2 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com)
9 //
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:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
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.
33 using System;
34 using System.Collections;
35 using System.IO;
36 using System.Security.Cryptography;
37 using System.Text;
39 using Mono.Security;
40 using Mono.Security.Cryptography;
42 namespace Mono.Security.X509 {
44 #if INSIDE_CORLIB
45 internal
46 #else
47 public
48 #endif
49 class PKCS5 {
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";
58 public PKCS5 () {}
61 #if INSIDE_CORLIB
62 internal
63 #else
64 public
65 #endif
66 class PKCS9 {
68 public const string friendlyName = "1.2.840.113549.1.9.20";
69 public const string localKeyId = "1.2.840.113549.1.9.21";
71 public PKCS9 () {}
75 internal class SafeBag {
76 private string _bagOID;
77 private ASN1 _asn1;
79 public SafeBag(string bagOID, ASN1 asn1) {
80 _bagOID = bagOID;
81 _asn1 = asn1;
84 public string BagOID {
85 get { return _bagOID; }
88 public ASN1 ASN1 {
89 get { return _asn1; }
94 #if INSIDE_CORLIB
95 internal
96 #else
97 public
98 #endif
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";
108 // bags
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";
116 // types
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 {
125 Key,
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 (); }
153 set {
154 if (value == null)
155 _password = new byte [0];
156 else
157 _password = (byte[]) value.Clone ();
161 public byte[] Salt {
162 get { return (byte[]) _salt.Clone (); }
163 set {
164 if (value != null)
165 _salt = (byte[]) value.Clone ();
166 else
167 _salt = null;
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;
176 x >>= 8;
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;
181 x >>= 8;
185 private byte[] Derive (byte[] diversifier, int n)
187 HashAlgorithm digest = HashAlgorithm.Create (_hashName);
188 int u = (digest.HashSize >> 3); // div 8
189 int v = 64;
190 byte[] dKey = new byte [n];
192 byte[] S;
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];
200 else {
201 S = new byte[0];
204 byte[] P;
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];
212 else {
213 P = new byte[0];
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);
241 if (i == c) {
242 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
244 else {
245 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length);
249 return dKey;
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 ArrayList _secretBags;
274 private X509CertificateCollection _certs;
275 private bool _keyBagsChanged;
276 private bool _secretBagsChanged;
277 private bool _certsChanged;
278 private int _iterations;
279 private ArrayList _safeBags;
280 private RandomNumberGenerator _rng;
282 // constructors
284 public PKCS12 ()
286 _iterations = recommendedIterationCount;
287 _keyBags = new ArrayList ();
288 _secretBags = new ArrayList ();
289 _certs = new X509CertificateCollection ();
290 _keyBagsChanged = false;
291 _secretBagsChanged = false;
292 _certsChanged = false;
293 _safeBags = new ArrayList ();
296 public PKCS12 (byte[] data)
297 : this ()
299 Password = null;
300 Decode (data);
304 * PFX ::= SEQUENCE {
305 * version INTEGER {v3(3)}(v3,...),
306 * authSafe ContentInfo,
307 * macData MacData OPTIONAL
310 * MacData ::= SEQUENCE {
311 * mac DigestInfo,
312 * macSalt OCTET STRING,
313 * iterations INTEGER DEFAULT 1
314 * -- Note: The default is for historical reasons and its use is deprecated. A higher
315 * -- value, like 1024 is recommended.
318 * SafeContents ::= SEQUENCE OF SafeBag
320 * SafeBag ::= SEQUENCE {
321 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
322 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
323 * bagAttributes SET OF PKCS12Attribute OPTIONAL
326 public PKCS12 (byte[] data, string password)
327 : this ()
329 Password = password;
330 Decode (data);
333 public PKCS12 (byte[] data, byte[] password)
334 : this ()
336 _password = password;
337 Decode (data);
340 private void Decode (byte[] data)
342 ASN1 pfx = new ASN1 (data);
343 if (pfx.Tag != 0x30)
344 throw new ArgumentException ("invalid data");
346 ASN1 version = pfx [0];
347 if (version.Tag != 0x02)
348 throw new ArgumentException ("invalid PFX version");
349 //_version = version.Value [0];
351 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
352 if (authSafe.ContentType != PKCS7.Oid.data)
353 throw new ArgumentException ("invalid authenticated safe");
355 // now that we know it's a PKCS#12 file, check the (optional) MAC
356 // before decoding anything else in the file
357 if (pfx.Count > 2) {
358 ASN1 macData = pfx [2];
359 if (macData.Tag != 0x30)
360 throw new ArgumentException ("invalid MAC");
362 ASN1 mac = macData [0];
363 if (mac.Tag != 0x30)
364 throw new ArgumentException ("invalid MAC");
365 ASN1 macAlgorithm = mac [0];
366 string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
367 if (macOid != "1.3.14.3.2.26")
368 throw new ArgumentException ("unsupported HMAC");
369 byte[] macValue = mac [1].Value;
371 ASN1 macSalt = macData [1];
372 if (macSalt.Tag != 0x04)
373 throw new ArgumentException ("missing MAC salt");
375 _iterations = 1; // default value
376 if (macData.Count > 2) {
377 ASN1 iters = macData [2];
378 if (iters.Tag != 0x02)
379 throw new ArgumentException ("invalid MAC iteration");
380 _iterations = ASN1Convert.ToInt32 (iters);
383 byte[] authSafeData = authSafe.Content [0].Value;
384 byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
385 if (!Compare (macValue, calculatedMac))
386 throw new CryptographicException ("Invalid MAC - file may have been tampered!");
389 // we now returns to our original presentation - PFX
390 ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
391 for (int i=0; i < authenticatedSafe.Count; i++) {
392 PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
393 switch (ci.ContentType) {
394 case PKCS7.Oid.data:
395 // unencrypted (by PKCS#12)
396 ASN1 safeContents = new ASN1 (ci.Content [0].Value);
397 for (int j=0; j < safeContents.Count; j++) {
398 ASN1 safeBag = safeContents [j];
399 ReadSafeBag (safeBag);
401 break;
402 case PKCS7.Oid.encryptedData:
403 // password encrypted
404 PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
405 ASN1 decrypted = new ASN1 (Decrypt (ed));
406 for (int j=0; j < decrypted.Count; j++) {
407 ASN1 safeBag = decrypted [j];
408 ReadSafeBag (safeBag);
410 break;
411 case PKCS7.Oid.envelopedData:
412 // public key encrypted
413 throw new NotImplementedException ("public key encrypted");
414 default:
415 throw new ArgumentException ("unknown authenticatedSafe");
420 ~PKCS12 ()
422 if (_password != null) {
423 Array.Clear (_password, 0, _password.Length);
425 _password = null;
428 // properties
430 public string Password {
431 set {
432 if (value != null) {
433 if (value.Length > 0) {
434 int size = value.Length;
435 int nul = 0;
436 if (size < MaximumPasswordLength) {
437 // if not present, add space for a NULL (0x00) character
438 if (value[size - 1] != 0x00)
439 nul = 1;
440 } else {
441 size = MaximumPasswordLength;
443 _password = new byte[(size + nul) << 1]; // double for unicode
444 Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0);
445 } else {
446 // double-byte (Unicode) NULL (0x00) - see bug #79617
447 _password = new byte[2];
449 } else {
450 // no password
451 _password = null;
456 public int IterationCount {
457 get { return _iterations; }
458 set { _iterations = value; }
461 public ArrayList Keys {
462 get {
463 if (_keyBagsChanged) {
464 _keyBags.Clear ();
465 foreach (SafeBag sb in _safeBags) {
466 if (sb.BagOID.Equals (keyBag)) {
467 ASN1 safeBag = sb.ASN1;
468 ASN1 bagValue = safeBag [1];
469 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
470 byte[] privateKey = pki.PrivateKey;
471 switch (privateKey [0]) {
472 case 0x02:
473 DSAParameters p = new DSAParameters (); // FIXME
474 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
475 break;
476 case 0x30:
477 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
478 break;
479 default:
480 break;
482 Array.Clear (privateKey, 0, privateKey.Length);
484 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
485 ASN1 safeBag = sb.ASN1;
486 ASN1 bagValue = safeBag [1];
487 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
488 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
489 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
490 byte[] privateKey = pki.PrivateKey;
491 switch (privateKey [0]) {
492 case 0x02:
493 DSAParameters p = new DSAParameters (); // FIXME
494 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
495 break;
496 case 0x30:
497 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
498 break;
499 default:
500 break;
502 Array.Clear (privateKey, 0, privateKey.Length);
503 Array.Clear (decrypted, 0, decrypted.Length);
506 _keyBagsChanged = false;
508 return ArrayList.ReadOnly(_keyBags);
512 public ArrayList Secrets {
513 get {
514 if (_secretBagsChanged) {
515 _secretBags.Clear ();
516 foreach (SafeBag sb in _safeBags) {
517 if (sb.BagOID.Equals (secretBag)) {
518 ASN1 safeBag = sb.ASN1;
519 ASN1 bagValue = safeBag [1];
520 byte[] secret = bagValue.Value;
521 _secretBags.Add(secret);
524 _secretBagsChanged = false;
526 return ArrayList.ReadOnly(_secretBags);
530 public X509CertificateCollection Certificates {
531 get {
532 if (_certsChanged) {
533 _certs.Clear ();
534 foreach (SafeBag sb in _safeBags) {
535 if (sb.BagOID.Equals (certBag)) {
536 ASN1 safeBag = sb.ASN1;
537 ASN1 bagValue = safeBag [1];
538 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
539 _certs.Add (new X509Certificate (cert.Content [0].Value));
542 _certsChanged = false;
544 return _certs;
548 internal RandomNumberGenerator RNG {
549 get {
550 if (_rng == null)
551 _rng = RandomNumberGenerator.Create ();
552 return _rng;
556 // private methods
558 private bool Compare (byte[] expected, byte[] actual)
560 bool compare = false;
561 if (expected.Length == actual.Length) {
562 for (int i=0; i < expected.Length; i++) {
563 if (expected [i] != actual [i])
564 return false;
566 compare = true;
568 return compare;
571 private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
573 string algorithm = null;
574 int keyLength = 8; // 64 bits (default)
575 int ivLength = 8; // 64 bits (default)
577 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
578 pd.Password = _password;
579 pd.Salt = salt;
580 pd.IterationCount = iterationCount;
582 switch (algorithmOid) {
583 case PKCS5.pbeWithMD2AndDESCBC: // no unit test available
584 pd.HashName = "MD2";
585 algorithm = "DES";
586 break;
587 case PKCS5.pbeWithMD5AndDESCBC: // no unit test available
588 pd.HashName = "MD5";
589 algorithm = "DES";
590 break;
591 case PKCS5.pbeWithMD2AndRC2CBC: // no unit test available
592 // TODO - RC2-CBC-Parameter (PKCS5)
593 // if missing default to 32 bits !!!
594 pd.HashName = "MD2";
595 algorithm = "RC2";
596 keyLength = 4; // default
597 break;
598 case PKCS5.pbeWithMD5AndRC2CBC: // no unit test available
599 // TODO - RC2-CBC-Parameter (PKCS5)
600 // if missing default to 32 bits !!!
601 pd.HashName = "MD5";
602 algorithm = "RC2";
603 keyLength = 4; // default
604 break;
605 case PKCS5.pbeWithSHA1AndDESCBC: // no unit test available
606 pd.HashName = "SHA1";
607 algorithm = "DES";
608 break;
609 case PKCS5.pbeWithSHA1AndRC2CBC: // no unit test available
610 // TODO - RC2-CBC-Parameter (PKCS5)
611 // if missing default to 32 bits !!!
612 pd.HashName = "SHA1";
613 algorithm = "RC2";
614 keyLength = 4; // default
615 break;
616 case PKCS12.pbeWithSHAAnd128BitRC4: // no unit test available
617 pd.HashName = "SHA1";
618 algorithm = "RC4";
619 keyLength = 16;
620 ivLength = 0; // N/A
621 break;
622 case PKCS12.pbeWithSHAAnd40BitRC4: // no unit test available
623 pd.HashName = "SHA1";
624 algorithm = "RC4";
625 keyLength = 5;
626 ivLength = 0; // N/A
627 break;
628 case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC:
629 pd.HashName = "SHA1";
630 algorithm = "TripleDES";
631 keyLength = 24;
632 break;
633 case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC: // no unit test available
634 pd.HashName = "SHA1";
635 algorithm = "TripleDES";
636 keyLength = 16;
637 break;
638 case PKCS12.pbeWithSHAAnd128BitRC2CBC: // no unit test available
639 pd.HashName = "SHA1";
640 algorithm = "RC2";
641 keyLength = 16;
642 break;
643 case PKCS12.pbeWithSHAAnd40BitRC2CBC:
644 pd.HashName = "SHA1";
645 algorithm = "RC2";
646 keyLength = 5;
647 break;
648 default:
649 throw new NotSupportedException ("unknown oid " + algorithm);
652 SymmetricAlgorithm sa = SymmetricAlgorithm.Create (algorithm);
653 sa.Key = pd.DeriveKey (keyLength);
654 // IV required only for block ciphers (not stream ciphers)
655 if (ivLength > 0) {
656 sa.IV = pd.DeriveIV (ivLength);
657 sa.Mode = CipherMode.CBC;
659 return sa;
662 public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData)
664 SymmetricAlgorithm sa = null;
665 byte[] result = null;
666 try {
667 sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
668 ICryptoTransform ct = sa.CreateDecryptor ();
669 result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
671 finally {
672 if (sa != null)
673 sa.Clear ();
675 return result;
678 public byte[] Decrypt (PKCS7.EncryptedData ed)
680 return Decrypt (ed.EncryptionAlgorithm.ContentType,
681 ed.EncryptionAlgorithm.Content [0].Value,
682 ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
683 ed.EncryptedContent);
686 public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data)
688 byte[] result = null;
689 using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
690 ICryptoTransform ct = sa.CreateEncryptor ();
691 result = ct.TransformFinalBlock (data, 0, data.Length);
693 return result;
696 private DSAParameters GetExistingParameters (out bool found)
698 foreach (X509Certificate cert in Certificates) {
699 // FIXME: that won't work if parts of the parameters are missing
700 if (cert.KeyAlgorithmParameters != null) {
701 DSA dsa = cert.DSA;
702 if (dsa != null) {
703 found = true;
704 return dsa.ExportParameters (false);
708 found = false;
709 return new DSAParameters ();
712 private void AddPrivateKey (PKCS8.PrivateKeyInfo pki)
714 byte[] privateKey = pki.PrivateKey;
715 switch (privateKey [0]) {
716 case 0x02:
717 bool found;
718 DSAParameters p = GetExistingParameters (out found);
719 if (found) {
720 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
722 break;
723 case 0x30:
724 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
725 break;
726 default:
727 Array.Clear (privateKey, 0, privateKey.Length);
728 throw new CryptographicException ("Unknown private key format");
730 Array.Clear (privateKey, 0, privateKey.Length);
733 private void ReadSafeBag (ASN1 safeBag)
735 if (safeBag.Tag != 0x30)
736 throw new ArgumentException ("invalid safeBag");
738 ASN1 bagId = safeBag [0];
739 if (bagId.Tag != 0x06)
740 throw new ArgumentException ("invalid safeBag id");
742 ASN1 bagValue = safeBag [1];
743 string oid = ASN1Convert.ToOid (bagId);
744 switch (oid) {
745 case keyBag:
746 // NEED UNIT TEST
747 AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
748 break;
749 case pkcs8ShroudedKeyBag:
750 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
751 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
752 AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
753 Array.Clear (decrypted, 0, decrypted.Length);
754 break;
755 case certBag:
756 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
757 if (cert.ContentType != x509Certificate)
758 throw new NotSupportedException ("unsupport certificate type");
759 X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
760 _certs.Add (x509);
761 break;
762 case crlBag:
763 // TODO
764 break;
765 case secretBag:
766 byte[] secret = bagValue.Value;
767 _secretBags.Add(secret);
768 break;
769 case safeContentsBag:
770 // TODO - ? recurse ?
771 break;
772 default:
773 throw new ArgumentException ("unknown safeBag oid");
776 if (safeBag.Count > 2) {
777 ASN1 bagAttributes = safeBag [2];
778 if (bagAttributes.Tag != 0x31)
779 throw new ArgumentException ("invalid safeBag attributes id");
781 for (int i = 0; i < bagAttributes.Count; i++) {
782 ASN1 pkcs12Attribute = bagAttributes[i];
784 if (pkcs12Attribute.Tag != 0x30)
785 throw new ArgumentException ("invalid PKCS12 attributes id");
787 ASN1 attrId = pkcs12Attribute [0];
788 if (attrId.Tag != 0x06)
789 throw new ArgumentException ("invalid attribute id");
791 string attrOid = ASN1Convert.ToOid (attrId);
793 ASN1 attrValues = pkcs12Attribute[1];
794 for (int j = 0; j < attrValues.Count; j++) {
795 ASN1 attrValue = attrValues[j];
797 switch (attrOid) {
798 case PKCS9.friendlyName:
799 if (attrValue.Tag != 0x1e)
800 throw new ArgumentException ("invalid attribute value id");
801 break;
802 case PKCS9.localKeyId:
803 if (attrValue.Tag != 0x04)
804 throw new ArgumentException ("invalid attribute value id");
805 break;
806 default:
807 // Unknown OID -- don't check Tag
808 break;
814 _safeBags.Add (new SafeBag(oid, safeBag));
817 private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
819 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
820 if (aa is RSA) {
821 pki.Algorithm = "1.2.840.113549.1.1.1";
822 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
824 else if (aa is DSA) {
825 pki.Algorithm = null;
826 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
828 else
829 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
831 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
832 epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
833 epki.IterationCount = _iterations;
834 epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
836 ASN1 safeBag = new ASN1 (0x30);
837 safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
838 ASN1 bagValue = new ASN1 (0xA0);
839 bagValue.Add (new ASN1 (epki.GetBytes ()));
840 safeBag.Add (bagValue);
842 if (attributes != null) {
843 ASN1 bagAttributes = new ASN1 (0x31);
844 IDictionaryEnumerator de = attributes.GetEnumerator ();
846 while (de.MoveNext ()) {
847 string oid = (string)de.Key;
848 switch (oid) {
849 case PKCS9.friendlyName:
850 ArrayList names = (ArrayList)de.Value;
851 if (names.Count > 0) {
852 ASN1 pkcs12Attribute = new ASN1 (0x30);
853 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
854 ASN1 attrValues = new ASN1 (0x31);
855 foreach (byte[] name in names) {
856 ASN1 attrValue = new ASN1 (0x1e);
857 attrValue.Value = name;
858 attrValues.Add (attrValue);
860 pkcs12Attribute.Add (attrValues);
861 bagAttributes.Add (pkcs12Attribute);
863 break;
864 case PKCS9.localKeyId:
865 ArrayList keys = (ArrayList)de.Value;
866 if (keys.Count > 0) {
867 ASN1 pkcs12Attribute = new ASN1 (0x30);
868 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
869 ASN1 attrValues = new ASN1 (0x31);
870 foreach (byte[] key in keys) {
871 ASN1 attrValue = new ASN1 (0x04);
872 attrValue.Value = key;
873 attrValues.Add (attrValue);
875 pkcs12Attribute.Add (attrValues);
876 bagAttributes.Add (pkcs12Attribute);
878 break;
879 default:
880 break;
884 if (bagAttributes.Count > 0) {
885 safeBag.Add (bagAttributes);
889 return safeBag;
892 private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
894 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
895 if (aa is RSA) {
896 pki.Algorithm = "1.2.840.113549.1.1.1";
897 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
899 else if (aa is DSA) {
900 pki.Algorithm = null;
901 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
903 else
904 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
906 ASN1 safeBag = new ASN1 (0x30);
907 safeBag.Add (ASN1Convert.FromOid (keyBag));
908 ASN1 bagValue = new ASN1 (0xA0);
909 bagValue.Add (new ASN1 (pki.GetBytes ()));
910 safeBag.Add (bagValue);
912 if (attributes != null) {
913 ASN1 bagAttributes = new ASN1 (0x31);
914 IDictionaryEnumerator de = attributes.GetEnumerator ();
916 while (de.MoveNext ()) {
917 string oid = (string)de.Key;
918 switch (oid) {
919 case PKCS9.friendlyName:
920 ArrayList names = (ArrayList)de.Value;
921 if (names.Count > 0) {
922 ASN1 pkcs12Attribute = new ASN1 (0x30);
923 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
924 ASN1 attrValues = new ASN1 (0x31);
925 foreach (byte[] name in names) {
926 ASN1 attrValue = new ASN1 (0x1e);
927 attrValue.Value = name;
928 attrValues.Add (attrValue);
930 pkcs12Attribute.Add (attrValues);
931 bagAttributes.Add (pkcs12Attribute);
933 break;
934 case PKCS9.localKeyId:
935 ArrayList keys = (ArrayList)de.Value;
936 if (keys.Count > 0) {
937 ASN1 pkcs12Attribute = new ASN1 (0x30);
938 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
939 ASN1 attrValues = new ASN1 (0x31);
940 foreach (byte[] key in keys) {
941 ASN1 attrValue = new ASN1 (0x04);
942 attrValue.Value = key;
943 attrValues.Add (attrValue);
945 pkcs12Attribute.Add (attrValues);
946 bagAttributes.Add (pkcs12Attribute);
948 break;
949 default:
950 break;
954 if (bagAttributes.Count > 0) {
955 safeBag.Add (bagAttributes);
959 return safeBag;
962 private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes)
964 ASN1 safeBag = new ASN1 (0x30);
965 safeBag.Add (ASN1Convert.FromOid (secretBag));
966 ASN1 bagValue = new ASN1 (0x80, secret);
967 safeBag.Add (bagValue);
969 if (attributes != null) {
970 ASN1 bagAttributes = new ASN1 (0x31);
971 IDictionaryEnumerator de = attributes.GetEnumerator ();
973 while (de.MoveNext ()) {
974 string oid = (string)de.Key;
975 switch (oid) {
976 case PKCS9.friendlyName:
977 ArrayList names = (ArrayList)de.Value;
978 if (names.Count > 0) {
979 ASN1 pkcs12Attribute = new ASN1 (0x30);
980 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
981 ASN1 attrValues = new ASN1 (0x31);
982 foreach (byte[] name in names) {
983 ASN1 attrValue = new ASN1 (0x1e);
984 attrValue.Value = name;
985 attrValues.Add (attrValue);
987 pkcs12Attribute.Add (attrValues);
988 bagAttributes.Add (pkcs12Attribute);
990 break;
991 case PKCS9.localKeyId:
992 ArrayList keys = (ArrayList)de.Value;
993 if (keys.Count > 0) {
994 ASN1 pkcs12Attribute = new ASN1 (0x30);
995 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
996 ASN1 attrValues = new ASN1 (0x31);
997 foreach (byte[] key in keys) {
998 ASN1 attrValue = new ASN1 (0x04);
999 attrValue.Value = key;
1000 attrValues.Add (attrValue);
1002 pkcs12Attribute.Add (attrValues);
1003 bagAttributes.Add (pkcs12Attribute);
1005 break;
1006 default:
1007 break;
1011 if (bagAttributes.Count > 0) {
1012 safeBag.Add (bagAttributes);
1016 return safeBag;
1019 private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes)
1021 ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
1023 PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
1024 ci.ContentType = x509Certificate;
1025 ci.Content.Add (encapsulatedCertificate);
1027 ASN1 bagValue = new ASN1 (0xA0);
1028 bagValue.Add (ci.ASN1);
1030 ASN1 safeBag = new ASN1 (0x30);
1031 safeBag.Add (ASN1Convert.FromOid (certBag));
1032 safeBag.Add (bagValue);
1034 if (attributes != null) {
1035 ASN1 bagAttributes = new ASN1 (0x31);
1036 IDictionaryEnumerator de = attributes.GetEnumerator ();
1038 while (de.MoveNext ()) {
1039 string oid = (string)de.Key;
1040 switch (oid) {
1041 case PKCS9.friendlyName:
1042 ArrayList names = (ArrayList)de.Value;
1043 if (names.Count > 0) {
1044 ASN1 pkcs12Attribute = new ASN1 (0x30);
1045 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
1046 ASN1 attrValues = new ASN1 (0x31);
1047 foreach (byte[] name in names) {
1048 ASN1 attrValue = new ASN1 (0x1e);
1049 attrValue.Value = name;
1050 attrValues.Add (attrValue);
1052 pkcs12Attribute.Add (attrValues);
1053 bagAttributes.Add (pkcs12Attribute);
1055 break;
1056 case PKCS9.localKeyId:
1057 ArrayList keys = (ArrayList)de.Value;
1058 if (keys.Count > 0) {
1059 ASN1 pkcs12Attribute = new ASN1 (0x30);
1060 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
1061 ASN1 attrValues = new ASN1 (0x31);
1062 foreach (byte[] key in keys) {
1063 ASN1 attrValue = new ASN1 (0x04);
1064 attrValue.Value = key;
1065 attrValues.Add (attrValue);
1067 pkcs12Attribute.Add (attrValues);
1068 bagAttributes.Add (pkcs12Attribute);
1070 break;
1071 default:
1072 break;
1076 if (bagAttributes.Count > 0) {
1077 safeBag.Add (bagAttributes);
1081 return safeBag;
1084 private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data)
1086 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
1087 pd.HashName = "SHA1";
1088 pd.Password = password;
1089 pd.Salt = salt;
1090 pd.IterationCount = iterations;
1092 HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
1093 hmac.Key = pd.DeriveMAC (20);
1094 return hmac.ComputeHash (data, 0, data.Length);
1098 * SafeContents ::= SEQUENCE OF SafeBag
1100 * SafeBag ::= SEQUENCE {
1101 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
1102 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
1103 * bagAttributes SET OF PKCS12Attribute OPTIONAL
1106 public byte[] GetBytes ()
1108 // TODO (incomplete)
1109 ASN1 safeBagSequence = new ASN1 (0x30);
1111 // Sync Safe Bag list since X509CertificateCollection may be updated
1112 ArrayList scs = new ArrayList ();
1113 foreach (SafeBag sb in _safeBags) {
1114 if (sb.BagOID.Equals (certBag)) {
1115 ASN1 safeBag = sb.ASN1;
1116 ASN1 bagValue = safeBag [1];
1117 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
1118 scs.Add (new X509Certificate (cert.Content [0].Value));
1122 ArrayList addcerts = new ArrayList ();
1123 ArrayList removecerts = new ArrayList ();
1125 foreach (X509Certificate c in Certificates) {
1126 bool found = false;
1128 foreach (X509Certificate lc in scs) {
1129 if (Compare (c.RawData, lc.RawData)) {
1130 found = true;
1134 if (!found) {
1135 addcerts.Add (c);
1138 foreach (X509Certificate c in scs) {
1139 bool found = false;
1141 foreach (X509Certificate lc in Certificates) {
1142 if (Compare (c.RawData, lc.RawData)) {
1143 found = true;
1147 if (!found) {
1148 removecerts.Add (c);
1152 foreach (X509Certificate c in removecerts) {
1153 RemoveCertificate (c);
1156 foreach (X509Certificate c in addcerts) {
1157 AddCertificate (c);
1159 // Sync done
1161 if (_safeBags.Count > 0) {
1162 ASN1 certsSafeBag = new ASN1 (0x30);
1163 foreach (SafeBag sb in _safeBags) {
1164 if (sb.BagOID.Equals (certBag)) {
1165 certsSafeBag.Add (sb.ASN1);
1169 if (certsSafeBag.Count > 0) {
1170 PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
1171 safeBagSequence.Add (contentInfo.ASN1);
1175 if (_safeBags.Count > 0) {
1176 ASN1 safeContents = new ASN1 (0x30);
1177 foreach (SafeBag sb in _safeBags) {
1178 if (sb.BagOID.Equals (keyBag) ||
1179 sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1180 safeContents.Add (sb.ASN1);
1183 if (safeContents.Count > 0) {
1184 ASN1 content = new ASN1 (0xA0);
1185 content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
1187 PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
1188 keyBag.Content = content;
1189 safeBagSequence.Add (keyBag.ASN1);
1193 // Doing SecretBags separately in case we want to change their encryption independently.
1194 if (_safeBags.Count > 0) {
1195 ASN1 secretsSafeBag = new ASN1 (0x30);
1196 foreach (SafeBag sb in _safeBags) {
1197 if (sb.BagOID.Equals (secretBag)) {
1198 secretsSafeBag.Add (sb.ASN1);
1202 if (secretsSafeBag.Count > 0) {
1203 PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
1204 safeBagSequence.Add (contentInfo.ASN1);
1209 ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
1210 ASN1 ci = new ASN1 (0xA0);
1211 ci.Add (encapsulates);
1212 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
1213 authSafe.Content = ci;
1215 ASN1 macData = new ASN1 (0x30);
1216 if (_password != null) {
1217 // only for password based encryption
1218 byte[] salt = new byte [20];
1219 RNG.GetBytes (salt);
1220 byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
1221 ASN1 oidSeq = new ASN1 (0x30);
1222 oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1
1223 oidSeq.Add (new ASN1 (0x05));
1225 ASN1 mac = new ASN1 (0x30);
1226 mac.Add (oidSeq);
1227 mac.Add (new ASN1 (0x04, macValue));
1229 macData.Add (mac);
1230 macData.Add (new ASN1 (0x04, salt));
1231 macData.Add (ASN1Convert.FromInt32 (_iterations));
1234 ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
1236 ASN1 pfx = new ASN1 (0x30);
1237 pfx.Add (version);
1238 pfx.Add (authSafe.ASN1);
1239 if (macData.Count > 0) {
1240 // only for password based encryption
1241 pfx.Add (macData);
1244 return pfx.GetBytes ();
1247 // Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents. Used in GetBytes(), above.
1248 private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid)
1250 byte[] salt = new byte [8];
1251 RNG.GetBytes (salt);
1253 ASN1 seqParams = new ASN1 (0x30);
1254 seqParams.Add (new ASN1 (0x04, salt));
1255 seqParams.Add (ASN1Convert.FromInt32 (_iterations));
1257 ASN1 seqPbe = new ASN1 (0x30);
1258 seqPbe.Add (ASN1Convert.FromOid (algorithmOid));
1259 seqPbe.Add (seqParams);
1261 byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ());
1262 ASN1 encryptedContent = new ASN1 (0x80, encrypted);
1264 ASN1 seq = new ASN1 (0x30);
1265 seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
1266 seq.Add (seqPbe);
1267 seq.Add (encryptedContent);
1269 ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 });
1270 ASN1 encData = new ASN1 (0x30);
1271 encData.Add (version);
1272 encData.Add (seq);
1274 ASN1 finalContent = new ASN1 (0xA0);
1275 finalContent.Add (encData);
1277 PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
1278 bag.Content = finalContent;
1279 return bag;
1282 public void AddCertificate (X509Certificate cert)
1284 AddCertificate (cert, null);
1287 public void AddCertificate (X509Certificate cert, IDictionary attributes)
1289 bool found = false;
1291 for (int i = 0; !found && i < _safeBags.Count; i++) {
1292 SafeBag sb = (SafeBag)_safeBags [i];
1294 if (sb.BagOID.Equals (certBag)) {
1295 ASN1 safeBag = sb.ASN1;
1296 ASN1 bagValue = safeBag [1];
1297 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1298 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1299 if (Compare (cert.RawData, c.RawData)) {
1300 found = true;
1305 if (!found) {
1306 _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
1307 _certsChanged = true;
1311 public void RemoveCertificate (X509Certificate cert)
1313 RemoveCertificate (cert, null);
1316 public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
1318 int certIndex = -1;
1320 for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
1321 SafeBag sb = (SafeBag)_safeBags [i];
1323 if (sb.BagOID.Equals (certBag)) {
1324 ASN1 safeBag = sb.ASN1;
1325 ASN1 bagValue = safeBag [1];
1326 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1327 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1328 if (Compare (cert.RawData, c.RawData)) {
1329 if (attrs != null) {
1330 if (safeBag.Count == 3) {
1331 ASN1 bagAttributes = safeBag [2];
1332 int bagAttributesFound = 0;
1333 for (int j = 0; j < bagAttributes.Count; j++) {
1334 ASN1 pkcs12Attribute = bagAttributes [j];
1335 ASN1 attrId = pkcs12Attribute [0];
1336 string ao = ASN1Convert.ToOid (attrId);
1337 ArrayList dattrValues = (ArrayList)attrs [ao];
1339 if (dattrValues != null) {
1340 ASN1 attrValues = pkcs12Attribute [1];
1342 if (dattrValues.Count == attrValues.Count) {
1343 int attrValuesFound = 0;
1344 for (int k = 0; k < attrValues.Count; k++) {
1345 ASN1 attrValue = attrValues [k];
1346 byte[] value = (byte[])dattrValues [k];
1348 if (Compare (value, attrValue.Value)) {
1349 attrValuesFound += 1;
1352 if (attrValuesFound == attrValues.Count) {
1353 bagAttributesFound += 1;
1358 if (bagAttributesFound == bagAttributes.Count) {
1359 certIndex = i;
1362 } else {
1363 certIndex = i;
1369 if (certIndex != -1) {
1370 _safeBags.RemoveAt (certIndex);
1371 _certsChanged = true;
1375 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
1377 // fast path
1378 if (a1.KeySize != a2.KeySize)
1379 return false;
1380 // compare public keys - if they match we can assume the private match too
1381 return (a1.ToXmlString (false) == a2.ToXmlString (false));
1384 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1386 AddPkcs8ShroudedKeyBag (aa, null);
1389 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1391 bool found = false;
1393 for (int i = 0; !found && i < _safeBags.Count; i++) {
1394 SafeBag sb = (SafeBag)_safeBags [i];
1396 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1397 ASN1 bagValue = sb.ASN1 [1];
1398 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1399 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1400 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1401 byte[] privateKey = pki.PrivateKey;
1403 AsymmetricAlgorithm saa = null;
1404 switch (privateKey [0]) {
1405 case 0x02:
1406 DSAParameters p = new DSAParameters (); // FIXME
1407 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1408 break;
1409 case 0x30:
1410 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1411 break;
1412 default:
1413 Array.Clear (decrypted, 0, decrypted.Length);
1414 Array.Clear (privateKey, 0, privateKey.Length);
1415 throw new CryptographicException ("Unknown private key format");
1418 Array.Clear (decrypted, 0, decrypted.Length);
1419 Array.Clear (privateKey, 0, privateKey.Length);
1421 if (CompareAsymmetricAlgorithm (aa , saa)) {
1422 found = true;
1427 if (!found) {
1428 _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
1429 _keyBagsChanged = true;
1433 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1435 int aaIndex = -1;
1437 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1438 SafeBag sb = (SafeBag)_safeBags [i];
1440 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1441 ASN1 bagValue = sb.ASN1 [1];
1442 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1443 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1444 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1445 byte[] privateKey = pki.PrivateKey;
1447 AsymmetricAlgorithm saa = null;
1448 switch (privateKey [0]) {
1449 case 0x02:
1450 DSAParameters p = new DSAParameters (); // FIXME
1451 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1452 break;
1453 case 0x30:
1454 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1455 break;
1456 default:
1457 Array.Clear (decrypted, 0, decrypted.Length);
1458 Array.Clear (privateKey, 0, privateKey.Length);
1459 throw new CryptographicException ("Unknown private key format");
1462 Array.Clear (decrypted, 0, decrypted.Length);
1463 Array.Clear (privateKey, 0, privateKey.Length);
1465 if (CompareAsymmetricAlgorithm (aa, saa)) {
1466 aaIndex = i;
1471 if (aaIndex != -1) {
1472 _safeBags.RemoveAt (aaIndex);
1473 _keyBagsChanged = true;
1477 public void AddKeyBag (AsymmetricAlgorithm aa)
1479 AddKeyBag (aa, null);
1482 public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1484 bool found = false;
1486 for (int i = 0; !found && i < _safeBags.Count; i++) {
1487 SafeBag sb = (SafeBag)_safeBags [i];
1489 if (sb.BagOID.Equals (keyBag)) {
1490 ASN1 bagValue = sb.ASN1 [1];
1491 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1492 byte[] privateKey = pki.PrivateKey;
1494 AsymmetricAlgorithm saa = null;
1495 switch (privateKey [0]) {
1496 case 0x02:
1497 DSAParameters p = new DSAParameters (); // FIXME
1498 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1499 break;
1500 case 0x30:
1501 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1502 break;
1503 default:
1504 Array.Clear (privateKey, 0, privateKey.Length);
1505 throw new CryptographicException ("Unknown private key format");
1508 Array.Clear (privateKey, 0, privateKey.Length);
1510 if (CompareAsymmetricAlgorithm (aa, saa)) {
1511 found = true;
1516 if (!found) {
1517 _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
1518 _keyBagsChanged = true;
1522 public void RemoveKeyBag (AsymmetricAlgorithm aa)
1524 int aaIndex = -1;
1526 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1527 SafeBag sb = (SafeBag)_safeBags [i];
1529 if (sb.BagOID.Equals (keyBag)) {
1530 ASN1 bagValue = sb.ASN1 [1];
1531 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1532 byte[] privateKey = pki.PrivateKey;
1534 AsymmetricAlgorithm saa = null;
1535 switch (privateKey [0]) {
1536 case 0x02:
1537 DSAParameters p = new DSAParameters (); // FIXME
1538 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1539 break;
1540 case 0x30:
1541 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1542 break;
1543 default:
1544 Array.Clear (privateKey, 0, privateKey.Length);
1545 throw new CryptographicException ("Unknown private key format");
1548 Array.Clear (privateKey, 0, privateKey.Length);
1550 if (CompareAsymmetricAlgorithm (aa, saa)) {
1551 aaIndex = i;
1556 if (aaIndex != -1) {
1557 _safeBags.RemoveAt (aaIndex);
1558 _keyBagsChanged = true;
1562 public void AddSecretBag (byte[] secret)
1564 AddSecretBag (secret, null);
1567 public void AddSecretBag (byte[] secret, IDictionary attributes)
1569 bool found = false;
1571 for (int i = 0; !found && i < _safeBags.Count; i++) {
1572 SafeBag sb = (SafeBag)_safeBags [i];
1574 if (sb.BagOID.Equals (secretBag)) {
1575 ASN1 bagValue = sb.ASN1 [1];
1576 byte[] ssecret = bagValue.Value;
1578 if (Compare (secret, ssecret)) {
1579 found = true;
1584 if (!found) {
1585 _safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes)));
1586 _secretBagsChanged = true;
1590 public void RemoveSecretBag (byte[] secret)
1592 int sIndex = -1;
1594 for (int i = 0; sIndex == -1 && 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)) {
1602 sIndex = i;
1607 if (sIndex != -1) {
1608 _safeBags.RemoveAt (sIndex);
1609 _secretBagsChanged = true;
1613 public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
1615 foreach (SafeBag sb in _safeBags) {
1616 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1617 ASN1 safeBag = sb.ASN1;
1619 if (safeBag.Count == 3) {
1620 ASN1 bagAttributes = safeBag [2];
1622 int bagAttributesFound = 0;
1623 for (int i = 0; i < bagAttributes.Count; i++) {
1624 ASN1 pkcs12Attribute = bagAttributes [i];
1625 ASN1 attrId = pkcs12Attribute [0];
1626 string ao = ASN1Convert.ToOid (attrId);
1627 ArrayList dattrValues = (ArrayList)attrs [ao];
1629 if (dattrValues != null) {
1630 ASN1 attrValues = pkcs12Attribute [1];
1632 if (dattrValues.Count == attrValues.Count) {
1633 int attrValuesFound = 0;
1634 for (int j = 0; j < attrValues.Count; j++) {
1635 ASN1 attrValue = attrValues [j];
1636 byte[] value = (byte[])dattrValues [j];
1638 if (Compare (value, attrValue.Value)) {
1639 attrValuesFound += 1;
1642 if (attrValuesFound == attrValues.Count) {
1643 bagAttributesFound += 1;
1648 if (bagAttributesFound == bagAttributes.Count) {
1649 ASN1 bagValue = safeBag [1];
1650 AsymmetricAlgorithm aa = null;
1651 if (sb.BagOID.Equals (keyBag)) {
1652 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1653 byte[] privateKey = pki.PrivateKey;
1654 switch (privateKey [0]) {
1655 case 0x02:
1656 DSAParameters p = new DSAParameters (); // FIXME
1657 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1658 break;
1659 case 0x30:
1660 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1661 break;
1662 default:
1663 break;
1665 Array.Clear (privateKey, 0, privateKey.Length);
1666 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1667 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1668 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1669 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1670 byte[] privateKey = pki.PrivateKey;
1671 switch (privateKey [0]) {
1672 case 0x02:
1673 DSAParameters p = new DSAParameters (); // FIXME
1674 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1675 break;
1676 case 0x30:
1677 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1678 break;
1679 default:
1680 break;
1682 Array.Clear (privateKey, 0, privateKey.Length);
1683 Array.Clear (decrypted, 0, decrypted.Length);
1685 return aa;
1691 return null;
1694 public byte[] GetSecret (IDictionary attrs)
1696 foreach (SafeBag sb in _safeBags) {
1697 if (sb.BagOID.Equals (secretBag)) {
1698 ASN1 safeBag = sb.ASN1;
1700 if (safeBag.Count == 3) {
1701 ASN1 bagAttributes = safeBag [2];
1703 int bagAttributesFound = 0;
1704 for (int i = 0; i < bagAttributes.Count; i++) {
1705 ASN1 pkcs12Attribute = bagAttributes [i];
1706 ASN1 attrId = pkcs12Attribute [0];
1707 string ao = ASN1Convert.ToOid (attrId);
1708 ArrayList dattrValues = (ArrayList)attrs [ao];
1710 if (dattrValues != null) {
1711 ASN1 attrValues = pkcs12Attribute [1];
1713 if (dattrValues.Count == attrValues.Count) {
1714 int attrValuesFound = 0;
1715 for (int j = 0; j < attrValues.Count; j++) {
1716 ASN1 attrValue = attrValues [j];
1717 byte[] value = (byte[])dattrValues [j];
1719 if (Compare (value, attrValue.Value)) {
1720 attrValuesFound += 1;
1723 if (attrValuesFound == attrValues.Count) {
1724 bagAttributesFound += 1;
1729 if (bagAttributesFound == bagAttributes.Count) {
1730 ASN1 bagValue = safeBag [1];
1731 return bagValue.Value;
1737 return null;
1740 public X509Certificate GetCertificate (IDictionary attrs)
1742 foreach (SafeBag sb in _safeBags) {
1743 if (sb.BagOID.Equals (certBag)) {
1744 ASN1 safeBag = sb.ASN1;
1746 if (safeBag.Count == 3) {
1747 ASN1 bagAttributes = safeBag [2];
1749 int bagAttributesFound = 0;
1750 for (int i = 0; i < bagAttributes.Count; i++) {
1751 ASN1 pkcs12Attribute = bagAttributes [i];
1752 ASN1 attrId = pkcs12Attribute [0];
1753 string ao = ASN1Convert.ToOid (attrId);
1754 ArrayList dattrValues = (ArrayList)attrs [ao];
1756 if (dattrValues != null) {
1757 ASN1 attrValues = pkcs12Attribute [1];
1759 if (dattrValues.Count == attrValues.Count) {
1760 int attrValuesFound = 0;
1761 for (int j = 0; j < attrValues.Count; j++) {
1762 ASN1 attrValue = attrValues [j];
1763 byte[] value = (byte[])dattrValues [j];
1765 if (Compare (value, attrValue.Value)) {
1766 attrValuesFound += 1;
1769 if (attrValuesFound == attrValues.Count) {
1770 bagAttributesFound += 1;
1775 if (bagAttributesFound == bagAttributes.Count) {
1776 ASN1 bagValue = safeBag [1];
1777 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1778 return new X509Certificate (crt.Content [0].Value);
1784 return null;
1787 public IDictionary GetAttributes (AsymmetricAlgorithm aa)
1789 IDictionary result = new Hashtable ();
1791 foreach (SafeBag sb in _safeBags) {
1792 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1793 ASN1 safeBag = sb.ASN1;
1795 ASN1 bagValue = safeBag [1];
1796 AsymmetricAlgorithm saa = null;
1797 if (sb.BagOID.Equals (keyBag)) {
1798 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1799 byte[] privateKey = pki.PrivateKey;
1800 switch (privateKey [0]) {
1801 case 0x02:
1802 DSAParameters p = new DSAParameters (); // FIXME
1803 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1804 break;
1805 case 0x30:
1806 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1807 break;
1808 default:
1809 break;
1811 Array.Clear (privateKey, 0, privateKey.Length);
1812 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1813 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1814 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1815 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1816 byte[] privateKey = pki.PrivateKey;
1817 switch (privateKey [0]) {
1818 case 0x02:
1819 DSAParameters p = new DSAParameters (); // FIXME
1820 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1821 break;
1822 case 0x30:
1823 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1824 break;
1825 default:
1826 break;
1828 Array.Clear (privateKey, 0, privateKey.Length);
1829 Array.Clear (decrypted, 0, decrypted.Length);
1832 if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
1833 if (safeBag.Count == 3) {
1834 ASN1 bagAttributes = safeBag [2];
1836 for (int i = 0; i < bagAttributes.Count; i++) {
1837 ASN1 pkcs12Attribute = bagAttributes [i];
1838 ASN1 attrId = pkcs12Attribute [0];
1839 string aOid = ASN1Convert.ToOid (attrId);
1840 ArrayList aValues = new ArrayList ();
1842 ASN1 attrValues = pkcs12Attribute [1];
1844 for (int j = 0; j < attrValues.Count; j++) {
1845 ASN1 attrValue = attrValues [j];
1846 aValues.Add (attrValue.Value);
1848 result.Add (aOid, aValues);
1855 return result;
1858 public IDictionary GetAttributes (X509Certificate cert)
1860 IDictionary result = new Hashtable ();
1862 foreach (SafeBag sb in _safeBags) {
1863 if (sb.BagOID.Equals (certBag)) {
1864 ASN1 safeBag = sb.ASN1;
1865 ASN1 bagValue = safeBag [1];
1866 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1867 X509Certificate xc = new X509Certificate (crt.Content [0].Value);
1869 if (Compare (cert.RawData, xc.RawData)) {
1870 if (safeBag.Count == 3) {
1871 ASN1 bagAttributes = safeBag [2];
1873 for (int i = 0; i < bagAttributes.Count; i++) {
1874 ASN1 pkcs12Attribute = bagAttributes [i];
1875 ASN1 attrId = pkcs12Attribute [0];
1877 string aOid = ASN1Convert.ToOid (attrId);
1878 ArrayList aValues = new ArrayList ();
1880 ASN1 attrValues = pkcs12Attribute [1];
1882 for (int j = 0; j < attrValues.Count; j++) {
1883 ASN1 attrValue = attrValues [j];
1884 aValues.Add (attrValue.Value);
1886 result.Add (aOid, aValues);
1893 return result;
1896 public void SaveToFile (string filename)
1898 if (filename == null)
1899 throw new ArgumentNullException ("filename");
1901 using (FileStream fs = File.Create (filename)) {
1902 byte[] data = GetBytes ();
1903 fs.Write (data, 0, data.Length);
1907 public object Clone ()
1909 PKCS12 clone = null;
1910 if (_password != null) {
1911 clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
1912 } else {
1913 clone = new PKCS12 (GetBytes ());
1915 clone.IterationCount = this.IterationCount;
1917 return clone;
1920 // static
1922 public const int CryptoApiPasswordLimit = 32;
1924 static private int password_max_length = Int32.MaxValue;
1926 // static properties
1928 // MS CryptoAPI limits the password to a maximum of 31 characters
1929 // other implementations, like OpenSSL, have no such limitation.
1930 // Setting a maximum value will truncate the password length to
1931 // ensure compatibility with MS's PFXImportCertStore API.
1932 static public int MaximumPasswordLength {
1933 get { return password_max_length; }
1934 set {
1935 if (value < CryptoApiPasswordLimit) {
1936 string msg = Locale.GetText ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit);
1937 throw new ArgumentOutOfRangeException (msg);
1939 password_max_length = value;
1943 // static methods
1945 static private byte[] LoadFile (string filename)
1947 byte[] data = null;
1948 using (FileStream fs = File.OpenRead (filename)) {
1949 data = new byte [fs.Length];
1950 fs.Read (data, 0, data.Length);
1951 fs.Close ();
1953 return data;
1956 static public PKCS12 LoadFromFile (string filename)
1958 if (filename == null)
1959 throw new ArgumentNullException ("filename");
1961 return new PKCS12 (LoadFile (filename));
1964 static public PKCS12 LoadFromFile (string filename, string password)
1966 if (filename == null)
1967 throw new ArgumentNullException ("filename");
1969 return new PKCS12 (LoadFile (filename), password);