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