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.Security.Cryptography;
15 namespace Mono.Security.Cryptography {
24 static private int ToInt32LE (byte [] bytes, int offset)
26 return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
29 static private uint ToUInt32LE (byte [] bytes, int offset)
31 return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
34 static private byte [] GetBytesLE (int val)
38 (byte) ((val >> 8) & 0xff),
39 (byte) ((val >> 16) & 0xff),
40 (byte) ((val >> 24) & 0xff)
44 static private byte[] Trim (byte[] array)
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);
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)
61 return FromCapiPrivateKeyBlob (blob, 0);
64 static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset)
67 throw new ArgumentNullException ("blob");
68 if (offset >= blob.Length)
69 throw new ArgumentException ("blob is too small.");
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");
79 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
80 int algId = ToInt32LE (blob, offset+4);
83 int bitLen = ToInt32LE (blob, offset+12);
85 // DWORD public exponent
86 RSAParameters rsap = new RSAParameters ();
87 byte[] exp = new byte [4];
88 Array.Copy (blob, offset+16, exp, 0, 4);
90 rsap.Exponent = Trim (exp);
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);
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);
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);
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);
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);
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);
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);
136 RSA rsa = (RSA)RSA.Create ();
137 rsa.ImportParameters (rsap);
140 catch (Exception e) {
141 throw new CryptographicException ("Invalid blob.", e);
145 static public byte[] ToCapiPrivateKeyBlob (RSA rsa)
147 RSAParameters p = rsa.ExportParameters (true);
148 int keyLength = p.Modulus.Length; // in bytes
149 byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
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)
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];
166 // public exponent (DWORD)
168 int n = p.Exponent.Length;
170 blob [pos++] = p.Exponent [--n];
173 byte[] part = p.Modulus;
174 int len = part.Length;
175 Array.Reverse (part, 0, len);
176 Array.Copy (part, 0, blob, pos, len);
181 Array.Reverse (part, 0, len);
182 Array.Copy (part, 0, blob, pos, len);
187 Array.Reverse (part, 0, len);
188 Array.Copy (part, 0, blob, pos, len);
193 Array.Reverse (part, 0, len);
194 Array.Copy (part, 0, blob, pos, len);
199 Array.Reverse (part, 0, len);
200 Array.Copy (part, 0, blob, pos, len);
205 Array.Reverse (part, 0, len);
206 Array.Copy (part, 0, blob, pos, len);
211 Array.Reverse (part, 0, len);
212 Array.Copy (part, 0, blob, pos, len);
217 static public RSA FromCapiPublicKeyBlob (byte[] blob)
219 return FromCapiPublicKeyBlob (blob, 0);
222 static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset)
225 throw new ArgumentNullException ("blob");
226 if (offset >= blob.Length)
227 throw new ArgumentException ("blob is too small.");
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");
237 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
238 int algId = ToInt32LE (blob, offset+4);
241 int bitLen = ToInt32LE (blob, offset+12);
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];
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);
257 RSA rsa = (RSA)RSA.Create ();
258 rsa.ImportParameters (rsap);
261 catch (Exception e) {
262 throw new CryptographicException ("Invalid blob.", e);
266 static public byte[] ToCapiPublicKeyBlob (RSA rsa)
268 RSAParameters p = rsa.ExportParameters (false);
269 int keyLength = p.Modulus.Length; // in bytes
270 byte[] blob = new byte [20 + keyLength];
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)
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];
287 // public exponent (DWORD)
289 int n = p.Exponent.Length;
291 blob [pos++] = p.Exponent [--n];
294 byte[] part = p.Modulus;
295 int len = part.Length;
296 Array.Reverse (part, 0, len);
297 Array.Copy (part, 0, blob, pos, len);
304 static public RSA FromCapiKeyBlob (byte[] blob)
306 return FromCapiKeyBlob (blob, 0);
309 static public RSA FromCapiKeyBlob (byte[] blob, int offset)
312 throw new ArgumentNullException ("blob");
313 if (offset >= blob.Length)
314 throw new ArgumentException ("blob is too small.");
316 switch (blob [offset]) {
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);
325 return FromCapiPublicKeyBlob (blob, offset);
327 return FromCapiPrivateKeyBlob (blob, offset);
329 throw new CryptographicException ("Unknown blob format.");
332 static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey)
335 throw new ArgumentNullException ("keypair");
337 // check between RSA and DSA (and potentially others like DH)
339 return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
344 static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey)
347 throw new ArgumentNullException ("rsa");
349 RSAParameters p = rsa.ExportParameters (includePrivateKey);
350 if (includePrivateKey)
351 return ToCapiPrivateKeyBlob (rsa);
353 return ToCapiPublicKeyBlob (rsa);
356 static public string ToHex (byte[] input)
361 StringBuilder sb = new StringBuilder (input.Length * 2);
362 foreach (byte b in input) {
363 sb.Append (b.ToString ("X2"));
365 return sb.ToString ();
368 static private byte FromHexChar (char c)
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");
379 static public byte[] FromHex (string hex)
383 if ((hex.Length & 0x1) == 0x1)
384 throw new ArgumentException ("Length must be a multiple of 2");
386 byte[] result = new byte [hex.Length >> 1];
389 while (n < result.Length) {
390 result [n] = (byte) (FromHexChar (hex [i++]) << 4);
391 result [n++] += FromHexChar (hex [i++]);