2 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
5 // Sebastien Pouliot <sebastien@xamarin.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com)
9 // Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
11 // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
12 // See bouncycastle.txt for license.
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
37 using System.Security.Cryptography;
41 using Mono.Security.Cryptography;
43 namespace Mono.Security.X509 {
52 public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1";
53 public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3";
54 public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4";
55 public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6";
56 public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10";
57 public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11";
69 public const string friendlyName = "1.2.840.113549.1.9.20";
70 public const string localKeyId = "1.2.840.113549.1.9.21";
76 internal class SafeBag {
77 private string _bagOID;
80 public SafeBag(string bagOID, ASN1 asn1) {
85 public string BagOID {
86 get { return _bagOID; }
100 class PKCS12 : ICloneable {
102 public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1";
103 public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2";
104 public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3";
105 public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4";
106 public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5";
107 public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6";
110 public const string keyBag = "1.2.840.113549.1.12.10.1.1";
111 public const string pkcs8ShroudedKeyBag = "1.2.840.113549.1.12.10.1.2";
112 public const string certBag = "1.2.840.113549.1.12.10.1.3";
113 public const string crlBag = "1.2.840.113549.1.12.10.1.4";
114 public const string secretBag = "1.2.840.113549.1.12.10.1.5";
115 public const string safeContentsBag = "1.2.840.113549.1.12.10.1.6";
118 public const string x509Certificate = "1.2.840.113549.1.9.22.1";
119 public const string sdsiCertificate = "1.2.840.113549.1.9.22.2";
120 public const string x509Crl = "1.2.840.113549.1.9.23.1";
122 // Adapted from BouncyCastle PKCS12ParametersGenerator.java
123 public class DeriveBytes {
125 public enum Purpose {
131 static private byte[] keyDiversifier = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
132 static private byte[] ivDiversifier = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 };
133 static private byte[] macDiversifier = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 };
135 private string _hashName;
136 private int _iterations;
137 private byte[] _password;
138 private byte[] _salt;
140 public DeriveBytes () {}
142 public string HashName {
143 get { return _hashName; }
144 set { _hashName = value; }
147 public int IterationCount {
148 get { return _iterations; }
149 set { _iterations = value; }
152 public byte[] Password {
153 get { return (byte[]) _password.Clone (); }
156 _password = new byte [0];
158 _password = (byte[]) value.Clone ();
163 get { return (byte[]) _salt.Clone (); }
166 _salt = (byte[]) value.Clone ();
172 private void Adjust (byte[] a, int aOff, byte[] b)
174 int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1;
176 a [aOff + b.Length - 1] = (byte) x;
179 for (int i = b.Length - 2; i >= 0; i--) {
180 x += (b [i] & 0xff) + (a [aOff + i] & 0xff);
181 a [aOff + i] = (byte) x;
186 private byte[] Derive (byte[] diversifier, int n)
188 HashAlgorithm digest = PKCS1.CreateFromName (_hashName);
189 int u = (digest.HashSize >> 3); // div 8
191 byte[] dKey = new byte [n];
194 if ((_salt != null) && (_salt.Length != 0)) {
195 S = new byte[v * ((_salt.Length + v - 1) / v)];
197 for (int i = 0; i != S.Length; i++) {
198 S[i] = _salt[i % _salt.Length];
206 if ((_password != null) && (_password.Length != 0)) {
207 P = new byte[v * ((_password.Length + v - 1) / v)];
209 for (int i = 0; i != P.Length; i++) {
210 P[i] = _password[i % _password.Length];
217 byte[] I = new byte [S.Length + P.Length];
219 Buffer.BlockCopy (S, 0, I, 0, S.Length);
220 Buffer.BlockCopy (P, 0, I, S.Length, P.Length);
222 byte[] B = new byte[v];
223 int c = (n + u - 1) / u;
225 for (int i = 1; i <= c; i++) {
226 digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0);
227 digest.TransformFinalBlock (I, 0, I.Length);
228 byte[] A = digest.Hash;
229 digest.Initialize ();
230 for (int j = 1; j != _iterations; j++) {
231 A = digest.ComputeHash (A, 0, A.Length);
234 for (int j = 0; j != B.Length; j++) {
235 B [j] = A [j % A.Length];
238 for (int j = 0; j != I.Length / v; j++) {
239 Adjust (I, j * v, B);
243 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
246 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length);
253 public byte[] DeriveKey (int size)
255 return Derive (keyDiversifier, size);
258 public byte[] DeriveIV (int size)
260 return Derive (ivDiversifier, size);
263 public byte[] DeriveMAC (int size)
265 return Derive (macDiversifier, size);
269 const int recommendedIterationCount = 2000;
271 //private int _version;
272 private byte[] _password;
273 private ArrayList _keyBags;
274 private ArrayList _secretBags;
275 private X509CertificateCollection _certs;
276 private bool _keyBagsChanged;
277 private bool _secretBagsChanged;
278 private bool _certsChanged;
279 private int _iterations;
280 private ArrayList _safeBags;
281 private RandomNumberGenerator _rng;
287 _iterations = recommendedIterationCount;
288 _keyBags = new ArrayList ();
289 _secretBags = new ArrayList ();
290 _certs = new X509CertificateCollection ();
291 _keyBagsChanged = false;
292 _secretBagsChanged = false;
293 _certsChanged = false;
294 _safeBags = new ArrayList ();
297 public PKCS12 (byte[] data)
306 * version INTEGER {v3(3)}(v3,...),
307 * authSafe ContentInfo,
308 * macData MacData OPTIONAL
311 * MacData ::= SEQUENCE {
313 * macSalt OCTET STRING,
314 * iterations INTEGER DEFAULT 1
315 * -- Note: The default is for historical reasons and its use is deprecated. A higher
316 * -- value, like 1024 is recommended.
319 * SafeContents ::= SEQUENCE OF SafeBag
321 * SafeBag ::= SEQUENCE {
322 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
323 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
324 * bagAttributes SET OF PKCS12Attribute OPTIONAL
327 public PKCS12 (byte[] data, string password)
334 public PKCS12 (byte[] data, byte[] password)
337 _password = password;
341 private void Decode (byte[] data)
343 ASN1 pfx = new ASN1 (data);
345 throw new ArgumentException ("invalid data");
347 ASN1 version = pfx [0];
348 if (version.Tag != 0x02)
349 throw new ArgumentException ("invalid PFX version");
350 //_version = version.Value [0];
352 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
353 if (authSafe.ContentType != PKCS7.Oid.data)
354 throw new ArgumentException ("invalid authenticated safe");
356 // now that we know it's a PKCS#12 file, check the (optional) MAC
357 // before decoding anything else in the file
359 ASN1 macData = pfx [2];
360 if (macData.Tag != 0x30)
361 throw new ArgumentException ("invalid MAC");
363 ASN1 mac = macData [0];
365 throw new ArgumentException ("invalid MAC");
366 ASN1 macAlgorithm = mac [0];
367 string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
368 if (macOid != "1.3.14.3.2.26")
369 throw new ArgumentException ("unsupported HMAC");
370 byte[] macValue = mac [1].Value;
372 ASN1 macSalt = macData [1];
373 if (macSalt.Tag != 0x04)
374 throw new ArgumentException ("missing MAC salt");
376 _iterations = 1; // default value
377 if (macData.Count > 2) {
378 ASN1 iters = macData [2];
379 if (iters.Tag != 0x02)
380 throw new ArgumentException ("invalid MAC iteration");
381 _iterations = ASN1Convert.ToInt32 (iters);
384 byte[] authSafeData = authSafe.Content [0].Value;
385 byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
386 if (!Compare (macValue, calculatedMac)) {
387 byte[] nullPassword = {0, 0};
388 calculatedMac = MAC(nullPassword, macSalt.Value, _iterations, authSafeData);
389 if (!Compare (macValue, calculatedMac))
390 throw new CryptographicException ("Invalid MAC - file may have been tampered with!");
391 _password = nullPassword;
395 // we now returns to our original presentation - PFX
396 ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
397 for (int i=0; i < authenticatedSafe.Count; i++) {
398 PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
399 switch (ci.ContentType) {
401 // unencrypted (by PKCS#12)
402 ASN1 safeContents = new ASN1 (ci.Content [0].Value);
403 for (int j=0; j < safeContents.Count; j++) {
404 ASN1 safeBag = safeContents [j];
405 ReadSafeBag (safeBag);
408 case PKCS7.Oid.encryptedData:
409 // password encrypted
410 PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
411 ASN1 decrypted = new ASN1 (Decrypt (ed));
412 for (int j=0; j < decrypted.Count; j++) {
413 ASN1 safeBag = decrypted [j];
414 ReadSafeBag (safeBag);
417 case PKCS7.Oid.envelopedData:
418 // public key encrypted
419 throw new NotImplementedException ("public key encrypted");
421 throw new ArgumentException ("unknown authenticatedSafe");
428 if (_password != null) {
429 Array.Clear (_password, 0, _password.Length);
436 public string Password {
438 // Clear old password.
439 if (_password != null)
440 Array.Clear (_password, 0, _password.Length);
443 if (value.Length > 0) {
444 int size = value.Length;
446 if (size < MaximumPasswordLength) {
447 // if not present, add space for a NULL (0x00) character
448 if (value[size - 1] != 0x00)
451 size = MaximumPasswordLength;
453 _password = new byte[(size + nul) << 1]; // double for unicode
454 Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0);
456 // double-byte (Unicode) NULL (0x00) - see bug #79617
457 _password = new byte[2];
463 public int IterationCount {
464 get { return _iterations; }
465 set { _iterations = value; }
468 public ArrayList Keys {
470 if (_keyBagsChanged) {
472 foreach (SafeBag sb in _safeBags) {
473 if (sb.BagOID.Equals (keyBag)) {
474 ASN1 safeBag = sb.ASN1;
475 ASN1 bagValue = safeBag [1];
476 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
477 byte[] privateKey = pki.PrivateKey;
478 switch (privateKey [0]) {
480 DSAParameters p = new DSAParameters (); // FIXME
481 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
484 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
489 Array.Clear (privateKey, 0, privateKey.Length);
491 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
492 ASN1 safeBag = sb.ASN1;
493 ASN1 bagValue = safeBag [1];
494 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
495 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
496 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
497 byte[] privateKey = pki.PrivateKey;
498 switch (privateKey [0]) {
500 DSAParameters p = new DSAParameters (); // FIXME
501 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
504 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
509 Array.Clear (privateKey, 0, privateKey.Length);
510 Array.Clear (decrypted, 0, decrypted.Length);
513 _keyBagsChanged = false;
515 return ArrayList.ReadOnly(_keyBags);
519 public ArrayList Secrets {
521 if (_secretBagsChanged) {
522 _secretBags.Clear ();
523 foreach (SafeBag sb in _safeBags) {
524 if (sb.BagOID.Equals (secretBag)) {
525 ASN1 safeBag = sb.ASN1;
526 ASN1 bagValue = safeBag [1];
527 byte[] secret = bagValue.Value;
528 _secretBags.Add(secret);
531 _secretBagsChanged = false;
533 return ArrayList.ReadOnly(_secretBags);
537 public X509CertificateCollection Certificates {
541 foreach (SafeBag sb in _safeBags) {
542 if (sb.BagOID.Equals (certBag)) {
543 ASN1 safeBag = sb.ASN1;
544 ASN1 bagValue = safeBag [1];
545 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
546 _certs.Add (new X509Certificate (cert.Content [0].Value));
549 _certsChanged = false;
555 internal RandomNumberGenerator RNG {
558 _rng = RandomNumberGenerator.Create ();
565 private bool Compare (byte[] expected, byte[] actual)
567 bool compare = false;
568 if (expected.Length == actual.Length) {
569 for (int i=0; i < expected.Length; i++) {
570 if (expected [i] != actual [i])
578 private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
580 string algorithm = null;
581 int keyLength = 8; // 64 bits (default)
582 int ivLength = 8; // 64 bits (default)
584 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
585 pd.Password = _password;
587 pd.IterationCount = iterationCount;
589 switch (algorithmOid) {
590 case PKCS5.pbeWithMD2AndDESCBC: // no unit test available
594 case PKCS5.pbeWithMD5AndDESCBC: // no unit test available
598 case PKCS5.pbeWithMD2AndRC2CBC: // no unit test available
599 // TODO - RC2-CBC-Parameter (PKCS5)
600 // if missing default to 32 bits !!!
603 keyLength = 4; // default
605 case PKCS5.pbeWithMD5AndRC2CBC: // no unit test available
606 // TODO - RC2-CBC-Parameter (PKCS5)
607 // if missing default to 32 bits !!!
610 keyLength = 4; // default
612 case PKCS5.pbeWithSHA1AndDESCBC: // no unit test available
613 pd.HashName = "SHA1";
616 case PKCS5.pbeWithSHA1AndRC2CBC: // no unit test available
617 // TODO - RC2-CBC-Parameter (PKCS5)
618 // if missing default to 32 bits !!!
619 pd.HashName = "SHA1";
621 keyLength = 4; // default
623 case PKCS12.pbeWithSHAAnd128BitRC4: // no unit test available
624 pd.HashName = "SHA1";
629 case PKCS12.pbeWithSHAAnd40BitRC4: // no unit test available
630 pd.HashName = "SHA1";
635 case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC:
636 pd.HashName = "SHA1";
637 algorithm = "TripleDES";
640 case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC: // no unit test available
641 pd.HashName = "SHA1";
642 algorithm = "TripleDES";
645 case PKCS12.pbeWithSHAAnd128BitRC2CBC: // no unit test available
646 pd.HashName = "SHA1";
650 case PKCS12.pbeWithSHAAnd40BitRC2CBC:
651 pd.HashName = "SHA1";
656 throw new NotSupportedException ("unknown oid " + algorithm);
659 SymmetricAlgorithm sa = null;
660 #if INSIDE_CORLIB && FULL_AOT_RUNTIME
661 // we do not want CryptoConfig to bring the whole crypto stack
662 // in particular Rijndael which is not supported by CommonCrypto
671 sa = TripleDES.Create ();
677 throw new NotSupportedException (algorithm);
680 sa = SymmetricAlgorithm.Create (algorithm);
682 sa.Key = pd.DeriveKey (keyLength);
683 // IV required only for block ciphers (not stream ciphers)
685 sa.IV = pd.DeriveIV (ivLength);
686 sa.Mode = CipherMode.CBC;
691 public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData)
693 SymmetricAlgorithm sa = null;
694 byte[] result = null;
696 sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
697 ICryptoTransform ct = sa.CreateDecryptor ();
698 result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
707 public byte[] Decrypt (PKCS7.EncryptedData ed)
709 return Decrypt (ed.EncryptionAlgorithm.ContentType,
710 ed.EncryptionAlgorithm.Content [0].Value,
711 ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
712 ed.EncryptedContent);
715 public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data)
717 byte[] result = null;
718 using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
719 ICryptoTransform ct = sa.CreateEncryptor ();
720 result = ct.TransformFinalBlock (data, 0, data.Length);
725 private DSAParameters GetExistingParameters (out bool found)
727 foreach (X509Certificate cert in Certificates) {
728 // FIXME: that won't work if parts of the parameters are missing
729 if (cert.KeyAlgorithmParameters != null) {
733 return dsa.ExportParameters (false);
738 return new DSAParameters ();
741 private void AddPrivateKey (PKCS8.PrivateKeyInfo pki)
743 byte[] privateKey = pki.PrivateKey;
744 switch (privateKey [0]) {
747 DSAParameters p = GetExistingParameters (out found);
749 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
753 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
756 Array.Clear (privateKey, 0, privateKey.Length);
757 throw new CryptographicException ("Unknown private key format");
759 Array.Clear (privateKey, 0, privateKey.Length);
762 private void ReadSafeBag (ASN1 safeBag)
764 if (safeBag.Tag != 0x30)
765 throw new ArgumentException ("invalid safeBag");
767 ASN1 bagId = safeBag [0];
768 if (bagId.Tag != 0x06)
769 throw new ArgumentException ("invalid safeBag id");
771 ASN1 bagValue = safeBag [1];
772 string oid = ASN1Convert.ToOid (bagId);
776 AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
778 case pkcs8ShroudedKeyBag:
779 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
780 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
781 AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
782 Array.Clear (decrypted, 0, decrypted.Length);
785 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
786 if (cert.ContentType != x509Certificate)
787 throw new NotSupportedException ("unsupport certificate type");
788 X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
795 byte[] secret = bagValue.Value;
796 _secretBags.Add(secret);
798 case safeContentsBag:
799 // TODO - ? recurse ?
802 throw new ArgumentException ("unknown safeBag oid");
805 if (safeBag.Count > 2) {
806 ASN1 bagAttributes = safeBag [2];
807 if (bagAttributes.Tag != 0x31)
808 throw new ArgumentException ("invalid safeBag attributes id");
810 for (int i = 0; i < bagAttributes.Count; i++) {
811 ASN1 pkcs12Attribute = bagAttributes[i];
813 if (pkcs12Attribute.Tag != 0x30)
814 throw new ArgumentException ("invalid PKCS12 attributes id");
816 ASN1 attrId = pkcs12Attribute [0];
817 if (attrId.Tag != 0x06)
818 throw new ArgumentException ("invalid attribute id");
820 string attrOid = ASN1Convert.ToOid (attrId);
822 ASN1 attrValues = pkcs12Attribute[1];
823 for (int j = 0; j < attrValues.Count; j++) {
824 ASN1 attrValue = attrValues[j];
827 case PKCS9.friendlyName:
828 if (attrValue.Tag != 0x1e)
829 throw new ArgumentException ("invalid attribute value id");
831 case PKCS9.localKeyId:
832 if (attrValue.Tag != 0x04)
833 throw new ArgumentException ("invalid attribute value id");
836 // Unknown OID -- don't check Tag
843 _safeBags.Add (new SafeBag(oid, safeBag));
846 private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
848 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
850 pki.Algorithm = "1.2.840.113549.1.1.1";
851 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
853 else if (aa is DSA) {
854 pki.Algorithm = null;
855 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
858 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
860 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
861 epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
862 epki.IterationCount = _iterations;
863 epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
865 ASN1 safeBag = new ASN1 (0x30);
866 safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
867 ASN1 bagValue = new ASN1 (0xA0);
868 bagValue.Add (new ASN1 (epki.GetBytes ()));
869 safeBag.Add (bagValue);
871 if (attributes != null) {
872 ASN1 bagAttributes = new ASN1 (0x31);
873 IDictionaryEnumerator de = attributes.GetEnumerator ();
875 while (de.MoveNext ()) {
876 string oid = (string)de.Key;
878 case PKCS9.friendlyName:
879 ArrayList names = (ArrayList)de.Value;
880 if (names.Count > 0) {
881 ASN1 pkcs12Attribute = new ASN1 (0x30);
882 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
883 ASN1 attrValues = new ASN1 (0x31);
884 foreach (byte[] name in names) {
885 ASN1 attrValue = new ASN1 (0x1e);
886 attrValue.Value = name;
887 attrValues.Add (attrValue);
889 pkcs12Attribute.Add (attrValues);
890 bagAttributes.Add (pkcs12Attribute);
893 case PKCS9.localKeyId:
894 ArrayList keys = (ArrayList)de.Value;
895 if (keys.Count > 0) {
896 ASN1 pkcs12Attribute = new ASN1 (0x30);
897 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
898 ASN1 attrValues = new ASN1 (0x31);
899 foreach (byte[] key in keys) {
900 ASN1 attrValue = new ASN1 (0x04);
901 attrValue.Value = key;
902 attrValues.Add (attrValue);
904 pkcs12Attribute.Add (attrValues);
905 bagAttributes.Add (pkcs12Attribute);
913 if (bagAttributes.Count > 0) {
914 safeBag.Add (bagAttributes);
921 private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes)
923 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
925 pki.Algorithm = "1.2.840.113549.1.1.1";
926 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
928 else if (aa is DSA) {
929 pki.Algorithm = null;
930 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
933 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
935 ASN1 safeBag = new ASN1 (0x30);
936 safeBag.Add (ASN1Convert.FromOid (keyBag));
937 ASN1 bagValue = new ASN1 (0xA0);
938 bagValue.Add (new ASN1 (pki.GetBytes ()));
939 safeBag.Add (bagValue);
941 if (attributes != null) {
942 ASN1 bagAttributes = new ASN1 (0x31);
943 IDictionaryEnumerator de = attributes.GetEnumerator ();
945 while (de.MoveNext ()) {
946 string oid = (string)de.Key;
948 case PKCS9.friendlyName:
949 ArrayList names = (ArrayList)de.Value;
950 if (names.Count > 0) {
951 ASN1 pkcs12Attribute = new ASN1 (0x30);
952 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
953 ASN1 attrValues = new ASN1 (0x31);
954 foreach (byte[] name in names) {
955 ASN1 attrValue = new ASN1 (0x1e);
956 attrValue.Value = name;
957 attrValues.Add (attrValue);
959 pkcs12Attribute.Add (attrValues);
960 bagAttributes.Add (pkcs12Attribute);
963 case PKCS9.localKeyId:
964 ArrayList keys = (ArrayList)de.Value;
965 if (keys.Count > 0) {
966 ASN1 pkcs12Attribute = new ASN1 (0x30);
967 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
968 ASN1 attrValues = new ASN1 (0x31);
969 foreach (byte[] key in keys) {
970 ASN1 attrValue = new ASN1 (0x04);
971 attrValue.Value = key;
972 attrValues.Add (attrValue);
974 pkcs12Attribute.Add (attrValues);
975 bagAttributes.Add (pkcs12Attribute);
983 if (bagAttributes.Count > 0) {
984 safeBag.Add (bagAttributes);
991 private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes)
993 ASN1 safeBag = new ASN1 (0x30);
994 safeBag.Add (ASN1Convert.FromOid (secretBag));
995 ASN1 bagValue = new ASN1 (0x80, secret);
996 safeBag.Add (bagValue);
998 if (attributes != null) {
999 ASN1 bagAttributes = new ASN1 (0x31);
1000 IDictionaryEnumerator de = attributes.GetEnumerator ();
1002 while (de.MoveNext ()) {
1003 string oid = (string)de.Key;
1005 case PKCS9.friendlyName:
1006 ArrayList names = (ArrayList)de.Value;
1007 if (names.Count > 0) {
1008 ASN1 pkcs12Attribute = new ASN1 (0x30);
1009 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
1010 ASN1 attrValues = new ASN1 (0x31);
1011 foreach (byte[] name in names) {
1012 ASN1 attrValue = new ASN1 (0x1e);
1013 attrValue.Value = name;
1014 attrValues.Add (attrValue);
1016 pkcs12Attribute.Add (attrValues);
1017 bagAttributes.Add (pkcs12Attribute);
1020 case PKCS9.localKeyId:
1021 ArrayList keys = (ArrayList)de.Value;
1022 if (keys.Count > 0) {
1023 ASN1 pkcs12Attribute = new ASN1 (0x30);
1024 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
1025 ASN1 attrValues = new ASN1 (0x31);
1026 foreach (byte[] key in keys) {
1027 ASN1 attrValue = new ASN1 (0x04);
1028 attrValue.Value = key;
1029 attrValues.Add (attrValue);
1031 pkcs12Attribute.Add (attrValues);
1032 bagAttributes.Add (pkcs12Attribute);
1040 if (bagAttributes.Count > 0) {
1041 safeBag.Add (bagAttributes);
1048 private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes)
1050 ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
1052 PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
1053 ci.ContentType = x509Certificate;
1054 ci.Content.Add (encapsulatedCertificate);
1056 ASN1 bagValue = new ASN1 (0xA0);
1057 bagValue.Add (ci.ASN1);
1059 ASN1 safeBag = new ASN1 (0x30);
1060 safeBag.Add (ASN1Convert.FromOid (certBag));
1061 safeBag.Add (bagValue);
1063 if (attributes != null) {
1064 ASN1 bagAttributes = new ASN1 (0x31);
1065 IDictionaryEnumerator de = attributes.GetEnumerator ();
1067 while (de.MoveNext ()) {
1068 string oid = (string)de.Key;
1070 case PKCS9.friendlyName:
1071 ArrayList names = (ArrayList)de.Value;
1072 if (names.Count > 0) {
1073 ASN1 pkcs12Attribute = new ASN1 (0x30);
1074 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
1075 ASN1 attrValues = new ASN1 (0x31);
1076 foreach (byte[] name in names) {
1077 ASN1 attrValue = new ASN1 (0x1e);
1078 attrValue.Value = name;
1079 attrValues.Add (attrValue);
1081 pkcs12Attribute.Add (attrValues);
1082 bagAttributes.Add (pkcs12Attribute);
1085 case PKCS9.localKeyId:
1086 ArrayList keys = (ArrayList)de.Value;
1087 if (keys.Count > 0) {
1088 ASN1 pkcs12Attribute = new ASN1 (0x30);
1089 pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
1090 ASN1 attrValues = new ASN1 (0x31);
1091 foreach (byte[] key in keys) {
1092 ASN1 attrValue = new ASN1 (0x04);
1093 attrValue.Value = key;
1094 attrValues.Add (attrValue);
1096 pkcs12Attribute.Add (attrValues);
1097 bagAttributes.Add (pkcs12Attribute);
1105 if (bagAttributes.Count > 0) {
1106 safeBag.Add (bagAttributes);
1113 private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data)
1115 PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
1116 pd.HashName = "SHA1";
1117 pd.Password = password;
1119 pd.IterationCount = iterations;
1121 HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
1122 hmac.Key = pd.DeriveMAC (20);
1123 return hmac.ComputeHash (data, 0, data.Length);
1127 * SafeContents ::= SEQUENCE OF SafeBag
1129 * SafeBag ::= SEQUENCE {
1130 * bagId BAG-TYPE.&id ({PKCS12BagSet}),
1131 * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
1132 * bagAttributes SET OF PKCS12Attribute OPTIONAL
1135 public byte[] GetBytes ()
1137 // TODO (incomplete)
1138 ASN1 safeBagSequence = new ASN1 (0x30);
1140 // Sync Safe Bag list since X509CertificateCollection may be updated
1141 ArrayList scs = new ArrayList ();
1142 foreach (SafeBag sb in _safeBags) {
1143 if (sb.BagOID.Equals (certBag)) {
1144 ASN1 safeBag = sb.ASN1;
1145 ASN1 bagValue = safeBag [1];
1146 PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
1147 scs.Add (new X509Certificate (cert.Content [0].Value));
1151 ArrayList addcerts = new ArrayList ();
1152 ArrayList removecerts = new ArrayList ();
1154 foreach (X509Certificate c in Certificates) {
1157 foreach (X509Certificate lc in scs) {
1158 if (Compare (c.RawData, lc.RawData)) {
1167 foreach (X509Certificate c in scs) {
1170 foreach (X509Certificate lc in Certificates) {
1171 if (Compare (c.RawData, lc.RawData)) {
1177 removecerts.Add (c);
1181 foreach (X509Certificate c in removecerts) {
1182 RemoveCertificate (c);
1185 foreach (X509Certificate c in addcerts) {
1190 if (_safeBags.Count > 0) {
1191 ASN1 certsSafeBag = new ASN1 (0x30);
1192 foreach (SafeBag sb in _safeBags) {
1193 if (sb.BagOID.Equals (certBag)) {
1194 certsSafeBag.Add (sb.ASN1);
1198 if (certsSafeBag.Count > 0) {
1199 PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
1200 safeBagSequence.Add (contentInfo.ASN1);
1204 if (_safeBags.Count > 0) {
1205 ASN1 safeContents = new ASN1 (0x30);
1206 foreach (SafeBag sb in _safeBags) {
1207 if (sb.BagOID.Equals (keyBag) ||
1208 sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1209 safeContents.Add (sb.ASN1);
1212 if (safeContents.Count > 0) {
1213 ASN1 content = new ASN1 (0xA0);
1214 content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
1216 PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
1217 keyBag.Content = content;
1218 safeBagSequence.Add (keyBag.ASN1);
1222 // Doing SecretBags separately in case we want to change their encryption independently.
1223 if (_safeBags.Count > 0) {
1224 ASN1 secretsSafeBag = new ASN1 (0x30);
1225 foreach (SafeBag sb in _safeBags) {
1226 if (sb.BagOID.Equals (secretBag)) {
1227 secretsSafeBag.Add (sb.ASN1);
1231 if (secretsSafeBag.Count > 0) {
1232 PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
1233 safeBagSequence.Add (contentInfo.ASN1);
1238 ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
1239 ASN1 ci = new ASN1 (0xA0);
1240 ci.Add (encapsulates);
1241 PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
1242 authSafe.Content = ci;
1244 ASN1 macData = new ASN1 (0x30);
1245 if (_password != null) {
1246 // only for password based encryption
1247 byte[] salt = new byte [20];
1248 RNG.GetBytes (salt);
1249 byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
1250 ASN1 oidSeq = new ASN1 (0x30);
1251 oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26")); // SHA1
1252 oidSeq.Add (new ASN1 (0x05));
1254 ASN1 mac = new ASN1 (0x30);
1256 mac.Add (new ASN1 (0x04, macValue));
1259 macData.Add (new ASN1 (0x04, salt));
1260 macData.Add (ASN1Convert.FromInt32 (_iterations));
1263 ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
1265 ASN1 pfx = new ASN1 (0x30);
1267 pfx.Add (authSafe.ASN1);
1268 if (macData.Count > 0) {
1269 // only for password based encryption
1273 return pfx.GetBytes ();
1276 // Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents. Used in GetBytes(), above.
1277 private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid)
1279 byte[] salt = new byte [8];
1280 RNG.GetBytes (salt);
1282 ASN1 seqParams = new ASN1 (0x30);
1283 seqParams.Add (new ASN1 (0x04, salt));
1284 seqParams.Add (ASN1Convert.FromInt32 (_iterations));
1286 ASN1 seqPbe = new ASN1 (0x30);
1287 seqPbe.Add (ASN1Convert.FromOid (algorithmOid));
1288 seqPbe.Add (seqParams);
1290 byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ());
1291 ASN1 encryptedContent = new ASN1 (0x80, encrypted);
1293 ASN1 seq = new ASN1 (0x30);
1294 seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
1296 seq.Add (encryptedContent);
1298 ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 });
1299 ASN1 encData = new ASN1 (0x30);
1300 encData.Add (version);
1303 ASN1 finalContent = new ASN1 (0xA0);
1304 finalContent.Add (encData);
1306 PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
1307 bag.Content = finalContent;
1311 public void AddCertificate (X509Certificate cert)
1313 AddCertificate (cert, null);
1316 public void AddCertificate (X509Certificate cert, IDictionary attributes)
1320 for (int i = 0; !found && 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)) {
1335 _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
1336 _certsChanged = true;
1340 public void RemoveCertificate (X509Certificate cert)
1342 RemoveCertificate (cert, null);
1345 public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
1349 for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
1350 SafeBag sb = (SafeBag)_safeBags [i];
1352 if (sb.BagOID.Equals (certBag)) {
1353 ASN1 safeBag = sb.ASN1;
1354 ASN1 bagValue = safeBag [1];
1355 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1356 X509Certificate c = new X509Certificate (crt.Content [0].Value);
1357 if (Compare (cert.RawData, c.RawData)) {
1358 if (attrs != null) {
1359 if (safeBag.Count == 3) {
1360 ASN1 bagAttributes = safeBag [2];
1361 int bagAttributesFound = 0;
1362 for (int j = 0; j < bagAttributes.Count; j++) {
1363 ASN1 pkcs12Attribute = bagAttributes [j];
1364 ASN1 attrId = pkcs12Attribute [0];
1365 string ao = ASN1Convert.ToOid (attrId);
1366 ArrayList dattrValues = (ArrayList)attrs [ao];
1368 if (dattrValues != null) {
1369 ASN1 attrValues = pkcs12Attribute [1];
1371 if (dattrValues.Count == attrValues.Count) {
1372 int attrValuesFound = 0;
1373 for (int k = 0; k < attrValues.Count; k++) {
1374 ASN1 attrValue = attrValues [k];
1375 byte[] value = (byte[])dattrValues [k];
1377 if (Compare (value, attrValue.Value)) {
1378 attrValuesFound += 1;
1381 if (attrValuesFound == attrValues.Count) {
1382 bagAttributesFound += 1;
1387 if (bagAttributesFound == bagAttributes.Count) {
1398 if (certIndex != -1) {
1399 _safeBags.RemoveAt (certIndex);
1400 _certsChanged = true;
1404 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
1407 if (a1.KeySize != a2.KeySize)
1409 // compare public keys - if they match we can assume the private match too
1410 return (a1.ToXmlString (false) == a2.ToXmlString (false));
1413 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1415 AddPkcs8ShroudedKeyBag (aa, null);
1418 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1422 for (int i = 0; !found && i < _safeBags.Count; i++) {
1423 SafeBag sb = (SafeBag)_safeBags [i];
1425 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1426 ASN1 bagValue = sb.ASN1 [1];
1427 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1428 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1429 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1430 byte[] privateKey = pki.PrivateKey;
1432 AsymmetricAlgorithm saa = null;
1433 switch (privateKey [0]) {
1435 DSAParameters p = new DSAParameters (); // FIXME
1436 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1439 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1442 Array.Clear (decrypted, 0, decrypted.Length);
1443 Array.Clear (privateKey, 0, privateKey.Length);
1444 throw new CryptographicException ("Unknown private key format");
1447 Array.Clear (decrypted, 0, decrypted.Length);
1448 Array.Clear (privateKey, 0, privateKey.Length);
1450 if (CompareAsymmetricAlgorithm (aa , saa)) {
1457 _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
1458 _keyBagsChanged = true;
1462 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1466 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1467 SafeBag sb = (SafeBag)_safeBags [i];
1469 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1470 ASN1 bagValue = sb.ASN1 [1];
1471 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1472 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1473 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1474 byte[] privateKey = pki.PrivateKey;
1476 AsymmetricAlgorithm saa = null;
1477 switch (privateKey [0]) {
1479 DSAParameters p = new DSAParameters (); // FIXME
1480 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1483 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1486 Array.Clear (decrypted, 0, decrypted.Length);
1487 Array.Clear (privateKey, 0, privateKey.Length);
1488 throw new CryptographicException ("Unknown private key format");
1491 Array.Clear (decrypted, 0, decrypted.Length);
1492 Array.Clear (privateKey, 0, privateKey.Length);
1494 if (CompareAsymmetricAlgorithm (aa, saa)) {
1500 if (aaIndex != -1) {
1501 _safeBags.RemoveAt (aaIndex);
1502 _keyBagsChanged = true;
1506 public void AddKeyBag (AsymmetricAlgorithm aa)
1508 AddKeyBag (aa, null);
1511 public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1515 for (int i = 0; !found && i < _safeBags.Count; i++) {
1516 SafeBag sb = (SafeBag)_safeBags [i];
1518 if (sb.BagOID.Equals (keyBag)) {
1519 ASN1 bagValue = sb.ASN1 [1];
1520 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1521 byte[] privateKey = pki.PrivateKey;
1523 AsymmetricAlgorithm saa = null;
1524 switch (privateKey [0]) {
1526 DSAParameters p = new DSAParameters (); // FIXME
1527 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1530 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1533 Array.Clear (privateKey, 0, privateKey.Length);
1534 throw new CryptographicException ("Unknown private key format");
1537 Array.Clear (privateKey, 0, privateKey.Length);
1539 if (CompareAsymmetricAlgorithm (aa, saa)) {
1546 _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
1547 _keyBagsChanged = true;
1551 public void RemoveKeyBag (AsymmetricAlgorithm aa)
1555 for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1556 SafeBag sb = (SafeBag)_safeBags [i];
1558 if (sb.BagOID.Equals (keyBag)) {
1559 ASN1 bagValue = sb.ASN1 [1];
1560 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1561 byte[] privateKey = pki.PrivateKey;
1563 AsymmetricAlgorithm saa = null;
1564 switch (privateKey [0]) {
1566 DSAParameters p = new DSAParameters (); // FIXME
1567 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1570 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1573 Array.Clear (privateKey, 0, privateKey.Length);
1574 throw new CryptographicException ("Unknown private key format");
1577 Array.Clear (privateKey, 0, privateKey.Length);
1579 if (CompareAsymmetricAlgorithm (aa, saa)) {
1585 if (aaIndex != -1) {
1586 _safeBags.RemoveAt (aaIndex);
1587 _keyBagsChanged = true;
1591 public void AddSecretBag (byte[] secret)
1593 AddSecretBag (secret, null);
1596 public void AddSecretBag (byte[] secret, IDictionary attributes)
1600 for (int i = 0; !found && i < _safeBags.Count; i++) {
1601 SafeBag sb = (SafeBag)_safeBags [i];
1603 if (sb.BagOID.Equals (secretBag)) {
1604 ASN1 bagValue = sb.ASN1 [1];
1605 byte[] ssecret = bagValue.Value;
1607 if (Compare (secret, ssecret)) {
1614 _safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes)));
1615 _secretBagsChanged = true;
1619 public void RemoveSecretBag (byte[] secret)
1623 for (int i = 0; sIndex == -1 && i < _safeBags.Count; i++) {
1624 SafeBag sb = (SafeBag)_safeBags [i];
1626 if (sb.BagOID.Equals (secretBag)) {
1627 ASN1 bagValue = sb.ASN1 [1];
1628 byte[] ssecret = bagValue.Value;
1630 if (Compare (secret, ssecret)) {
1637 _safeBags.RemoveAt (sIndex);
1638 _secretBagsChanged = true;
1642 public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
1644 foreach (SafeBag sb in _safeBags) {
1645 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1646 ASN1 safeBag = sb.ASN1;
1648 if (safeBag.Count == 3) {
1649 ASN1 bagAttributes = safeBag [2];
1651 int bagAttributesFound = 0;
1652 for (int i = 0; i < bagAttributes.Count; i++) {
1653 ASN1 pkcs12Attribute = bagAttributes [i];
1654 ASN1 attrId = pkcs12Attribute [0];
1655 string ao = ASN1Convert.ToOid (attrId);
1656 ArrayList dattrValues = (ArrayList)attrs [ao];
1658 if (dattrValues != null) {
1659 ASN1 attrValues = pkcs12Attribute [1];
1661 if (dattrValues.Count == attrValues.Count) {
1662 int attrValuesFound = 0;
1663 for (int j = 0; j < attrValues.Count; j++) {
1664 ASN1 attrValue = attrValues [j];
1665 byte[] value = (byte[])dattrValues [j];
1667 if (Compare (value, attrValue.Value)) {
1668 attrValuesFound += 1;
1671 if (attrValuesFound == attrValues.Count) {
1672 bagAttributesFound += 1;
1677 if (bagAttributesFound == bagAttributes.Count) {
1678 ASN1 bagValue = safeBag [1];
1679 AsymmetricAlgorithm aa = null;
1680 if (sb.BagOID.Equals (keyBag)) {
1681 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1682 byte[] privateKey = pki.PrivateKey;
1683 switch (privateKey [0]) {
1685 DSAParameters p = new DSAParameters (); // FIXME
1686 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1689 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1694 Array.Clear (privateKey, 0, privateKey.Length);
1695 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1696 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1697 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1698 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1699 byte[] privateKey = pki.PrivateKey;
1700 switch (privateKey [0]) {
1702 DSAParameters p = new DSAParameters (); // FIXME
1703 aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1706 aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1711 Array.Clear (privateKey, 0, privateKey.Length);
1712 Array.Clear (decrypted, 0, decrypted.Length);
1723 public byte[] GetSecret (IDictionary attrs)
1725 foreach (SafeBag sb in _safeBags) {
1726 if (sb.BagOID.Equals (secretBag)) {
1727 ASN1 safeBag = sb.ASN1;
1729 if (safeBag.Count == 3) {
1730 ASN1 bagAttributes = safeBag [2];
1732 int bagAttributesFound = 0;
1733 for (int i = 0; i < bagAttributes.Count; i++) {
1734 ASN1 pkcs12Attribute = bagAttributes [i];
1735 ASN1 attrId = pkcs12Attribute [0];
1736 string ao = ASN1Convert.ToOid (attrId);
1737 ArrayList dattrValues = (ArrayList)attrs [ao];
1739 if (dattrValues != null) {
1740 ASN1 attrValues = pkcs12Attribute [1];
1742 if (dattrValues.Count == attrValues.Count) {
1743 int attrValuesFound = 0;
1744 for (int j = 0; j < attrValues.Count; j++) {
1745 ASN1 attrValue = attrValues [j];
1746 byte[] value = (byte[])dattrValues [j];
1748 if (Compare (value, attrValue.Value)) {
1749 attrValuesFound += 1;
1752 if (attrValuesFound == attrValues.Count) {
1753 bagAttributesFound += 1;
1758 if (bagAttributesFound == bagAttributes.Count) {
1759 ASN1 bagValue = safeBag [1];
1760 return bagValue.Value;
1769 public X509Certificate GetCertificate (IDictionary attrs)
1771 foreach (SafeBag sb in _safeBags) {
1772 if (sb.BagOID.Equals (certBag)) {
1773 ASN1 safeBag = sb.ASN1;
1775 if (safeBag.Count == 3) {
1776 ASN1 bagAttributes = safeBag [2];
1778 int bagAttributesFound = 0;
1779 for (int i = 0; i < bagAttributes.Count; i++) {
1780 ASN1 pkcs12Attribute = bagAttributes [i];
1781 ASN1 attrId = pkcs12Attribute [0];
1782 string ao = ASN1Convert.ToOid (attrId);
1783 ArrayList dattrValues = (ArrayList)attrs [ao];
1785 if (dattrValues != null) {
1786 ASN1 attrValues = pkcs12Attribute [1];
1788 if (dattrValues.Count == attrValues.Count) {
1789 int attrValuesFound = 0;
1790 for (int j = 0; j < attrValues.Count; j++) {
1791 ASN1 attrValue = attrValues [j];
1792 byte[] value = (byte[])dattrValues [j];
1794 if (Compare (value, attrValue.Value)) {
1795 attrValuesFound += 1;
1798 if (attrValuesFound == attrValues.Count) {
1799 bagAttributesFound += 1;
1804 if (bagAttributesFound == bagAttributes.Count) {
1805 ASN1 bagValue = safeBag [1];
1806 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1807 return new X509Certificate (crt.Content [0].Value);
1816 public IDictionary GetAttributes (AsymmetricAlgorithm aa)
1818 IDictionary result = new Hashtable ();
1820 foreach (SafeBag sb in _safeBags) {
1821 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1822 ASN1 safeBag = sb.ASN1;
1824 ASN1 bagValue = safeBag [1];
1825 AsymmetricAlgorithm saa = null;
1826 if (sb.BagOID.Equals (keyBag)) {
1827 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1828 byte[] privateKey = pki.PrivateKey;
1829 switch (privateKey [0]) {
1831 DSAParameters p = new DSAParameters (); // FIXME
1832 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1835 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1840 Array.Clear (privateKey, 0, privateKey.Length);
1841 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1842 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1843 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1844 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1845 byte[] privateKey = pki.PrivateKey;
1846 switch (privateKey [0]) {
1848 DSAParameters p = new DSAParameters (); // FIXME
1849 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1852 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1857 Array.Clear (privateKey, 0, privateKey.Length);
1858 Array.Clear (decrypted, 0, decrypted.Length);
1861 if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
1862 if (safeBag.Count == 3) {
1863 ASN1 bagAttributes = safeBag [2];
1865 for (int i = 0; i < bagAttributes.Count; i++) {
1866 ASN1 pkcs12Attribute = bagAttributes [i];
1867 ASN1 attrId = pkcs12Attribute [0];
1868 string aOid = ASN1Convert.ToOid (attrId);
1869 ArrayList aValues = new ArrayList ();
1871 ASN1 attrValues = pkcs12Attribute [1];
1873 for (int j = 0; j < attrValues.Count; j++) {
1874 ASN1 attrValue = attrValues [j];
1875 aValues.Add (attrValue.Value);
1877 result.Add (aOid, aValues);
1887 public IDictionary GetAttributes (X509Certificate cert)
1889 IDictionary result = new Hashtable ();
1891 foreach (SafeBag sb in _safeBags) {
1892 if (sb.BagOID.Equals (certBag)) {
1893 ASN1 safeBag = sb.ASN1;
1894 ASN1 bagValue = safeBag [1];
1895 PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1896 X509Certificate xc = new X509Certificate (crt.Content [0].Value);
1898 if (Compare (cert.RawData, xc.RawData)) {
1899 if (safeBag.Count == 3) {
1900 ASN1 bagAttributes = safeBag [2];
1902 for (int i = 0; i < bagAttributes.Count; i++) {
1903 ASN1 pkcs12Attribute = bagAttributes [i];
1904 ASN1 attrId = pkcs12Attribute [0];
1906 string aOid = ASN1Convert.ToOid (attrId);
1907 ArrayList aValues = new ArrayList ();
1909 ASN1 attrValues = pkcs12Attribute [1];
1911 for (int j = 0; j < attrValues.Count; j++) {
1912 ASN1 attrValue = attrValues [j];
1913 aValues.Add (attrValue.Value);
1915 result.Add (aOid, aValues);
1925 public void SaveToFile (string filename)
1927 if (filename == null)
1928 throw new ArgumentNullException ("filename");
1930 using (FileStream fs = File.Create (filename)) {
1931 byte[] data = GetBytes ();
1932 fs.Write (data, 0, data.Length);
1936 public object Clone ()
1938 PKCS12 clone = null;
1939 if (_password != null) {
1940 clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
1942 clone = new PKCS12 (GetBytes ());
1944 clone.IterationCount = this.IterationCount;
1951 public const int CryptoApiPasswordLimit = 32;
1953 static private int password_max_length = Int32.MaxValue;
1955 // static properties
1957 // MS CryptoAPI limits the password to a maximum of 31 characters
1958 // other implementations, like OpenSSL, have no such limitation.
1959 // Setting a maximum value will truncate the password length to
1960 // ensure compatibility with MS's PFXImportCertStore API.
1961 static public int MaximumPasswordLength {
1962 get { return password_max_length; }
1964 if (value < CryptoApiPasswordLimit) {
1965 string msg = Locale.GetText ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit);
1966 throw new ArgumentOutOfRangeException (msg);
1968 password_max_length = value;
1974 static private byte[] LoadFile (string filename)
1977 using (FileStream fs = File.OpenRead (filename)) {
1978 data = new byte [fs.Length];
1979 fs.Read (data, 0, data.Length);
1985 static public PKCS12 LoadFromFile (string filename)
1987 if (filename == null)
1988 throw new ArgumentNullException ("filename");
1990 return new PKCS12 (LoadFile (filename));
1993 static public PKCS12 LoadFromFile (string filename, string password)
1995 if (filename == null)
1996 throw new ArgumentNullException ("filename");
1998 return new PKCS12 (LoadFile (filename), password);