**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.Security / Mono.Security.Cryptography / PKCS8.cs
blob710c3bea4be5c6a0d7c213b59e466f9c907bf30c
1 //
2 // PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard
3 // ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc
4 //
5 // Author:
6 // Sebastien Pouliot <sebastien@ximian.com>
7 //
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:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System;
34 using System.Collections;
35 using System.Security.Cryptography;
36 using System.Text;
38 using Mono.Security.Cryptography;
39 using Mono.Security.X509;
41 namespace Mono.Security.Cryptography {
43 public sealed class PKCS8 {
45 public enum KeyInfo {
46 PrivateKey,
47 EncryptedPrivateKey,
48 Unknown
51 private PKCS8 ()
55 static public KeyInfo GetType (byte[] data)
57 if (data == null)
58 throw new ArgumentNullException ("data");
60 KeyInfo ki = KeyInfo.Unknown;
61 try {
62 ASN1 top = new ASN1 (data);
63 if ((top.Tag == 0x30) && (top.Count > 0)) {
64 ASN1 firstLevel = top [0];
65 switch (firstLevel.Tag) {
66 case 0x02:
67 ki = KeyInfo.PrivateKey;
68 break;
69 case 0x30:
70 ki = KeyInfo.EncryptedPrivateKey;
71 break;
75 catch {
76 throw new CryptographicException ("invalid ASN.1 data");
78 return ki;
82 * PrivateKeyInfo ::= SEQUENCE {
83 * version Version,
84 * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
85 * privateKey PrivateKey,
86 * attributes [0] IMPLICIT Attributes OPTIONAL
87 * }
89 * Version ::= INTEGER
91 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
93 * PrivateKey ::= OCTET STRING
95 * Attributes ::= SET OF Attribute
97 public class PrivateKeyInfo {
99 private int _version;
100 private string _algorithm;
101 private byte[] _key;
102 private ArrayList _list;
104 public PrivateKeyInfo ()
106 _version = 0;
107 _list = new ArrayList ();
110 public PrivateKeyInfo (byte[] data) : this ()
112 Decode (data);
115 // properties
117 public string Algorithm {
118 get { return _algorithm; }
119 set { _algorithm = value; }
122 public ArrayList Attributes {
123 get { return _list; }
126 public byte[] PrivateKey {
127 get {
128 if (_key == null)
129 return null;
130 return (byte[]) _key.Clone ();
132 set {
133 if (value == null)
134 throw new ArgumentNullException ("PrivateKey");
135 _key = (byte[]) value.Clone ();
139 public int Version {
140 get { return _version; }
141 set {
142 if (value < 0)
143 throw new ArgumentOutOfRangeException ("negative version");
144 _version = value;
148 // methods
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 ();
204 // static methods
206 static private byte[] RemoveLeadingZero (byte[] bigInt)
208 int start = 0;
209 int length = bigInt.Length;
210 if (bigInt [0] == 0x00) {
211 start = 1;
212 length--;
214 byte[] bi = new byte [length];
215 Buffer.BlockCopy (bigInt, start, bi, 0, length);
216 return bi;
219 static private byte[] Normalize (byte[] bigInt, int length)
221 if (bigInt.Length == length)
222 return bigInt;
223 else if (bigInt.Length > length)
224 return RemoveLeadingZero (bigInt);
225 else {
226 // pad with 0
227 byte[] bi = new byte [length];
228 Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
229 return bi;
234 * RSAPrivateKey ::= SEQUENCE {
235 * version Version,
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);
276 return rsa;
280 * RSAPrivateKey ::= SEQUENCE {
281 * version Version,
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);
319 if (pvk.Tag != 0x02)
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);
326 return dsa;
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)
337 if (aa is RSA)
338 return Encode ((RSA)aa);
339 else if (aa is DSA)
340 return Encode ((DSA)aa);
341 else
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
356 * --
357 * AlgorithmIdentifier ::= SEQUENCE {
358 * algorithm OBJECT IDENTIFIER,
359 * parameters ANY DEFINED BY algorithm OPTIONAL
362 * -- from PKCS#5
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 ()
379 Decode (data);
382 // properties
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 (); }
394 public byte[] Salt {
395 get {
396 if (_salt == null) {
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; }
408 set {
409 if (value < 0)
410 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
411 _iterations = value;
415 // methods
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");
439 _salt = salt.Value;
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 ();