2 // PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard
3 // ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
9 // (C) 2004 Novell (http://www.novell.com)
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
;
35 using System
.Security
.Cryptography
;
38 using Mono
.Security
.Cryptography
;
39 using Mono
.Security
.X509
;
41 namespace Mono
.Security
.Cryptography
{
43 public sealed class PKCS8
{
55 static public KeyInfo
GetType (byte[] data
)
58 throw new ArgumentNullException ("data");
60 KeyInfo ki
= KeyInfo
.Unknown
;
62 ASN1 top
= new ASN1 (data
);
63 if ((top
.Tag
== 0x30) && (top
.Count
> 0)) {
64 ASN1 firstLevel
= top
[0];
65 switch (firstLevel
.Tag
) {
67 ki
= KeyInfo
.PrivateKey
;
70 ki
= KeyInfo
.EncryptedPrivateKey
;
76 throw new CryptographicException ("invalid ASN.1 data");
82 * PrivateKeyInfo ::= SEQUENCE {
84 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
85 * privateKey PrivateKey,
86 * attributes [0] IMPLICIT Attributes OPTIONAL
91 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
93 * PrivateKey ::= OCTET STRING
95 * Attributes ::= SET OF Attribute
97 public class PrivateKeyInfo
{
100 private string _algorithm
;
102 private ArrayList _list
;
104 public PrivateKeyInfo ()
107 _list
= new ArrayList ();
110 public PrivateKeyInfo (byte[] data
) : this ()
117 public string Algorithm
{
118 get { return _algorithm; }
119 set { _algorithm = value; }
122 public ArrayList Attributes
{
123 get { return _list; }
126 public byte[] PrivateKey
{
130 return (byte[]) _key
.Clone ();
134 throw new ArgumentNullException ("PrivateKey");
135 _key
= (byte[]) value.Clone ();
140 get { return _version; }
143 throw new ArgumentOutOfRangeException ("negative version");
150 private void Decode (byte[] data
)
152 ASN1 privateKeyInfo
= new ASN1 (data
);
153 if (privateKeyInfo
.Tag
!= 0x30)
154 throw new CryptographicException ("invalid PrivateKeyInfo");
156 ASN1 version
= privateKeyInfo
[0];
157 if (version
.Tag
!= 0x02)
158 throw new CryptographicException ("invalid version");
159 _version
= version
.Value
[0];
161 ASN1 privateKeyAlgorithm
= privateKeyInfo
[1];
162 if (privateKeyAlgorithm
.Tag
!= 0x30)
163 throw new CryptographicException ("invalid algorithm");
165 ASN1 algorithm
= privateKeyAlgorithm
[0];
166 if (algorithm
.Tag
!= 0x06)
167 throw new CryptographicException ("missing algorithm OID");
168 _algorithm
= ASN1Convert
.ToOid (algorithm
);
170 ASN1 privateKey
= privateKeyInfo
[2];
171 _key
= privateKey
.Value
;
173 // attributes [0] IMPLICIT Attributes OPTIONAL
174 if (privateKeyInfo
.Count
> 3) {
175 ASN1 attributes
= privateKeyInfo
[3];
176 for (int i
=0; i
< attributes
.Count
; i
++) {
177 _list
.Add (attributes
[i
]);
182 public byte[] GetBytes ()
184 ASN1 privateKeyAlgorithm
= new ASN1 (0x30);
185 privateKeyAlgorithm
.Add (ASN1Convert
.FromOid (_algorithm
));
186 privateKeyAlgorithm
.Add (new ASN1 (0x05)); // ASN.1 NULL
188 ASN1 pki
= new ASN1 (0x30);
189 pki
.Add (new ASN1 (0x02, new byte [1] { (byte) _version }
));
190 pki
.Add (privateKeyAlgorithm
);
191 pki
.Add (new ASN1 (0x04, _key
));
193 if (_list
.Count
> 0) {
194 ASN1 attributes
= new ASN1 (0xA0);
195 foreach (ASN1 attribute
in _list
) {
196 attributes
.Add (attribute
);
198 pki
.Add (attributes
);
201 return pki
.GetBytes ();
206 static private byte[] RemoveLeadingZero (byte[] bigInt
)
209 int length
= bigInt
.Length
;
210 if (bigInt
[0] == 0x00) {
214 byte[] bi
= new byte [length
];
215 Buffer
.BlockCopy (bigInt
, start
, bi
, 0, length
);
219 static private byte[] Normalize (byte[] bigInt
, int length
)
221 if (bigInt
.Length
== length
)
223 else if (bigInt
.Length
> length
)
224 return RemoveLeadingZero (bigInt
);
227 byte[] bi
= new byte [length
];
228 Buffer
.BlockCopy (bigInt
, 0, bi
, (length
- bigInt
.Length
), bigInt
.Length
);
234 * RSAPrivateKey ::= SEQUENCE {
236 * modulus INTEGER, -- n
237 * publicExponent INTEGER, -- e
238 * privateExponent INTEGER, -- d
239 * prime1 INTEGER, -- p
240 * prime2 INTEGER, -- q
241 * exponent1 INTEGER, -- d mod (p-1)
242 * exponent2 INTEGER, -- d mod (q-1)
243 * coefficient INTEGER, -- (inverse of q) mod p
244 * otherPrimeInfos OtherPrimeInfos OPTIONAL
247 static public RSA
DecodeRSA (byte[] keypair
)
249 ASN1 privateKey
= new ASN1 (keypair
);
250 if (privateKey
.Tag
!= 0x30)
251 throw new CryptographicException ("invalid private key format");
253 ASN1 version
= privateKey
[0];
254 if (version
.Tag
!= 0x02)
255 throw new CryptographicException ("missing version");
257 if (privateKey
.Count
< 9)
258 throw new CryptographicException ("not enough key parameters");
260 RSAParameters param
= new RSAParameters ();
261 // note: MUST remove leading 0 - else MS wont import the key
262 param
.Modulus
= RemoveLeadingZero (privateKey
[1].Value
);
263 int keysize
= param
.Modulus
.Length
;
264 int keysize2
= (keysize
>> 1); // half-size
265 // size must be normalized - else MS wont import the key
266 param
.D
= Normalize (privateKey
[3].Value
, keysize
);
267 param
.DP
= Normalize (privateKey
[6].Value
, keysize2
);
268 param
.DQ
= Normalize (privateKey
[7].Value
, keysize2
);
269 param
.Exponent
= RemoveLeadingZero (privateKey
[2].Value
);
270 param
.InverseQ
= Normalize (privateKey
[8].Value
, keysize2
);
271 param
.P
= Normalize (privateKey
[4].Value
, keysize2
);
272 param
.Q
= Normalize (privateKey
[5].Value
, keysize2
);
274 RSA rsa
= RSA
.Create ();
275 rsa
.ImportParameters (param
);
280 * RSAPrivateKey ::= SEQUENCE {
282 * modulus INTEGER, -- n
283 * publicExponent INTEGER, -- e
284 * privateExponent INTEGER, -- d
285 * prime1 INTEGER, -- p
286 * prime2 INTEGER, -- q
287 * exponent1 INTEGER, -- d mod (p-1)
288 * exponent2 INTEGER, -- d mod (q-1)
289 * coefficient INTEGER, -- (inverse of q) mod p
290 * otherPrimeInfos OtherPrimeInfos OPTIONAL
293 static public byte[] Encode (RSA rsa
)
295 RSAParameters param
= rsa
.ExportParameters (true);
297 ASN1 rsaPrivateKey
= new ASN1 (0x30);
298 rsaPrivateKey
.Add (new ASN1 (0x02, new byte [1] { 0x00 }
));
299 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.Modulus
));
300 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.Exponent
));
301 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.D
));
302 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.P
));
303 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.Q
));
304 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.DP
));
305 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.DQ
));
306 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.InverseQ
));
308 return rsaPrivateKey
.GetBytes ();
311 // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
312 // which isn't enough for rebuilding the keypair. The other parameters
313 // can be found (98% of the time) in the X.509 certificate associated
314 // with the private key or (2% of the time) the parameters are in it's
315 // issuer X.509 certificate (not supported in the .NET framework).
316 static public DSA
DecodeDSA (byte[] privateKey
, DSAParameters dsaParameters
)
318 ASN1 pvk
= new ASN1 (privateKey
);
320 throw new CryptographicException ("invalid private key format");
322 // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
323 dsaParameters
.X
= Normalize (privateKey
, 20);
324 DSA dsa
= DSA
.Create ();
325 dsa
.ImportParameters (dsaParameters
);
329 static public byte[] Encode (DSA dsa
)
331 DSAParameters param
= dsa
.ExportParameters (true);
332 return ASN1Convert
.FromUnsignedBigInteger (param
.X
).GetBytes ();
335 static public byte[] Encode (AsymmetricAlgorithm aa
)
338 return Encode ((RSA
)aa
);
340 return Encode ((DSA
)aa
);
342 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa
.ToString ());
347 * EncryptedPrivateKeyInfo ::= SEQUENCE {
348 * encryptionAlgorithm EncryptionAlgorithmIdentifier,
349 * encryptedData EncryptedData
352 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
354 * EncryptedData ::= OCTET STRING
357 * AlgorithmIdentifier ::= SEQUENCE {
358 * algorithm OBJECT IDENTIFIER,
359 * parameters ANY DEFINED BY algorithm OPTIONAL
363 * PBEParameter ::= SEQUENCE {
364 * salt OCTET STRING SIZE(8),
365 * iterationCount INTEGER
368 public class EncryptedPrivateKeyInfo
{
370 private string _algorithm
;
371 private byte[] _salt
;
372 private int _iterations
;
373 private byte[] _data
;
375 public EncryptedPrivateKeyInfo () {}
377 public EncryptedPrivateKeyInfo (byte[] data
) : this ()
384 public string Algorithm
{
385 get { return _algorithm; }
386 set { _algorithm = value; }
389 public byte[] EncryptedData
{
390 get { return (_data == null) ? null : (byte[]) _data.Clone (); }
391 set { _data = (value == null) ? null : (byte[]) value.Clone (); }
397 RandomNumberGenerator rng
= RandomNumberGenerator
.Create ();
398 _salt
= new byte [8];
399 rng
.GetBytes (_salt
);
401 return (byte[]) _salt
.Clone ();
403 set { _salt = (byte[]) value.Clone (); }
406 public int IterationCount
{
407 get { return _iterations; }
410 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
417 private void Decode (byte[] data
)
419 ASN1 encryptedPrivateKeyInfo
= new ASN1 (data
);
420 if (encryptedPrivateKeyInfo
.Tag
!= 0x30)
421 throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
423 ASN1 encryptionAlgorithm
= encryptedPrivateKeyInfo
[0];
424 if (encryptionAlgorithm
.Tag
!= 0x30)
425 throw new CryptographicException ("invalid encryptionAlgorithm");
426 ASN1 algorithm
= encryptionAlgorithm
[0];
427 if (algorithm
.Tag
!= 0x06)
428 throw new CryptographicException ("invalid algorithm");
429 _algorithm
= ASN1Convert
.ToOid (algorithm
);
430 // parameters ANY DEFINED BY algorithm OPTIONAL
431 if (encryptionAlgorithm
.Count
> 1) {
432 ASN1 parameters
= encryptionAlgorithm
[1];
433 if (parameters
.Tag
!= 0x30)
434 throw new CryptographicException ("invalid parameters");
436 ASN1 salt
= parameters
[0];
437 if (salt
.Tag
!= 0x04)
438 throw new CryptographicException ("invalid salt");
441 ASN1 iterationCount
= parameters
[1];
442 if (iterationCount
.Tag
!= 0x02)
443 throw new CryptographicException ("invalid iterationCount");
444 _iterations
= ASN1Convert
.ToInt32 (iterationCount
);
447 ASN1 encryptedData
= encryptedPrivateKeyInfo
[1];
448 if (encryptedData
.Tag
!= 0x04)
449 throw new CryptographicException ("invalid EncryptedData");
450 _data
= encryptedData
.Value
;
453 // Note: PKCS#8 doesn't define how to generate the key required for encryption
454 // so you're on your own. Just don't try to copy the big guys too much ;)
455 // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
456 // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
457 public byte[] GetBytes ()
459 if (_algorithm
== null)
460 throw new CryptographicException ("No algorithm OID specified");
462 ASN1 encryptionAlgorithm
= new ASN1 (0x30);
463 encryptionAlgorithm
.Add (ASN1Convert
.FromOid (_algorithm
));
465 // parameters ANY DEFINED BY algorithm OPTIONAL
466 if ((_iterations
> 0) || (_salt
!= null)) {
467 ASN1 salt
= new ASN1 (0x04, _salt
);
468 ASN1 iterations
= ASN1Convert
.FromInt32 (_iterations
);
470 ASN1 parameters
= new ASN1 (0x30);
471 parameters
.Add (salt
);
472 parameters
.Add (iterations
);
473 encryptionAlgorithm
.Add (parameters
);
476 // encapsulates EncryptedData into an OCTET STRING
477 ASN1 encryptedData
= new ASN1 (0x04, _data
);
479 ASN1 encryptedPrivateKeyInfo
= new ASN1 (0x30);
480 encryptedPrivateKeyInfo
.Add (encryptionAlgorithm
);
481 encryptedPrivateKeyInfo
.Add (encryptedData
);
483 return encryptedPrivateKeyInfo
.GetBytes ();