2 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com)
10 // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
11 // See bouncycastle.txt for license.
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
36 using System
.Security
.Cryptography
;
40 using Mono
.Security
.Cryptography
;
42 namespace Mono
.Security
.X509
{
51 public const string pbeWithMD2AndDESCBC
= "1.2.840.113549.1.5.1";
52 public const string pbeWithMD5AndDESCBC
= "1.2.840.113549.1.5.3";
53 public const string pbeWithMD2AndRC2CBC
= "1.2.840.113549.1.5.4";
54 public const string pbeWithMD5AndRC2CBC
= "1.2.840.113549.1.5.6";
55 public const string pbeWithSHA1AndDESCBC
= "1.2.840.113549.1.5.10";
56 public const string pbeWithSHA1AndRC2CBC
= "1.2.840.113549.1.5.11";
68 public const string friendlyName
= "1.2.840.113549.1.9.20";
69 public const string localKeyId
= "1.2.840.113549.1.9.21";
75 internal class SafeBag
{
76 private string _bagOID
;
79 public SafeBag(string bagOID
, ASN1 asn1
) {
84 public string BagOID
{
85 get { return _bagOID; }
99 class PKCS12
: ICloneable
{
101 public const string pbeWithSHAAnd128BitRC4
= "1.2.840.113549.1.12.1.1";
102 public const string pbeWithSHAAnd40BitRC4
= "1.2.840.113549.1.12.1.2";
103 public const string pbeWithSHAAnd3KeyTripleDESCBC
= "1.2.840.113549.1.12.1.3";
104 public const string pbeWithSHAAnd2KeyTripleDESCBC
= "1.2.840.113549.1.12.1.4";
105 public const string pbeWithSHAAnd128BitRC2CBC
= "1.2.840.113549.1.12.1.5";
106 public const string pbeWithSHAAnd40BitRC2CBC
= "1.2.840.113549.1.12.1.6";
109 public const string keyBag
= "1.2.840.113549.1.12.10.1.1";
110 public const string pkcs8ShroudedKeyBag
= "1.2.840.113549.1.12.10.1.2";
111 public const string certBag
= "1.2.840.113549.1.12.10.1.3";
112 public const string crlBag
= "1.2.840.113549.1.12.10.1.4";
113 public const string secretBag
= "1.2.840.113549.1.12.10.1.5";
114 public const string safeContentsBag
= "1.2.840.113549.1.12.10.1.6";
117 public const string x509Certificate
= "1.2.840.113549.1.9.22.1";
118 public const string sdsiCertificate
= "1.2.840.113549.1.9.22.2";
119 public const string x509Crl
= "1.2.840.113549.1.9.23.1";
121 // Adapted from BouncyCastle PKCS12ParametersGenerator.java
122 public class DeriveBytes
{
124 public enum Purpose
{
130 static private byte[] keyDiversifier
= { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }
;
131 static private byte[] ivDiversifier
= { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 }
;
132 static private byte[] macDiversifier
= { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 }
;
134 private string _hashName
;
135 private int _iterations
;
136 private byte[] _password
;
137 private byte[] _salt
;
139 public DeriveBytes () {}
141 public string HashName
{
142 get { return _hashName; }
143 set { _hashName = value; }
146 public int IterationCount
{
147 get { return _iterations; }
148 set { _iterations = value; }
151 public byte[] Password
{
152 get { return (byte[]) _password.Clone (); }
155 _password
= new byte [0];
157 _password
= (byte[]) value.Clone ();
162 get { return (byte[]) _salt.Clone (); }
165 _salt
= (byte[]) value.Clone ();
171 private void Adjust (byte[] a
, int aOff
, byte[] b
)
173 int x
= (b
[b
.Length
- 1] & 0xff) + (a
[aOff
+ b
.Length
- 1] & 0xff) + 1;
175 a
[aOff
+ b
.Length
- 1] = (byte) x
;
178 for (int i
= b
.Length
- 2; i
>= 0; i
--) {
179 x
+= (b
[i
] & 0xff) + (a
[aOff
+ i
] & 0xff);
180 a
[aOff
+ i
] = (byte) x
;
185 private byte[] Derive (byte[] diversifier
, int n
)
187 HashAlgorithm digest
= HashAlgorithm
.Create (_hashName
);
188 int u
= (digest
.HashSize
>> 3); // div 8
190 byte[] dKey
= new byte [n
];
193 if ((_salt
!= null) && (_salt
.Length
!= 0)) {
194 S
= new byte[v
* ((_salt
.Length
+ v
- 1) / v
)];
196 for (int i
= 0; i
!= S
.Length
; i
++) {
197 S
[i
] = _salt
[i
% _salt
.Length
];
205 if ((_password
!= null) && (_password
.Length
!= 0)) {
206 P
= new byte[v
* ((_password
.Length
+ v
- 1) / v
)];
208 for (int i
= 0; i
!= P
.Length
; i
++) {
209 P
[i
] = _password
[i
% _password
.Length
];
216 byte[] I
= new byte [S
.Length
+ P
.Length
];
218 Buffer
.BlockCopy (S
, 0, I
, 0, S
.Length
);
219 Buffer
.BlockCopy (P
, 0, I
, S
.Length
, P
.Length
);
221 byte[] B
= new byte[v
];
222 int c
= (n
+ u
- 1) / u
;
224 for (int i
= 1; i
<= c
; i
++) {
225 digest
.TransformBlock (diversifier
, 0, diversifier
.Length
, diversifier
, 0);
226 digest
.TransformFinalBlock (I
, 0, I
.Length
);
227 byte[] A
= digest
.Hash
;
228 digest
.Initialize ();
229 for (int j
= 1; j
!= _iterations
; j
++) {
230 A
= digest
.ComputeHash (A
, 0, A
.Length
);
233 for (int j
= 0; j
!= B
.Length
; j
++) {
234 B
[j
] = A
[j
% A
.Length
];
237 for (int j
= 0; j
!= I
.Length
/ v
; j
++) {
238 Adjust (I
, j
* v
, B
);
242 Buffer
.BlockCopy(A
, 0, dKey
, (i
- 1) * u
, dKey
.Length
- ((i
- 1) * u
));
245 Buffer
.BlockCopy(A
, 0, dKey
, (i
- 1) * u
, A
.Length
);
252 public byte[] DeriveKey (int size
)
254 return Derive (keyDiversifier
, size
);
257 public byte[] DeriveIV (int size
)
259 return Derive (ivDiversifier
, size
);
262 public byte[] DeriveMAC (int size
)
264 return Derive (macDiversifier
, size
);
268 static private int recommendedIterationCount
= 2000;
270 //private int _version;
271 private byte[] _password
;
272 private ArrayList _keyBags
;
273 private 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
;
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
)
305 * version INTEGER {v3(3)}(v3,...),
306 * authSafe ContentInfo,
307 * macData MacData OPTIONAL
310 * MacData ::= SEQUENCE {
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
)
333 public PKCS12 (byte[] data
, byte[] password
)
336 _password
= password
;
340 private void Decode (byte[] data
)
342 ASN1 pfx
= new ASN1 (data
);
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
358 ASN1 macData
= pfx
[2];
359 if (macData
.Tag
!= 0x30)
360 throw new ArgumentException ("invalid MAC");
362 ASN1 mac
= macData
[0];
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
) {
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
);
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
);
411 case PKCS7
.Oid
.envelopedData
:
412 // public key encrypted
413 throw new NotImplementedException ("public key encrypted");
415 throw new ArgumentException ("unknown authenticatedSafe");
422 if (_password
!= null) {
423 Array
.Clear (_password
, 0, _password
.Length
);
430 public string Password
{
433 if (value.Length
> 0) {
434 int size
= value.Length
;
436 if (size
< MaximumPasswordLength
) {
437 // if not present, add space for a NULL (0x00) character
438 if (value[size
- 1] != 0x00)
441 size
= MaximumPasswordLength
;
443 _password
= new byte[(size
+ nul
) << 1]; // double for unicode
444 Encoding
.BigEndianUnicode
.GetBytes (value, 0, size
, _password
, 0);
446 // double-byte (Unicode) NULL (0x00) - see bug #79617
447 _password
= new byte[2];
456 public int IterationCount
{
457 get { return _iterations; }
458 set { _iterations = value; }
461 public ArrayList Keys
{
463 if (_keyBagsChanged
) {
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]) {
473 DSAParameters p
= new DSAParameters (); // FIXME
474 _keyBags
.Add (PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
));
477 _keyBags
.Add (PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
));
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]) {
493 DSAParameters p
= new DSAParameters (); // FIXME
494 _keyBags
.Add (PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
));
497 _keyBags
.Add (PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
));
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
{
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
{
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;
548 internal RandomNumberGenerator RNG
{
551 _rng
= RandomNumberGenerator
.Create ();
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
])
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
;
580 pd
.IterationCount
= iterationCount
;
582 switch (algorithmOid
) {
583 case PKCS5
.pbeWithMD2AndDESCBC
: // no unit test available
587 case PKCS5
.pbeWithMD5AndDESCBC
: // no unit test available
591 case PKCS5
.pbeWithMD2AndRC2CBC
: // no unit test available
592 // TODO - RC2-CBC-Parameter (PKCS5)
593 // if missing default to 32 bits !!!
596 keyLength
= 4; // default
598 case PKCS5
.pbeWithMD5AndRC2CBC
: // no unit test available
599 // TODO - RC2-CBC-Parameter (PKCS5)
600 // if missing default to 32 bits !!!
603 keyLength
= 4; // default
605 case PKCS5
.pbeWithSHA1AndDESCBC
: // no unit test available
606 pd
.HashName
= "SHA1";
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";
614 keyLength
= 4; // default
616 case PKCS12
.pbeWithSHAAnd128BitRC4
: // no unit test available
617 pd
.HashName
= "SHA1";
622 case PKCS12
.pbeWithSHAAnd40BitRC4
: // no unit test available
623 pd
.HashName
= "SHA1";
628 case PKCS12
.pbeWithSHAAnd3KeyTripleDESCBC
:
629 pd
.HashName
= "SHA1";
630 algorithm
= "TripleDES";
633 case PKCS12
.pbeWithSHAAnd2KeyTripleDESCBC
: // no unit test available
634 pd
.HashName
= "SHA1";
635 algorithm
= "TripleDES";
638 case PKCS12
.pbeWithSHAAnd128BitRC2CBC
: // no unit test available
639 pd
.HashName
= "SHA1";
643 case PKCS12
.pbeWithSHAAnd40BitRC2CBC
:
644 pd
.HashName
= "SHA1";
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)
656 sa
.IV
= pd
.DeriveIV (ivLength
);
657 sa
.Mode
= CipherMode
.CBC
;
662 public byte[] Decrypt (string algorithmOid
, byte[] salt
, int iterationCount
, byte[] encryptedData
)
664 SymmetricAlgorithm sa
= null;
665 byte[] result
= null;
667 sa
= GetSymmetricAlgorithm (algorithmOid
, salt
, iterationCount
);
668 ICryptoTransform ct
= sa
.CreateDecryptor ();
669 result
= ct
.TransformFinalBlock (encryptedData
, 0, encryptedData
.Length
);
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
);
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) {
704 return dsa
.ExportParameters (false);
709 return new DSAParameters ();
712 private void AddPrivateKey (PKCS8
.PrivateKeyInfo pki
)
714 byte[] privateKey
= pki
.PrivateKey
;
715 switch (privateKey
[0]) {
718 DSAParameters p
= GetExistingParameters (out found
);
720 _keyBags
.Add (PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
));
724 _keyBags
.Add (PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
));
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
);
747 AddPrivateKey (new PKCS8
.PrivateKeyInfo (bagValue
.Value
));
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
);
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
);
766 byte[] secret
= bagValue
.Value
;
767 _secretBags
.Add(secret
);
769 case safeContentsBag
:
770 // TODO - ? recurse ?
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
];
798 case PKCS9
.friendlyName
:
799 if (attrValue
.Tag
!= 0x1e)
800 throw new ArgumentException ("invalid attribute value id");
802 case PKCS9
.localKeyId
:
803 if (attrValue
.Tag
!= 0x04)
804 throw new ArgumentException ("invalid attribute value id");
807 // Unknown OID -- don't check Tag
814 _safeBags
.Add (new SafeBag(oid
, safeBag
));
817 private ASN1
Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa
, IDictionary attributes
)
819 PKCS8
.PrivateKeyInfo pki
= new PKCS8
.PrivateKeyInfo ();
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
);
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
;
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
);
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
);
884 if (bagAttributes
.Count
> 0) {
885 safeBag
.Add (bagAttributes
);
892 private ASN1
KeyBagSafeBag (AsymmetricAlgorithm aa
, IDictionary attributes
)
894 PKCS8
.PrivateKeyInfo pki
= new PKCS8
.PrivateKeyInfo ();
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
);
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
;
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
);
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
);
954 if (bagAttributes
.Count
> 0) {
955 safeBag
.Add (bagAttributes
);
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
;
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
);
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
);
1011 if (bagAttributes
.Count
> 0) {
1012 safeBag
.Add (bagAttributes
);
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
;
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
);
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
);
1076 if (bagAttributes
.Count
> 0) {
1077 safeBag
.Add (bagAttributes
);
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
;
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
) {
1128 foreach (X509Certificate lc
in scs
) {
1129 if (Compare (c
.RawData
, lc
.RawData
)) {
1138 foreach (X509Certificate c
in scs
) {
1141 foreach (X509Certificate lc
in Certificates
) {
1142 if (Compare (c
.RawData
, lc
.RawData
)) {
1148 removecerts
.Add (c
);
1152 foreach (X509Certificate c
in removecerts
) {
1153 RemoveCertificate (c
);
1156 foreach (X509Certificate c
in addcerts
) {
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);
1227 mac
.Add (new ASN1 (0x04, macValue
));
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);
1238 pfx
.Add (authSafe
.ASN1
);
1239 if (macData
.Count
> 0) {
1240 // only for password based encryption
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
));
1267 seq
.Add (encryptedContent
);
1269 ASN1 version
= new ASN1 (0x02, new byte [1] { 0x00 }
);
1270 ASN1 encData
= new ASN1 (0x30);
1271 encData
.Add (version
);
1274 ASN1 finalContent
= new ASN1 (0xA0);
1275 finalContent
.Add (encData
);
1277 PKCS7
.ContentInfo bag
= new PKCS7
.ContentInfo (PKCS7
.Oid
.encryptedData
);
1278 bag
.Content
= finalContent
;
1282 public void AddCertificate (X509Certificate cert
)
1284 AddCertificate (cert
, null);
1287 public void AddCertificate (X509Certificate cert
, IDictionary attributes
)
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
)) {
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
)
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
) {
1369 if (certIndex
!= -1) {
1370 _safeBags
.RemoveAt (certIndex
);
1371 _certsChanged
= true;
1375 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1
, AsymmetricAlgorithm a2
)
1378 if (a1
.KeySize
!= a2
.KeySize
)
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
)
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]) {
1406 DSAParameters p
= new DSAParameters (); // FIXME
1407 saa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1410 saa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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
)) {
1428 _safeBags
.Add (new SafeBag (pkcs8ShroudedKeyBag
, Pkcs8ShroudedKeyBagSafeBag (aa
, attributes
)));
1429 _keyBagsChanged
= true;
1433 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa
)
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]) {
1450 DSAParameters p
= new DSAParameters (); // FIXME
1451 saa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1454 saa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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
)) {
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
)
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]) {
1497 DSAParameters p
= new DSAParameters (); // FIXME
1498 saa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1501 saa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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
)) {
1517 _safeBags
.Add (new SafeBag (keyBag
, KeyBagSafeBag (aa
, attributes
)));
1518 _keyBagsChanged
= true;
1522 public void RemoveKeyBag (AsymmetricAlgorithm aa
)
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]) {
1537 DSAParameters p
= new DSAParameters (); // FIXME
1538 saa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1541 saa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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
)) {
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
)
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
)) {
1585 _safeBags
.Add (new SafeBag (secretBag
, SecretBagSafeBag (secret
, attributes
)));
1586 _secretBagsChanged
= true;
1590 public void RemoveSecretBag (byte[] secret
)
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
)) {
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]) {
1656 DSAParameters p
= new DSAParameters (); // FIXME
1657 aa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1660 aa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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]) {
1673 DSAParameters p
= new DSAParameters (); // FIXME
1674 aa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1677 aa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
1682 Array
.Clear (privateKey
, 0, privateKey
.Length
);
1683 Array
.Clear (decrypted
, 0, decrypted
.Length
);
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
;
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
);
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]) {
1802 DSAParameters p
= new DSAParameters (); // FIXME
1803 saa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1806 saa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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]) {
1819 DSAParameters p
= new DSAParameters (); // FIXME
1820 saa
= PKCS8
.PrivateKeyInfo
.DecodeDSA (privateKey
, p
);
1823 saa
= PKCS8
.PrivateKeyInfo
.DecodeRSA (privateKey
);
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
);
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
);
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
));
1913 clone
= new PKCS12 (GetBytes ());
1915 clone
.IterationCount
= this.IterationCount
;
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; }
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;
1945 static private byte[] LoadFile (string filename
)
1948 using (FileStream fs
= File
.OpenRead (filename
)) {
1949 data
= new byte [fs
.Length
];
1950 fs
.Read (data
, 0, data
.Length
);
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
);