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