f333854b9e2a0b40602272755fe284406bff87e4
[mono.git] / mcs / gmcs / CryptoConvert.cs
1 //
2 // CryptoConvert.cs - Crypto Convertion Routines
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
9 //
10
11 using System;
12 using System.Security.Cryptography;
13 using System.Text;
14
15 namespace Mono.Security.Cryptography {
16
17 #if INSIDE_CORLIB
18         internal
19 #else
20         public
21 #endif
22         class CryptoConvert {
23
24                 static private int ToInt32LE (byte [] bytes, int offset)
25                 {
26                         return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
27                 }
28
29                 static private uint ToUInt32LE (byte [] bytes, int offset)
30                 {
31                         return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
32                 }
33
34                 static private byte [] GetBytesLE (int val)
35                 {
36                         return new byte [] { 
37                                 (byte) (val & 0xff), 
38                                 (byte) ((val >> 8) & 0xff), 
39                                 (byte) ((val >> 16) & 0xff), 
40                                 (byte) ((val >> 24) & 0xff)
41                         };
42                 }
43
44                 static private byte[] Trim (byte[] array) 
45                 {
46                         for (int i=0; i < array.Length; i++) {
47                                 if (array [i] != 0x00) {
48                                         byte[] result = new byte [array.Length - i];
49                                         Array.Copy (array, i, result, 0, result.Length);
50                                         return result;
51                                 }
52                         }
53                         return null;
54                 }
55
56                 // convert the key from PRIVATEKEYBLOB to RSA
57                 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
58                 // e.g. SNK files, PVK files
59                 static public RSA FromCapiPrivateKeyBlob (byte[] blob) 
60                 {
61                         return FromCapiPrivateKeyBlob (blob, 0);
62                 }
63
64                 static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) 
65                 {
66                         if (blob == null)
67                                 throw new ArgumentNullException ("blob");
68                         if (offset >= blob.Length)
69                                 throw new ArgumentException ("blob is too small.");
70
71                         try {
72                                 if ((blob [offset]   != 0x07) ||                                // PRIVATEKEYBLOB (0x07)
73                                     (blob [offset+1] != 0x02) ||                                // Version (0x02)
74                                     (blob [offset+2] != 0x00) ||                                // Reserved (word)
75                                     (blob [offset+3] != 0x00) ||
76                                     (ToUInt32LE (blob, offset+8) != 0x32415352))        // DWORD magic = RSA2
77                                         throw new CryptographicException ("Invalid blob header");
78                                 
79                                 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
80                                 int algId = ToInt32LE (blob, offset+4);
81
82                                 // DWORD bitlen
83                                 int bitLen = ToInt32LE (blob, offset+12);
84
85                                 // DWORD public exponent
86                                 RSAParameters rsap = new RSAParameters ();
87                                 byte[] exp = new byte [4];
88                                 Array.Copy (blob, offset+16, exp, 0, 4);
89                                 Array.Reverse (exp);
90                                 rsap.Exponent = Trim (exp);
91                         
92                                 int pos = offset+20;
93                                 // BYTE modulus[rsapubkey.bitlen/8];
94                                 int byteLen = (bitLen >> 3);
95                                 rsap.Modulus = new byte [byteLen];
96                                 Array.Copy (blob, pos, rsap.Modulus, 0, byteLen);
97                                 Array.Reverse (rsap.Modulus);
98                                 pos += byteLen;
99
100                                 // BYTE prime1[rsapubkey.bitlen/16];
101                                 int byteHalfLen = (byteLen >> 1);
102                                 rsap.P = new byte [byteHalfLen];
103                                 Array.Copy (blob, pos, rsap.P, 0, byteHalfLen);
104                                 Array.Reverse (rsap.P);
105                                 pos += byteHalfLen;
106
107                                 // BYTE prime2[rsapubkey.bitlen/16];
108                                 rsap.Q = new byte [byteHalfLen];
109                                 Array.Copy (blob, pos, rsap.Q, 0, byteHalfLen);
110                                 Array.Reverse (rsap.Q);
111                                 pos += byteHalfLen;
112
113                                 // BYTE exponent1[rsapubkey.bitlen/16];
114                                 rsap.DP = new byte [byteHalfLen];
115                                 Array.Copy (blob, pos, rsap.DP, 0, byteHalfLen);
116                                 Array.Reverse (rsap.DP);
117                                 pos += byteHalfLen;
118
119                                 // BYTE exponent2[rsapubkey.bitlen/16];
120                                 rsap.DQ = new byte [byteHalfLen];
121                                 Array.Copy (blob, pos, rsap.DQ, 0, byteHalfLen);
122                                 Array.Reverse (rsap.DQ);
123                                 pos += byteHalfLen;
124
125                                 // BYTE coefficient[rsapubkey.bitlen/16];
126                                 rsap.InverseQ = new byte [byteHalfLen];
127                                 Array.Copy (blob, pos, rsap.InverseQ, 0, byteHalfLen);
128                                 Array.Reverse (rsap.InverseQ);
129                                 pos += byteHalfLen;
130
131                                 // BYTE privateExponent[rsapubkey.bitlen/8];
132                                 rsap.D = new byte [byteLen];
133                                 Array.Copy (blob, pos, rsap.D, 0, byteLen);
134                                 Array.Reverse (rsap.D);
135
136                                 RSA rsa = (RSA)RSA.Create ();
137                                 rsa.ImportParameters (rsap);
138                                 return rsa;
139                         }
140                         catch (Exception e) {
141                                 throw new CryptographicException ("Invalid blob.", e);
142                         }
143                 }
144
145                 static public byte[] ToCapiPrivateKeyBlob (RSA rsa) 
146                 {
147                         RSAParameters p = rsa.ExportParameters (true);
148                         int keyLength = p.Modulus.Length; // in bytes
149                         byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
150
151                         blob [0] = 0x07;        // Type - PRIVATEKEYBLOB (0x07)
152                         blob [1] = 0x02;        // Version - Always CUR_BLOB_VERSION (0x02)
153                         // [2], [3]             // RESERVED - Always 0
154                         blob [5] = 0x24;        // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
155                         blob [8] = 0x52;        // Magic - RSA2 (ASCII in hex)
156                         blob [9] = 0x53;
157                         blob [10] = 0x41;
158                         blob [11] = 0x32;
159
160                         byte[] bitlen = GetBytesLE (keyLength << 3);
161                         blob [12] = bitlen [0]; // bitlen
162                         blob [13] = bitlen [1]; 
163                         blob [14] = bitlen [2]; 
164                         blob [15] = bitlen [3];
165
166                         // public exponent (DWORD)
167                         int pos = 16;
168                         int n = p.Exponent.Length;
169                         while (n > 0)
170                                 blob [pos++] = p.Exponent [--n];
171                         // modulus
172                         pos = 20;
173                         byte[] part = p.Modulus;
174                         int len = part.Length;
175                         Array.Reverse (part, 0, len);
176                         Array.Copy (part, 0, blob, pos, len);
177                         pos += len;
178                         // private key
179                         part = p.P;
180                         len = part.Length;
181                         Array.Reverse (part, 0, len);
182                         Array.Copy (part, 0, blob, pos, len);
183                         pos += len;
184
185                         part = p.Q;
186                         len = part.Length;
187                         Array.Reverse (part, 0, len);
188                         Array.Copy (part, 0, blob, pos, len);
189                         pos += len;
190
191                         part = p.DP;
192                         len = part.Length;
193                         Array.Reverse (part, 0, len);
194                         Array.Copy (part, 0, blob, pos, len);
195                         pos += len;
196
197                         part = p.DQ;
198                         len = part.Length;
199                         Array.Reverse (part, 0, len);
200                         Array.Copy (part, 0, blob, pos, len);
201                         pos += len;
202
203                         part = p.InverseQ;
204                         len = part.Length;
205                         Array.Reverse (part, 0, len);
206                         Array.Copy (part, 0, blob, pos, len);
207                         pos += len;
208
209                         part = p.D;
210                         len = part.Length;
211                         Array.Reverse (part, 0, len);
212                         Array.Copy (part, 0, blob, pos, len);
213
214                         return blob;
215                 }
216
217                 static public RSA FromCapiPublicKeyBlob (byte[] blob) 
218                 {
219                         return FromCapiPublicKeyBlob (blob, 0);
220                 }
221
222                 static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) 
223                 {
224                         if (blob == null)
225                                 throw new ArgumentNullException ("blob");
226                         if (offset >= blob.Length)
227                                 throw new ArgumentException ("blob is too small.");
228
229                         try {
230                                 if ((blob [offset]   != 0x06) ||                                // PUBLICKEYBLOB (0x06)
231                                     (blob [offset+1] != 0x02) ||                                // Version (0x02)
232                                     (blob [offset+2] != 0x00) ||                                // Reserved (word)
233                                     (blob [offset+3] != 0x00) || 
234                                     (ToUInt32LE (blob, offset+8) != 0x31415352))        // DWORD magic = RSA1
235                                         throw new CryptographicException ("Invalid blob header");
236
237                                 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
238                                 int algId = ToInt32LE (blob, offset+4);
239
240                                 // DWORD bitlen
241                                 int bitLen = ToInt32LE (blob, offset+12);
242
243                                 // DWORD public exponent
244                                 RSAParameters rsap = new RSAParameters ();
245                                 rsap.Exponent = new byte [3];
246                                 rsap.Exponent [0] = blob [offset+18];
247                                 rsap.Exponent [1] = blob [offset+17];
248                                 rsap.Exponent [2] = blob [offset+16];
249                         
250                                 int pos = offset+20;
251                                 // BYTE modulus[rsapubkey.bitlen/8];
252                                 int byteLen = (bitLen >> 3);
253                                 rsap.Modulus = new byte [byteLen];
254                                 Array.Copy (blob, pos, rsap.Modulus, 0, byteLen);
255                                 Array.Reverse (rsap.Modulus);
256
257                                 RSA rsa = (RSA)RSA.Create ();
258                                 rsa.ImportParameters (rsap);
259                                 return rsa;
260                         }
261                         catch (Exception e) {
262                                 throw new CryptographicException ("Invalid blob.", e);
263                         }
264                 }
265
266                 static public byte[] ToCapiPublicKeyBlob (RSA rsa) 
267                 {
268                         RSAParameters p = rsa.ExportParameters (false);
269                         int keyLength = p.Modulus.Length; // in bytes
270                         byte[] blob = new byte [20 + keyLength];
271
272                         blob [0] = 0x06;        // Type - PUBLICKEYBLOB (0x06)
273                         blob [1] = 0x02;        // Version - Always CUR_BLOB_VERSION (0x02)
274                         // [2], [3]             // RESERVED - Always 0
275                         blob [5] = 0x24;        // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
276                         blob [8] = 0x52;        // Magic - RSA1 (ASCII in hex)
277                         blob [9] = 0x53;
278                         blob [10] = 0x41;
279                         blob [11] = 0x31;
280
281                         byte[] bitlen = GetBytesLE (keyLength << 3);
282                         blob [12] = bitlen [0]; // bitlen
283                         blob [13] = bitlen [1]; 
284                         blob [14] = bitlen [2]; 
285                         blob [15] = bitlen [3];
286
287                         // public exponent (DWORD)
288                         int pos = 16;
289                         int n = p.Exponent.Length;
290                         while (n > 0)
291                                 blob [pos++] = p.Exponent [--n];
292                         // modulus
293                         pos = 20;
294                         byte[] part = p.Modulus;
295                         int len = part.Length;
296                         Array.Reverse (part, 0, len);
297                         Array.Copy (part, 0, blob, pos, len);
298                         pos += len;
299                         return blob;
300                 }
301
302                 // PRIVATEKEYBLOB
303                 // PUBLICKEYBLOB
304                 static public RSA FromCapiKeyBlob (byte[] blob) 
305                 {
306                         return FromCapiKeyBlob (blob, 0);
307                 }
308
309                 static public RSA FromCapiKeyBlob (byte[] blob, int offset) 
310                 {
311                         if (blob == null)
312                                 throw new ArgumentNullException ("blob");
313                         if (offset >= blob.Length)
314                                 throw new ArgumentException ("blob is too small.");
315
316                         switch (blob [offset]) {
317                                 case 0x00:
318                                         // this could be a public key inside an header
319                                         // like "sn -e" would produce
320                                         if (blob [offset + 12] == 0x06) {
321                                                 return FromCapiPublicKeyBlob (blob, offset + 12);
322                                         }
323                                         break;
324                                 case 0x06:
325                                         return FromCapiPublicKeyBlob (blob, offset);
326                                 case 0x07:
327                                         return FromCapiPrivateKeyBlob (blob, offset);
328                         }
329                         throw new CryptographicException ("Unknown blob format.");
330                 }
331
332                 static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) 
333                 {
334                         if (keypair == null)
335                                 throw new ArgumentNullException ("keypair");
336
337                         // check between RSA and DSA (and potentially others like DH)
338                         if (keypair is RSA)
339                                 return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
340                         else
341                                 return null;    // TODO
342                 }
343
344                 static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) 
345                 {
346                         if (rsa == null)
347                                 throw new ArgumentNullException ("rsa");
348
349                         RSAParameters p = rsa.ExportParameters (includePrivateKey);
350                         if (includePrivateKey)
351                                 return ToCapiPrivateKeyBlob (rsa);
352                         else
353                                 return ToCapiPublicKeyBlob (rsa);
354                 }
355
356                 static public string ToHex (byte[] input) 
357                 {
358                         if (input == null)
359                                 return null;
360
361                         StringBuilder sb = new StringBuilder (input.Length * 2);
362                         foreach (byte b in input) {
363                                 sb.Append (b.ToString ("X2"));
364                         }
365                         return sb.ToString ();
366                 }
367
368                 static private byte FromHexChar (char c) 
369                 {
370                         if ((c >= 'a') && (c <= 'f'))
371                                 return (byte) (c - 'a' + 10);
372                         if ((c >= 'A') && (c <= 'F'))
373                                 return (byte) (c - 'A' + 10);
374                         if ((c >= '0') && (c <= '9'))
375                                 return (byte) (c - '0');
376                         throw new ArgumentException ("invalid hex char");
377                 }
378
379                 static public byte[] FromHex (string hex) 
380                 {
381                         if (hex == null)
382                                 return null;
383                         if ((hex.Length & 0x1) == 0x1)
384                                 throw new ArgumentException ("Length must be a multiple of 2");
385
386                         byte[] result = new byte [hex.Length >> 1];
387                         int n = 0;
388                         int i = 0;
389                         while (n < result.Length) {
390                                 result [n] = (byte) (FromHexChar (hex [i++]) << 4);
391                                 result [n++] += FromHexChar (hex [i++]);
392                         }
393                         return result;
394                 }
395         }
396 }