2004-05-11 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / Mono.Security / Mono.Security.Cryptography / PKCS8.cs
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)
10 //
11
12 using System;
13 using System.Collections;
14 using System.Security.Cryptography;
15 using System.Text;
16
17 using Mono.Security.Cryptography;
18 using Mono.Security.X509;
19
20 namespace Mono.Security.Cryptography {
21
22         public sealed class PKCS8 {
23
24                 public enum KeyInfo {
25                         PrivateKey,
26                         EncryptedPrivateKey,
27                         Unknown
28                 }
29
30                 private PKCS8 () 
31                 {
32                 }
33
34                 static public KeyInfo GetType (byte[] data) 
35                 {
36                         if (data == null)
37                                 throw new ArgumentNullException ("data");
38
39                         KeyInfo ki = KeyInfo.Unknown;
40                         try {
41                                 ASN1 top = new ASN1 (data);
42                                 if ((top.Tag == 0x30) && (top.Count > 0)) {
43                                         ASN1 firstLevel = top [0];
44                                         switch (firstLevel.Tag) {
45                                                 case 0x02:
46                                                         ki = KeyInfo.PrivateKey;
47                                                         break;
48                                                 case 0x30:
49                                                         ki = KeyInfo.EncryptedPrivateKey;
50                                                         break;
51                                         }
52                                 }
53                         }
54                         catch {
55                                 throw new CryptographicException ("invalid ASN.1 data");
56                         }
57                         return ki;
58                 }
59
60                 /*
61                  * PrivateKeyInfo ::= SEQUENCE {
62                  *      version Version,
63                  *      privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
64                  *      privateKey PrivateKey,
65                  *      attributes [0] IMPLICIT Attributes OPTIONAL 
66                  * }
67                  * 
68                  * Version ::= INTEGER
69                  * 
70                  * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
71                  * 
72                  * PrivateKey ::= OCTET STRING
73                  * 
74                  * Attributes ::= SET OF Attribute
75                  */
76                 public class PrivateKeyInfo {
77
78                         private int _version;
79                         private string _algorithm;
80                         private byte[] _key;
81                         private ArrayList _list;
82
83                         public PrivateKeyInfo () 
84                         {
85                                 _version = 0;
86                                 _list = new ArrayList ();
87                         }
88
89                         public PrivateKeyInfo (byte[] data) : this () 
90                         {
91                                 Decode (data);
92                         }
93
94                         // properties
95
96                         public string Algorithm {
97                                 get { return _algorithm; }
98                                 set { _algorithm = value; }
99                         }
100
101                         public ArrayList Attributes {
102                                 get { return _list; }
103                         }
104
105                         public byte[] PrivateKey {
106                                 get {
107                                         if (_key == null)
108                                                 return null;
109                                         return (byte[]) _key.Clone (); 
110                                 }
111                                 set { 
112                                         if (value == null)
113                                                 throw new ArgumentNullException ("PrivateKey");
114                                         _key = (byte[]) value.Clone (); 
115                                 }
116                         }
117
118                         public int Version {
119                                 get { return _version; }
120                                 set { 
121                                         if (value < 0)
122                                                 throw new ArgumentOutOfRangeException ("negative version");
123                                         _version = value; 
124                                 }
125                         }
126
127                         // methods
128
129                         private void Decode (byte[] data) 
130                         {
131                                 ASN1 privateKeyInfo = new ASN1 (data);
132                                 if (privateKeyInfo.Tag != 0x30)
133                                         throw new CryptographicException ("invalid PrivateKeyInfo");
134
135                                 ASN1 version = privateKeyInfo [0];
136                                 if (version.Tag != 0x02)
137                                         throw new CryptographicException ("invalid version");
138                                 _version = version.Value [0];
139
140                                 ASN1 privateKeyAlgorithm = privateKeyInfo [1];
141                                 if (privateKeyAlgorithm.Tag != 0x30)
142                                         throw new CryptographicException ("invalid algorithm");
143                                 
144                                 ASN1 algorithm = privateKeyAlgorithm [0];
145                                 if (algorithm.Tag != 0x06)
146                                         throw new CryptographicException ("missing algorithm OID");
147                                 _algorithm = ASN1Convert.ToOid (algorithm);
148
149                                 ASN1 privateKey = privateKeyInfo [2];
150                                 _key = privateKey.Value;
151
152                                 // attributes [0] IMPLICIT Attributes OPTIONAL
153                                 if (privateKeyInfo.Count > 3) {
154                                         ASN1 attributes = privateKeyInfo [3];
155                                         for (int i=0; i < attributes.Count; i++) {
156                                                 _list.Add (attributes [i]);
157                                         }
158                                 }
159                         }
160
161                         public byte[] GetBytes () 
162                         {
163                                 ASN1 privateKeyAlgorithm = new ASN1 (0x30);
164                                 privateKeyAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
165                                 privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL
166
167                                 ASN1 pki = new ASN1 (0x30);
168                                 pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version }));
169                                 pki.Add (privateKeyAlgorithm);
170                                 pki.Add (new ASN1 (0x04, _key));
171
172                                 if (_list.Count > 0) {
173                                         ASN1 attributes = new ASN1 (0xA0);
174                                         foreach (ASN1 attribute in _list) {
175                                                 attributes.Add (attribute);
176                                         }
177                                         pki.Add (attributes);
178                                 }
179
180                                 return pki.GetBytes ();
181                         }
182
183                         // static methods
184
185                         static private byte[] RemoveLeadingZero (byte[] bigInt) 
186                         {
187                                 int start = 0;
188                                 int length = bigInt.Length;
189                                 if (bigInt [0] == 0x00) {
190                                         start = 1;
191                                         length--;
192                                 }
193                                 byte[] bi = new byte [length];
194                                 Buffer.BlockCopy (bigInt, start, bi, 0, length);
195                                 return bi;
196                         }
197
198                         static private byte[] Normalize (byte[] bigInt, int length) 
199                         {
200                                 if (bigInt.Length == length)
201                                         return bigInt;
202                                 else if (bigInt.Length > length)
203                                         return RemoveLeadingZero (bigInt);
204                                 else {
205                                         // pad with 0
206                                         byte[] bi = new byte [length];
207                                         Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
208                                         return bi;
209                                 }
210                         }
211                         
212                         /*
213                          * RSAPrivateKey ::= SEQUENCE {
214                          *      version           Version, 
215                          *      modulus           INTEGER,  -- n
216                          *      publicExponent    INTEGER,  -- e
217                          *      privateExponent   INTEGER,  -- d
218                          *      prime1            INTEGER,  -- p
219                          *      prime2            INTEGER,  -- q
220                          *      exponent1         INTEGER,  -- d mod (p-1)
221                          *      exponent2         INTEGER,  -- d mod (q-1) 
222                          *      coefficient       INTEGER,  -- (inverse of q) mod p
223                          *      otherPrimeInfos   OtherPrimeInfos OPTIONAL 
224                          * }
225                          */
226                         static public RSA DecodeRSA (byte[] keypair) 
227                         {
228                                 ASN1 privateKey = new ASN1 (keypair);
229                                 if (privateKey.Tag != 0x30)
230                                         throw new CryptographicException ("invalid private key format");
231
232                                 ASN1 version = privateKey [0];
233                                 if (version.Tag != 0x02)
234                                         throw new CryptographicException ("missing version");
235
236                                 if (privateKey.Count < 9)
237                                         throw new CryptographicException ("not enough key parameters");
238
239                                 RSAParameters param = new RSAParameters ();
240                                 // note: MUST remove leading 0 - else MS wont import the key
241                                 param.Modulus = RemoveLeadingZero (privateKey [1].Value);
242                                 int keysize = param.Modulus.Length;
243                                 int keysize2 = (keysize >> 1); // half-size
244                                 // size must be normalized - else MS wont import the key
245                                 param.D = Normalize (privateKey [3].Value, keysize);
246                                 param.DP = Normalize (privateKey [6].Value, keysize2);
247                                 param.DQ = Normalize (privateKey [7].Value, keysize2);
248                                 param.Exponent = RemoveLeadingZero (privateKey [2].Value);
249                                 param.InverseQ = Normalize (privateKey [8].Value, keysize2);
250                                 param.P = Normalize (privateKey [4].Value, keysize2);
251                                 param.Q = Normalize (privateKey [5].Value, keysize2);
252
253                                 RSA rsa = RSA.Create ();
254                                 rsa.ImportParameters (param);
255                                 return rsa;
256                         }
257
258                         /*
259                          * RSAPrivateKey ::= SEQUENCE {
260                          *      version           Version, 
261                          *      modulus           INTEGER,  -- n
262                          *      publicExponent    INTEGER,  -- e
263                          *      privateExponent   INTEGER,  -- d
264                          *      prime1            INTEGER,  -- p
265                          *      prime2            INTEGER,  -- q
266                          *      exponent1         INTEGER,  -- d mod (p-1)
267                          *      exponent2         INTEGER,  -- d mod (q-1) 
268                          *      coefficient       INTEGER,  -- (inverse of q) mod p
269                          *      otherPrimeInfos   OtherPrimeInfos OPTIONAL 
270                          * }
271                          */
272                         static public byte[] Encode (RSA rsa) 
273                         {
274                                 RSAParameters param = rsa.ExportParameters (true);
275
276                                 ASN1 rsaPrivateKey = new ASN1 (0x30);
277                                 rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 }));
278                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus));
279                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent));
280                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D));
281                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P));
282                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q));
283                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP));
284                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ));
285                                 rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ));
286
287                                 return rsaPrivateKey.GetBytes ();
288                         }
289
290                         // DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
291                         // which isn't enough for rebuilding the keypair. The other parameters
292                         // can be found (98% of the time) in the X.509 certificate associated
293                         // with the private key or (2% of the time) the parameters are in it's
294                         // issuer X.509 certificate (not supported in the .NET framework).
295                         static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters) 
296                         {
297                                 ASN1 pvk = new ASN1 (privateKey);
298                                 if (pvk.Tag != 0x02)
299                                         throw new CryptographicException ("invalid private key format");
300
301                                 // X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
302                                 dsaParameters.X = Normalize (privateKey, 20);
303                                 DSA dsa = DSA.Create ();
304                                 dsa.ImportParameters (dsaParameters);
305                                 return dsa;
306                         }
307
308                         static public byte[] Encode (DSA dsa) 
309                         {
310                                 DSAParameters param = dsa.ExportParameters (true);
311                                 return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes ();
312                         }
313
314                         static public byte[] Encode (AsymmetricAlgorithm aa) 
315                         {
316                                 if (aa is RSA)
317                                         return Encode ((RSA)aa);
318                                 else if (aa is DSA)
319                                         return Encode ((DSA)aa);
320                                 else
321                                         throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
322                         }
323                 }
324
325                 /*
326                  * EncryptedPrivateKeyInfo ::= SEQUENCE {
327                  *      encryptionAlgorithm EncryptionAlgorithmIdentifier,
328                  *      encryptedData EncryptedData 
329                  * }
330                  * 
331                  * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
332                  * 
333                  * EncryptedData ::= OCTET STRING
334                  * 
335                  * --
336                  *  AlgorithmIdentifier  ::= SEQUENCE {
337                  *      algorithm  OBJECT IDENTIFIER,
338                  *      parameters ANY DEFINED BY algorithm OPTIONAL
339                  * }
340                  * 
341                  * -- from PKCS#5
342                  * PBEParameter ::= SEQUENCE {
343                  *      salt OCTET STRING SIZE(8),
344                  *      iterationCount INTEGER 
345                  * }
346                  */
347                 public class EncryptedPrivateKeyInfo {
348
349                         private string _algorithm;
350                         private byte[] _salt;
351                         private int _iterations;
352                         private byte[] _data;
353
354                         public EncryptedPrivateKeyInfo () {}
355
356                         public EncryptedPrivateKeyInfo (byte[] data) : this () 
357                         {
358                                 Decode (data);
359                         }
360
361                         // properties
362
363                         public string Algorithm {
364                                 get { return _algorithm; }
365                                 set { _algorithm = value; }
366                         }
367
368                         public byte[] EncryptedData {
369                                 get { return (_data == null) ? null : (byte[]) _data.Clone (); }
370                                 set { _data = (value == null) ? null : (byte[]) value.Clone (); }
371                         }
372
373                         public byte[] Salt {
374                                 get { 
375                                         if (_salt == null) {
376                                                 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
377                                                 _salt = new byte [8];
378                                                 rng.GetBytes (_salt);
379                                         }
380                                         return (byte[]) _salt.Clone (); 
381                                 }
382                                 set { _salt = (byte[]) value.Clone (); }
383                         }
384
385                         public int IterationCount {
386                                 get { return _iterations; }
387                                 set { 
388                                         if (value < 0)
389                                                 throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
390                                         _iterations = value; 
391                                 }
392                         }
393
394                         // methods
395
396                         private void Decode (byte[] data) 
397                         {
398                                 ASN1 encryptedPrivateKeyInfo = new ASN1 (data);
399                                 if (encryptedPrivateKeyInfo.Tag != 0x30)
400                                         throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
401
402                                 ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0];
403                                 if (encryptionAlgorithm.Tag != 0x30)
404                                         throw new CryptographicException ("invalid encryptionAlgorithm");
405                                 ASN1 algorithm = encryptionAlgorithm [0];
406                                 if (algorithm.Tag != 0x06)
407                                         throw new CryptographicException ("invalid algorithm");
408                                 _algorithm = ASN1Convert.ToOid (algorithm);
409                                 // parameters ANY DEFINED BY algorithm OPTIONAL
410                                 if (encryptionAlgorithm.Count > 1) {
411                                         ASN1 parameters = encryptionAlgorithm [1];
412                                         if (parameters.Tag != 0x30)
413                                                 throw new CryptographicException ("invalid parameters");
414
415                                         ASN1 salt = parameters [0];
416                                         if (salt.Tag != 0x04)
417                                                 throw new CryptographicException ("invalid salt");
418                                         _salt = salt.Value;
419
420                                         ASN1 iterationCount = parameters [1];
421                                         if (iterationCount.Tag != 0x02)
422                                                 throw new CryptographicException ("invalid iterationCount");
423                                         _iterations = ASN1Convert.ToInt32 (iterationCount);
424                                 }
425
426                                 ASN1 encryptedData = encryptedPrivateKeyInfo [1];
427                                 if (encryptedData.Tag != 0x04)
428                                         throw new CryptographicException ("invalid EncryptedData");
429                                 _data = encryptedData.Value;
430                         }
431
432                         // Note: PKCS#8 doesn't define how to generate the key required for encryption
433                         // so you're on your own. Just don't try to copy the big guys too much ;)
434                         // Netscape:    http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
435                         // Microsoft:   http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
436                         public byte[] GetBytes ()
437                         {
438                                 if (_algorithm == null)
439                                         throw new CryptographicException ("No algorithm OID specified");
440
441                                 ASN1 encryptionAlgorithm = new ASN1 (0x30);
442                                 encryptionAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
443
444                                 // parameters ANY DEFINED BY algorithm OPTIONAL
445                                 if ((_iterations > 0) || (_salt != null)) {
446                                         ASN1 salt = new ASN1 (0x04, _salt);
447                                         ASN1 iterations = ASN1Convert.FromInt32 (_iterations);
448
449                                         ASN1 parameters = new ASN1 (0x30);
450                                         parameters.Add (salt);
451                                         parameters.Add (iterations);
452                                         encryptionAlgorithm.Add (parameters);
453                                 }
454
455                                 // encapsulates EncryptedData into an OCTET STRING
456                                 ASN1 encryptedData = new ASN1 (0x04, _data);
457
458                                 ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30);
459                                 encryptedPrivateKeyInfo.Add (encryptionAlgorithm);
460                                 encryptedPrivateKeyInfo.Add (encryptedData);
461
462                                 return encryptedPrivateKeyInfo.GetBytes ();
463                         }
464                 }
465         }
466 }