2 // CryptoConvert.cs - Crypto Convertion Routines
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System
.Globalization
;
36 using System
.Security
.Cryptography
;
39 namespace Mono
.Security
.Cryptography
{
46 sealed class CryptoConvert
{
48 private CryptoConvert ()
52 static private int ToInt32LE (byte [] bytes
, int offset
)
54 return (bytes
[offset
+3] << 24) | (bytes
[offset
+2] << 16) | (bytes
[offset
+1] << 8) | bytes
[offset
];
57 static private uint ToUInt32LE (byte [] bytes
, int offset
)
59 return (uint)((bytes
[offset
+3] << 24) | (bytes
[offset
+2] << 16) | (bytes
[offset
+1] << 8) | bytes
[offset
]);
62 static private byte [] GetBytesLE (int val
)
66 (byte) ((val
>> 8) & 0xff),
67 (byte) ((val
>> 16) & 0xff),
68 (byte) ((val
>> 24) & 0xff)
72 static private byte[] Trim (byte[] array
)
74 for (int i
=0; i
< array
.Length
; i
++) {
75 if (array
[i
] != 0x00) {
76 byte[] result
= new byte [array
.Length
- i
];
77 Buffer
.BlockCopy (array
, i
, result
, 0, result
.Length
);
84 // convert the key from PRIVATEKEYBLOB to RSA
85 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
86 // e.g. SNK files, PVK files
87 static public RSA
FromCapiPrivateKeyBlob (byte[] blob
)
89 return FromCapiPrivateKeyBlob (blob
, 0);
92 static public RSA
FromCapiPrivateKeyBlob (byte[] blob
, int offset
)
95 throw new ArgumentNullException ("blob");
96 if (offset
>= blob
.Length
)
97 throw new ArgumentException ("blob is too small.");
100 if ((blob
[offset
] != 0x07) || // PRIVATEKEYBLOB (0x07)
101 (blob
[offset
+1] != 0x02) || // Version (0x02)
102 (blob
[offset
+2] != 0x00) || // Reserved (word)
103 (blob
[offset
+3] != 0x00) ||
104 (ToUInt32LE (blob
, offset
+8) != 0x32415352)) // DWORD magic = RSA2
105 throw new CryptographicException ("Invalid blob header");
107 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
108 // int algId = ToInt32LE (blob, offset+4);
111 int bitLen
= ToInt32LE (blob
, offset
+12);
113 // DWORD public exponent
114 RSAParameters rsap
= new RSAParameters ();
115 byte[] exp
= new byte [4];
116 Buffer
.BlockCopy (blob
, offset
+16, exp
, 0, 4);
118 rsap
.Exponent
= Trim (exp
);
121 // BYTE modulus[rsapubkey.bitlen/8];
122 int byteLen
= (bitLen
>> 3);
123 rsap
.Modulus
= new byte [byteLen
];
124 Buffer
.BlockCopy (blob
, pos
, rsap
.Modulus
, 0, byteLen
);
125 Array
.Reverse (rsap
.Modulus
);
128 // BYTE prime1[rsapubkey.bitlen/16];
129 int byteHalfLen
= (byteLen
>> 1);
130 rsap
.P
= new byte [byteHalfLen
];
131 Buffer
.BlockCopy (blob
, pos
, rsap
.P
, 0, byteHalfLen
);
132 Array
.Reverse (rsap
.P
);
135 // BYTE prime2[rsapubkey.bitlen/16];
136 rsap
.Q
= new byte [byteHalfLen
];
137 Buffer
.BlockCopy (blob
, pos
, rsap
.Q
, 0, byteHalfLen
);
138 Array
.Reverse (rsap
.Q
);
141 // BYTE exponent1[rsapubkey.bitlen/16];
142 rsap
.DP
= new byte [byteHalfLen
];
143 Buffer
.BlockCopy (blob
, pos
, rsap
.DP
, 0, byteHalfLen
);
144 Array
.Reverse (rsap
.DP
);
147 // BYTE exponent2[rsapubkey.bitlen/16];
148 rsap
.DQ
= new byte [byteHalfLen
];
149 Buffer
.BlockCopy (blob
, pos
, rsap
.DQ
, 0, byteHalfLen
);
150 Array
.Reverse (rsap
.DQ
);
153 // BYTE coefficient[rsapubkey.bitlen/16];
154 rsap
.InverseQ
= new byte [byteHalfLen
];
155 Buffer
.BlockCopy (blob
, pos
, rsap
.InverseQ
, 0, byteHalfLen
);
156 Array
.Reverse (rsap
.InverseQ
);
159 // ok, this is hackish but CryptoAPI support it so...
160 // note: only works because CRT is used by default
161 // http://bugzilla.ximian.com/show_bug.cgi?id=57941
162 rsap
.D
= new byte [byteLen
]; // must be allocated
163 if (pos
+ byteLen
+ offset
<= blob
.Length
) {
164 // BYTE privateExponent[rsapubkey.bitlen/8];
165 Buffer
.BlockCopy (blob
, pos
, rsap
.D
, 0, byteLen
);
166 Array
.Reverse (rsap
.D
);
169 RSA rsa
= (RSA
)RSA
.Create ();
170 rsa
.ImportParameters (rsap
);
173 catch (Exception e
) {
174 throw new CryptographicException ("Invalid blob.", e
);
178 static public byte[] ToCapiPrivateKeyBlob (RSA rsa
)
180 RSAParameters p
= rsa
.ExportParameters (true);
181 int keyLength
= p
.Modulus
.Length
; // in bytes
182 byte[] blob
= new byte [20 + (keyLength
<< 2) + (keyLength
>> 1)];
184 blob
[0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
185 blob
[1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
186 // [2], [3] // RESERVED - Always 0
187 blob
[5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
188 blob
[8] = 0x52; // Magic - RSA2 (ASCII in hex)
193 byte[] bitlen
= GetBytesLE (keyLength
<< 3);
194 blob
[12] = bitlen
[0]; // bitlen
195 blob
[13] = bitlen
[1];
196 blob
[14] = bitlen
[2];
197 blob
[15] = bitlen
[3];
199 // public exponent (DWORD)
201 int n
= p
.Exponent
.Length
;
203 blob
[pos
++] = p
.Exponent
[--n
];
206 byte[] part
= p
.Modulus
;
207 int len
= part
.Length
;
208 Array
.Reverse (part
, 0, len
);
209 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
214 Array
.Reverse (part
, 0, len
);
215 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
220 Array
.Reverse (part
, 0, len
);
221 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
226 Array
.Reverse (part
, 0, len
);
227 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
232 Array
.Reverse (part
, 0, len
);
233 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
238 Array
.Reverse (part
, 0, len
);
239 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
244 Array
.Reverse (part
, 0, len
);
245 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
250 static public RSA
FromCapiPublicKeyBlob (byte[] blob
)
252 return FromCapiPublicKeyBlob (blob
, 0);
255 static public RSA
FromCapiPublicKeyBlob (byte[] blob
, int offset
)
258 throw new ArgumentNullException ("blob");
259 if (offset
>= blob
.Length
)
260 throw new ArgumentException ("blob is too small.");
263 if ((blob
[offset
] != 0x06) || // PUBLICKEYBLOB (0x06)
264 (blob
[offset
+1] != 0x02) || // Version (0x02)
265 (blob
[offset
+2] != 0x00) || // Reserved (word)
266 (blob
[offset
+3] != 0x00) ||
267 (ToUInt32LE (blob
, offset
+8) != 0x31415352)) // DWORD magic = RSA1
268 throw new CryptographicException ("Invalid blob header");
270 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
271 // int algId = ToInt32LE (blob, offset+4);
274 int bitLen
= ToInt32LE (blob
, offset
+12);
276 // DWORD public exponent
277 RSAParameters rsap
= new RSAParameters ();
278 rsap
.Exponent
= new byte [3];
279 rsap
.Exponent
[0] = blob
[offset
+18];
280 rsap
.Exponent
[1] = blob
[offset
+17];
281 rsap
.Exponent
[2] = blob
[offset
+16];
284 // BYTE modulus[rsapubkey.bitlen/8];
285 int byteLen
= (bitLen
>> 3);
286 rsap
.Modulus
= new byte [byteLen
];
287 Buffer
.BlockCopy (blob
, pos
, rsap
.Modulus
, 0, byteLen
);
288 Array
.Reverse (rsap
.Modulus
);
290 RSA rsa
= (RSA
)RSA
.Create ();
291 rsa
.ImportParameters (rsap
);
294 catch (Exception e
) {
295 throw new CryptographicException ("Invalid blob.", e
);
299 static public byte[] ToCapiPublicKeyBlob (RSA rsa
)
301 RSAParameters p
= rsa
.ExportParameters (false);
302 int keyLength
= p
.Modulus
.Length
; // in bytes
303 byte[] blob
= new byte [20 + keyLength
];
305 blob
[0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
306 blob
[1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
307 // [2], [3] // RESERVED - Always 0
308 blob
[5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
309 blob
[8] = 0x52; // Magic - RSA1 (ASCII in hex)
314 byte[] bitlen
= GetBytesLE (keyLength
<< 3);
315 blob
[12] = bitlen
[0]; // bitlen
316 blob
[13] = bitlen
[1];
317 blob
[14] = bitlen
[2];
318 blob
[15] = bitlen
[3];
320 // public exponent (DWORD)
322 int n
= p
.Exponent
.Length
;
324 blob
[pos
++] = p
.Exponent
[--n
];
327 byte[] part
= p
.Modulus
;
328 int len
= part
.Length
;
329 Array
.Reverse (part
, 0, len
);
330 Buffer
.BlockCopy (part
, 0, blob
, pos
, len
);
337 static public RSA
FromCapiKeyBlob (byte[] blob
)
339 return FromCapiKeyBlob (blob
, 0);
342 static public RSA
FromCapiKeyBlob (byte[] blob
, int offset
)
345 throw new ArgumentNullException ("blob");
346 if (offset
>= blob
.Length
)
347 throw new ArgumentException ("blob is too small.");
349 switch (blob
[offset
]) {
351 // this could be a public key inside an header
352 // like "sn -e" would produce
353 if (blob
[offset
+ 12] == 0x06) {
354 return FromCapiPublicKeyBlob (blob
, offset
+ 12);
358 return FromCapiPublicKeyBlob (blob
, offset
);
360 return FromCapiPrivateKeyBlob (blob
, offset
);
362 throw new CryptographicException ("Unknown blob format.");
365 static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair
, bool includePrivateKey
)
368 throw new ArgumentNullException ("keypair");
370 // check between RSA and DSA (and potentially others like DH)
372 return ToCapiKeyBlob ((RSA
)keypair
, includePrivateKey
);
377 static public byte[] ToCapiKeyBlob (RSA rsa
, bool includePrivateKey
)
380 throw new ArgumentNullException ("rsa");
382 if (includePrivateKey
)
383 return ToCapiPrivateKeyBlob (rsa
);
385 return ToCapiPublicKeyBlob (rsa
);
388 static public string ToHex (byte[] input
)
393 StringBuilder sb
= new StringBuilder (input
.Length
* 2);
394 foreach (byte b
in input
) {
395 sb
.Append (b
.ToString ("X2", CultureInfo
.InvariantCulture
));
397 return sb
.ToString ();
400 static private byte FromHexChar (char c
)
402 if ((c
>= 'a') && (c
<= 'f'))
403 return (byte) (c
- 'a' + 10);
404 if ((c
>= 'A') && (c
<= 'F'))
405 return (byte) (c
- 'A' + 10);
406 if ((c
>= '0') && (c
<= '9'))
407 return (byte) (c
- '0');
408 throw new ArgumentException ("invalid hex char");
411 static public byte[] FromHex (string hex
)
415 if ((hex
.Length
& 0x1) == 0x1)
416 throw new ArgumentException ("Length must be a multiple of 2");
418 byte[] result
= new byte [hex
.Length
>> 1];
421 while (n
< result
.Length
) {
422 result
[n
] = (byte) (FromHexChar (hex
[i
++]) << 4);
423 result
[n
++] += FromHexChar (hex
[i
++]);