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 using System.Globalization;
13 using System.Security.Cryptography;
16 namespace Mono.Security.Cryptography {
23 sealed class CryptoConvert {
25 private CryptoConvert ()
29 static private int ToInt32LE (byte [] bytes, int offset)
31 return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
34 static private uint ToUInt32LE (byte [] bytes, int offset)
36 return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
39 static private byte [] GetBytesLE (int val)
43 (byte) ((val >> 8) & 0xff),
44 (byte) ((val >> 16) & 0xff),
45 (byte) ((val >> 24) & 0xff)
49 static private byte[] Trim (byte[] array)
51 for (int i=0; i < array.Length; i++) {
52 if (array [i] != 0x00) {
53 byte[] result = new byte [array.Length - i];
54 Buffer.BlockCopy (array, i, result, 0, result.Length);
61 // convert the key from PRIVATEKEYBLOB to RSA
62 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
63 // e.g. SNK files, PVK files
64 static public RSA FromCapiPrivateKeyBlob (byte[] blob)
66 return FromCapiPrivateKeyBlob (blob, 0);
69 static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset)
72 throw new ArgumentNullException ("blob");
73 if (offset >= blob.Length)
74 throw new ArgumentException ("blob is too small.");
77 if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
78 (blob [offset+1] != 0x02) || // Version (0x02)
79 (blob [offset+2] != 0x00) || // Reserved (word)
80 (blob [offset+3] != 0x00) ||
81 (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2
82 throw new CryptographicException ("Invalid blob header");
84 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
85 int algId = ToInt32LE (blob, offset+4);
88 int bitLen = ToInt32LE (blob, offset+12);
90 // DWORD public exponent
91 RSAParameters rsap = new RSAParameters ();
92 byte[] exp = new byte [4];
93 Buffer.BlockCopy (blob, offset+16, exp, 0, 4);
95 rsap.Exponent = Trim (exp);
98 // BYTE modulus[rsapubkey.bitlen/8];
99 int byteLen = (bitLen >> 3);
100 rsap.Modulus = new byte [byteLen];
101 Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
102 Array.Reverse (rsap.Modulus);
105 // BYTE prime1[rsapubkey.bitlen/16];
106 int byteHalfLen = (byteLen >> 1);
107 rsap.P = new byte [byteHalfLen];
108 Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen);
109 Array.Reverse (rsap.P);
112 // BYTE prime2[rsapubkey.bitlen/16];
113 rsap.Q = new byte [byteHalfLen];
114 Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen);
115 Array.Reverse (rsap.Q);
118 // BYTE exponent1[rsapubkey.bitlen/16];
119 rsap.DP = new byte [byteHalfLen];
120 Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen);
121 Array.Reverse (rsap.DP);
124 // BYTE exponent2[rsapubkey.bitlen/16];
125 rsap.DQ = new byte [byteHalfLen];
126 Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen);
127 Array.Reverse (rsap.DQ);
130 // BYTE coefficient[rsapubkey.bitlen/16];
131 rsap.InverseQ = new byte [byteHalfLen];
132 Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen);
133 Array.Reverse (rsap.InverseQ);
136 // ok, this is hackish but CryptoAPI support it so...
137 // note: only works because CRT is used by default
138 // http://bugzilla.ximian.com/show_bug.cgi?id=57941
139 rsap.D = new byte [byteLen]; // must be allocated
140 if (pos + byteLen + offset <= blob.Length) {
141 // BYTE privateExponent[rsapubkey.bitlen/8];
142 Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen);
143 Array.Reverse (rsap.D);
146 RSA rsa = (RSA)RSA.Create ();
147 rsa.ImportParameters (rsap);
150 catch (Exception e) {
151 throw new CryptographicException ("Invalid blob.", e);
155 static public byte[] ToCapiPrivateKeyBlob (RSA rsa)
157 RSAParameters p = rsa.ExportParameters (true);
158 int keyLength = p.Modulus.Length; // in bytes
159 byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
161 blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
162 blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
163 // [2], [3] // RESERVED - Always 0
164 blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
165 blob [8] = 0x52; // Magic - RSA2 (ASCII in hex)
170 byte[] bitlen = GetBytesLE (keyLength << 3);
171 blob [12] = bitlen [0]; // bitlen
172 blob [13] = bitlen [1];
173 blob [14] = bitlen [2];
174 blob [15] = bitlen [3];
176 // public exponent (DWORD)
178 int n = p.Exponent.Length;
180 blob [pos++] = p.Exponent [--n];
183 byte[] part = p.Modulus;
184 int len = part.Length;
185 Array.Reverse (part, 0, len);
186 Buffer.BlockCopy (part, 0, blob, pos, len);
191 Array.Reverse (part, 0, len);
192 Buffer.BlockCopy (part, 0, blob, pos, len);
197 Array.Reverse (part, 0, len);
198 Buffer.BlockCopy (part, 0, blob, pos, len);
203 Array.Reverse (part, 0, len);
204 Buffer.BlockCopy (part, 0, blob, pos, len);
209 Array.Reverse (part, 0, len);
210 Buffer.BlockCopy (part, 0, blob, pos, len);
215 Array.Reverse (part, 0, len);
216 Buffer.BlockCopy (part, 0, blob, pos, len);
221 Array.Reverse (part, 0, len);
222 Buffer.BlockCopy (part, 0, blob, pos, len);
227 static public RSA FromCapiPublicKeyBlob (byte[] blob)
229 return FromCapiPublicKeyBlob (blob, 0);
232 static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset)
235 throw new ArgumentNullException ("blob");
236 if (offset >= blob.Length)
237 throw new ArgumentException ("blob is too small.");
240 if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
241 (blob [offset+1] != 0x02) || // Version (0x02)
242 (blob [offset+2] != 0x00) || // Reserved (word)
243 (blob [offset+3] != 0x00) ||
244 (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1
245 throw new CryptographicException ("Invalid blob header");
247 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
248 int algId = ToInt32LE (blob, offset+4);
251 int bitLen = ToInt32LE (blob, offset+12);
253 // DWORD public exponent
254 RSAParameters rsap = new RSAParameters ();
255 rsap.Exponent = new byte [3];
256 rsap.Exponent [0] = blob [offset+18];
257 rsap.Exponent [1] = blob [offset+17];
258 rsap.Exponent [2] = blob [offset+16];
261 // BYTE modulus[rsapubkey.bitlen/8];
262 int byteLen = (bitLen >> 3);
263 rsap.Modulus = new byte [byteLen];
264 Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
265 Array.Reverse (rsap.Modulus);
267 RSA rsa = (RSA)RSA.Create ();
268 rsa.ImportParameters (rsap);
271 catch (Exception e) {
272 throw new CryptographicException ("Invalid blob.", e);
276 static public byte[] ToCapiPublicKeyBlob (RSA rsa)
278 RSAParameters p = rsa.ExportParameters (false);
279 int keyLength = p.Modulus.Length; // in bytes
280 byte[] blob = new byte [20 + keyLength];
282 blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
283 blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
284 // [2], [3] // RESERVED - Always 0
285 blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
286 blob [8] = 0x52; // Magic - RSA1 (ASCII in hex)
291 byte[] bitlen = GetBytesLE (keyLength << 3);
292 blob [12] = bitlen [0]; // bitlen
293 blob [13] = bitlen [1];
294 blob [14] = bitlen [2];
295 blob [15] = bitlen [3];
297 // public exponent (DWORD)
299 int n = p.Exponent.Length;
301 blob [pos++] = p.Exponent [--n];
304 byte[] part = p.Modulus;
305 int len = part.Length;
306 Array.Reverse (part, 0, len);
307 Buffer.BlockCopy (part, 0, blob, pos, len);
314 static public RSA FromCapiKeyBlob (byte[] blob)
316 return FromCapiKeyBlob (blob, 0);
319 static public RSA FromCapiKeyBlob (byte[] blob, int offset)
322 throw new ArgumentNullException ("blob");
323 if (offset >= blob.Length)
324 throw new ArgumentException ("blob is too small.");
326 switch (blob [offset]) {
328 // this could be a public key inside an header
329 // like "sn -e" would produce
330 if (blob [offset + 12] == 0x06) {
331 return FromCapiPublicKeyBlob (blob, offset + 12);
335 return FromCapiPublicKeyBlob (blob, offset);
337 return FromCapiPrivateKeyBlob (blob, offset);
339 throw new CryptographicException ("Unknown blob format.");
342 static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey)
345 throw new ArgumentNullException ("keypair");
347 // check between RSA and DSA (and potentially others like DH)
349 return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
354 static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey)
357 throw new ArgumentNullException ("rsa");
359 RSAParameters p = rsa.ExportParameters (includePrivateKey);
360 if (includePrivateKey)
361 return ToCapiPrivateKeyBlob (rsa);
363 return ToCapiPublicKeyBlob (rsa);
366 static public string ToHex (byte[] input)
371 StringBuilder sb = new StringBuilder (input.Length * 2);
372 foreach (byte b in input) {
373 sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture));
375 return sb.ToString ();
378 static private byte FromHexChar (char c)
380 if ((c >= 'a') && (c <= 'f'))
381 return (byte) (c - 'a' + 10);
382 if ((c >= 'A') && (c <= 'F'))
383 return (byte) (c - 'A' + 10);
384 if ((c >= '0') && (c <= '9'))
385 return (byte) (c - '0');
386 throw new ArgumentException ("invalid hex char");
389 static public byte[] FromHex (string hex)
393 if ((hex.Length & 0x1) == 0x1)
394 throw new ArgumentException ("Length must be a multiple of 2");
396 byte[] result = new byte [hex.Length >> 1];
399 while (n < result.Length) {
400 result [n] = (byte) (FromHexChar (hex [i++]) << 4);
401 result [n++] += FromHexChar (hex [i++]);