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 // Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Collections
;
33 using System
.Security
.Cryptography
;
36 using Mono
.Security
.X509
;
38 namespace Mono
.Security
.Cryptography
{
57 static public KeyInfo
GetType (byte[] data
)
60 throw new ArgumentNullException ("data");
62 KeyInfo ki
= KeyInfo
.Unknown
;
64 ASN1 top
= new ASN1 (data
);
65 if ((top
.Tag
== 0x30) && (top
.Count
> 0)) {
66 ASN1 firstLevel
= top
[0];
67 switch (firstLevel
.Tag
) {
69 ki
= KeyInfo
.PrivateKey
;
72 ki
= KeyInfo
.EncryptedPrivateKey
;
78 throw new CryptographicException ("invalid ASN.1 data");
84 * PrivateKeyInfo ::= SEQUENCE {
86 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
87 * privateKey PrivateKey,
88 * attributes [0] IMPLICIT Attributes OPTIONAL
93 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
95 * PrivateKey ::= OCTET STRING
97 * Attributes ::= SET OF Attribute
99 public class PrivateKeyInfo
{
101 private int _version
;
102 private string _algorithm
;
104 private ArrayList _list
;
106 public PrivateKeyInfo ()
109 _list
= new ArrayList ();
112 public PrivateKeyInfo (byte[] data
) : this ()
119 public string Algorithm
{
120 get { return _algorithm; }
121 set { _algorithm = value; }
124 public ArrayList Attributes
{
125 get { return _list; }
128 public byte[] PrivateKey
{
132 return (byte[]) _key
.Clone ();
136 throw new ArgumentNullException ("PrivateKey");
137 _key
= (byte[]) value.Clone ();
142 get { return _version; }
145 throw new ArgumentOutOfRangeException ("negative version");
152 private void Decode (byte[] data
)
154 ASN1 privateKeyInfo
= new ASN1 (data
);
155 if (privateKeyInfo
.Tag
!= 0x30)
156 throw new CryptographicException ("invalid PrivateKeyInfo");
158 ASN1 version
= privateKeyInfo
[0];
159 if (version
.Tag
!= 0x02)
160 throw new CryptographicException ("invalid version");
161 _version
= version
.Value
[0];
163 ASN1 privateKeyAlgorithm
= privateKeyInfo
[1];
164 if (privateKeyAlgorithm
.Tag
!= 0x30)
165 throw new CryptographicException ("invalid algorithm");
167 ASN1 algorithm
= privateKeyAlgorithm
[0];
168 if (algorithm
.Tag
!= 0x06)
169 throw new CryptographicException ("missing algorithm OID");
170 _algorithm
= ASN1Convert
.ToOid (algorithm
);
172 ASN1 privateKey
= privateKeyInfo
[2];
173 _key
= privateKey
.Value
;
175 // attributes [0] IMPLICIT Attributes OPTIONAL
176 if (privateKeyInfo
.Count
> 3) {
177 ASN1 attributes
= privateKeyInfo
[3];
178 for (int i
=0; i
< attributes
.Count
; i
++) {
179 _list
.Add (attributes
[i
]);
184 public byte[] GetBytes ()
186 ASN1 privateKeyAlgorithm
= new ASN1 (0x30);
187 privateKeyAlgorithm
.Add (ASN1Convert
.FromOid (_algorithm
));
188 privateKeyAlgorithm
.Add (new ASN1 (0x05)); // ASN.1 NULL
190 ASN1 pki
= new ASN1 (0x30);
191 pki
.Add (new ASN1 (0x02, new byte [1] { (byte) _version }
));
192 pki
.Add (privateKeyAlgorithm
);
193 pki
.Add (new ASN1 (0x04, _key
));
195 if (_list
.Count
> 0) {
196 ASN1 attributes
= new ASN1 (0xA0);
197 foreach (ASN1 attribute
in _list
) {
198 attributes
.Add (attribute
);
200 pki
.Add (attributes
);
203 return pki
.GetBytes ();
208 static private byte[] RemoveLeadingZero (byte[] bigInt
)
211 int length
= bigInt
.Length
;
212 if (bigInt
[0] == 0x00) {
216 byte[] bi
= new byte [length
];
217 Buffer
.BlockCopy (bigInt
, start
, bi
, 0, length
);
221 static private byte[] Normalize (byte[] bigInt
, int length
)
223 if (bigInt
.Length
== length
)
225 else if (bigInt
.Length
> length
)
226 return RemoveLeadingZero (bigInt
);
229 byte[] bi
= new byte [length
];
230 Buffer
.BlockCopy (bigInt
, 0, bi
, (length
- bigInt
.Length
), bigInt
.Length
);
236 * RSAPrivateKey ::= SEQUENCE {
238 * modulus INTEGER, -- n
239 * publicExponent INTEGER, -- e
240 * privateExponent INTEGER, -- d
241 * prime1 INTEGER, -- p
242 * prime2 INTEGER, -- q
243 * exponent1 INTEGER, -- d mod (p-1)
244 * exponent2 INTEGER, -- d mod (q-1)
245 * coefficient INTEGER, -- (inverse of q) mod p
246 * otherPrimeInfos OtherPrimeInfos OPTIONAL
249 static public RSA
DecodeRSA (byte[] keypair
)
251 ASN1 privateKey
= new ASN1 (keypair
);
252 if (privateKey
.Tag
!= 0x30)
253 throw new CryptographicException ("invalid private key format");
255 ASN1 version
= privateKey
[0];
256 if (version
.Tag
!= 0x02)
257 throw new CryptographicException ("missing version");
259 if (privateKey
.Count
< 9)
260 throw new CryptographicException ("not enough key parameters");
262 RSAParameters param
= new RSAParameters ();
263 // note: MUST remove leading 0 - else MS wont import the key
264 param
.Modulus
= RemoveLeadingZero (privateKey
[1].Value
);
265 int keysize
= param
.Modulus
.Length
;
266 int keysize2
= (keysize
>> 1); // half-size
267 // size must be normalized - else MS wont import the key
268 param
.D
= Normalize (privateKey
[3].Value
, keysize
);
269 param
.DP
= Normalize (privateKey
[6].Value
, keysize2
);
270 param
.DQ
= Normalize (privateKey
[7].Value
, keysize2
);
271 param
.Exponent
= RemoveLeadingZero (privateKey
[2].Value
);
272 param
.InverseQ
= Normalize (privateKey
[8].Value
, keysize2
);
273 param
.P
= Normalize (privateKey
[4].Value
, keysize2
);
274 param
.Q
= Normalize (privateKey
[5].Value
, keysize2
);
276 RSA rsa
= RSA
.Create ();
277 rsa
.ImportParameters (param
);
282 rsa
.ImportParameters (param
);
284 catch (CryptographicException
) {
285 // this may cause problem when this code is run under
286 // the SYSTEM identity on Windows (e.g. ASP.NET). See
287 // http://bugzilla.ximian.com/show_bug.cgi?id=77559
288 CspParameters csp
= new CspParameters ();
289 csp
.Flags
= CspProviderFlags
.UseMachineKeyStore
;
290 rsa
= new RSACryptoServiceProvider (csp
);
291 rsa
.ImportParameters (param
);
298 * RSAPrivateKey ::= SEQUENCE {
300 * modulus INTEGER, -- n
301 * publicExponent INTEGER, -- e
302 * privateExponent INTEGER, -- d
303 * prime1 INTEGER, -- p
304 * prime2 INTEGER, -- q
305 * exponent1 INTEGER, -- d mod (p-1)
306 * exponent2 INTEGER, -- d mod (q-1)
307 * coefficient INTEGER, -- (inverse of q) mod p
308 * otherPrimeInfos OtherPrimeInfos OPTIONAL
311 static public byte[] Encode (RSA rsa
)
313 RSAParameters param
= rsa
.ExportParameters (true);
315 ASN1 rsaPrivateKey
= new ASN1 (0x30);
316 rsaPrivateKey
.Add (new ASN1 (0x02, new byte [1] { 0x00 }
));
317 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.Modulus
));
318 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.Exponent
));
319 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.D
));
320 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.P
));
321 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.Q
));
322 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.DP
));
323 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.DQ
));
324 rsaPrivateKey
.Add (ASN1Convert
.FromUnsignedBigInteger (param
.InverseQ
));
326 return rsaPrivateKey
.GetBytes ();
329 // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
330 // which isn't enough for rebuilding the keypair. The other parameters
331 // can be found (98% of the time) in the X.509 certificate associated
332 // with the private key or (2% of the time) the parameters are in it's
333 // issuer X.509 certificate (not supported in the .NET framework).
334 static public DSA
DecodeDSA (byte[] privateKey
, DSAParameters dsaParameters
)
336 ASN1 pvk
= new ASN1 (privateKey
);
338 throw new CryptographicException ("invalid private key format");
340 // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
341 dsaParameters
.X
= Normalize (pvk
.Value
, 20);
342 DSA dsa
= DSA
.Create ();
343 dsa
.ImportParameters (dsaParameters
);
347 static public byte[] Encode (DSA dsa
)
349 DSAParameters param
= dsa
.ExportParameters (true);
350 return ASN1Convert
.FromUnsignedBigInteger (param
.X
).GetBytes ();
353 static public byte[] Encode (AsymmetricAlgorithm aa
)
356 return Encode ((RSA
)aa
);
358 return Encode ((DSA
)aa
);
360 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa
.ToString ());
365 * EncryptedPrivateKeyInfo ::= SEQUENCE {
366 * encryptionAlgorithm EncryptionAlgorithmIdentifier,
367 * encryptedData EncryptedData
370 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
372 * EncryptedData ::= OCTET STRING
375 * AlgorithmIdentifier ::= SEQUENCE {
376 * algorithm OBJECT IDENTIFIER,
377 * parameters ANY DEFINED BY algorithm OPTIONAL
381 * PBEParameter ::= SEQUENCE {
382 * salt OCTET STRING SIZE(8),
383 * iterationCount INTEGER
386 public class EncryptedPrivateKeyInfo
{
388 private string _algorithm
;
389 private byte[] _salt
;
390 private int _iterations
;
391 private byte[] _data
;
393 public EncryptedPrivateKeyInfo () {}
395 public EncryptedPrivateKeyInfo (byte[] data
) : this ()
402 public string Algorithm
{
403 get { return _algorithm; }
404 set { _algorithm = value; }
407 public byte[] EncryptedData
{
408 get { return (_data == null) ? null : (byte[]) _data.Clone (); }
409 set { _data = (value == null) ? null : (byte[]) value.Clone (); }
415 RandomNumberGenerator rng
= RandomNumberGenerator
.Create ();
416 _salt
= new byte [8];
417 rng
.GetBytes (_salt
);
419 return (byte[]) _salt
.Clone ();
421 set { _salt = (byte[]) value.Clone (); }
424 public int IterationCount
{
425 get { return _iterations; }
428 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
435 private void Decode (byte[] data
)
437 ASN1 encryptedPrivateKeyInfo
= new ASN1 (data
);
438 if (encryptedPrivateKeyInfo
.Tag
!= 0x30)
439 throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
441 ASN1 encryptionAlgorithm
= encryptedPrivateKeyInfo
[0];
442 if (encryptionAlgorithm
.Tag
!= 0x30)
443 throw new CryptographicException ("invalid encryptionAlgorithm");
444 ASN1 algorithm
= encryptionAlgorithm
[0];
445 if (algorithm
.Tag
!= 0x06)
446 throw new CryptographicException ("invalid algorithm");
447 _algorithm
= ASN1Convert
.ToOid (algorithm
);
448 // parameters ANY DEFINED BY algorithm OPTIONAL
449 if (encryptionAlgorithm
.Count
> 1) {
450 ASN1 parameters
= encryptionAlgorithm
[1];
451 if (parameters
.Tag
!= 0x30)
452 throw new CryptographicException ("invalid parameters");
454 ASN1 salt
= parameters
[0];
455 if (salt
.Tag
!= 0x04)
456 throw new CryptographicException ("invalid salt");
459 ASN1 iterationCount
= parameters
[1];
460 if (iterationCount
.Tag
!= 0x02)
461 throw new CryptographicException ("invalid iterationCount");
462 _iterations
= ASN1Convert
.ToInt32 (iterationCount
);
465 ASN1 encryptedData
= encryptedPrivateKeyInfo
[1];
466 if (encryptedData
.Tag
!= 0x04)
467 throw new CryptographicException ("invalid EncryptedData");
468 _data
= encryptedData
.Value
;
471 // Note: PKCS#8 doesn't define how to generate the key required for encryption
472 // so you're on your own. Just don't try to copy the big guys too much ;)
473 // Netscape: http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
474 // Microsoft: http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
475 public byte[] GetBytes ()
477 if (_algorithm
== null)
478 throw new CryptographicException ("No algorithm OID specified");
480 ASN1 encryptionAlgorithm
= new ASN1 (0x30);
481 encryptionAlgorithm
.Add (ASN1Convert
.FromOid (_algorithm
));
483 // parameters ANY DEFINED BY algorithm OPTIONAL
484 if ((_iterations
> 0) || (_salt
!= null)) {
485 ASN1 salt
= new ASN1 (0x04, _salt
);
486 ASN1 iterations
= ASN1Convert
.FromInt32 (_iterations
);
488 ASN1 parameters
= new ASN1 (0x30);
489 parameters
.Add (salt
);
490 parameters
.Add (iterations
);
491 encryptionAlgorithm
.Add (parameters
);
494 // encapsulates EncryptedData into an OCTET STRING
495 ASN1 encryptedData
= new ASN1 (0x04, _data
);
497 ASN1 encryptedPrivateKeyInfo
= new ASN1 (0x30);
498 encryptedPrivateKeyInfo
.Add (encryptionAlgorithm
);
499 encryptedPrivateKeyInfo
.Add (encryptedData
);
501 return encryptedPrivateKeyInfo
.GetBytes ();