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 byte[] Trim (byte[] array)
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);
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)
41 return FromCapiPrivateKeyBlob (blob, 0);
44 static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset)
47 throw new ArgumentNullException ("blob");
48 if (offset >= blob.Length)
49 throw new ArgumentException ("blob is too small.");
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");
59 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
60 int algId = BitConverter.ToInt32 (blob, offset+4);
63 int bitLen = BitConverter.ToInt32 (blob, offset+12);
65 // DWORD public exponent
66 RSAParameters rsap = new RSAParameters ();
67 byte[] exp = new byte [4];
68 Array.Copy (blob, offset+16, exp, 0, 4);
70 rsap.Exponent = Trim (exp);
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);
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);
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);
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);
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);
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);
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);
116 RSA rsa = (RSA)RSA.Create ();
117 rsa.ImportParameters (rsap);
120 catch (Exception e) {
121 throw new CryptographicException ("Invalid blob.", e);
125 static public byte[] ToCapiPrivateKeyBlob (RSA rsa)
127 RSAParameters p = rsa.ExportParameters (true);
128 int keyLength = p.Modulus.Length; // in bytes
129 byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
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)
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];
146 // public exponent (DWORD)
148 int n = p.Exponent.Length;
150 blob [pos++] = p.Exponent [--n];
153 byte[] part = p.Modulus;
154 int len = part.Length;
155 Array.Reverse (part, 0, len);
156 Array.Copy (part, 0, blob, pos, len);
161 Array.Reverse (part, 0, len);
162 Array.Copy (part, 0, blob, pos, len);
167 Array.Reverse (part, 0, len);
168 Array.Copy (part, 0, blob, pos, len);
173 Array.Reverse (part, 0, len);
174 Array.Copy (part, 0, blob, pos, len);
179 Array.Reverse (part, 0, len);
180 Array.Copy (part, 0, blob, pos, len);
185 Array.Reverse (part, 0, len);
186 Array.Copy (part, 0, blob, pos, len);
191 Array.Reverse (part, 0, len);
192 Array.Copy (part, 0, blob, pos, len);
197 static public RSA FromCapiPublicKeyBlob (byte[] blob)
199 return FromCapiPublicKeyBlob (blob, 0);
202 static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset)
205 throw new ArgumentNullException ("blob");
206 if (offset >= blob.Length)
207 throw new ArgumentException ("blob is too small.");
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");
217 // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
218 int algId = BitConverter.ToInt32 (blob, offset+4);
221 int bitLen = BitConverter.ToInt32 (blob, offset+12);
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];
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);
237 RSA rsa = (RSA)RSA.Create ();
238 rsa.ImportParameters (rsap);
241 catch (Exception e) {
242 throw new CryptographicException ("Invalid blob.", e);
246 static public byte[] ToCapiPublicKeyBlob (RSA rsa)
248 RSAParameters p = rsa.ExportParameters (false);
249 int keyLength = p.Modulus.Length; // in bytes
250 byte[] blob = new byte [20 + keyLength];
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)
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];
267 // public exponent (DWORD)
269 int n = p.Exponent.Length;
271 blob [pos++] = p.Exponent [--n];
274 byte[] part = p.Modulus;
275 int len = part.Length;
276 Array.Reverse (part, 0, len);
277 Array.Copy (part, 0, blob, pos, len);
284 static public RSA FromCapiKeyBlob (byte[] blob)
286 return FromCapiKeyBlob (blob, 0);
289 static public RSA FromCapiKeyBlob (byte[] blob, int offset)
292 throw new ArgumentNullException ("blob");
293 if (offset >= blob.Length)
294 throw new ArgumentException ("blob is too small.");
296 switch (blob [offset]) {
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);
305 return FromCapiPublicKeyBlob (blob, offset);
307 return FromCapiPrivateKeyBlob (blob, offset);
309 throw new CryptographicException ("Unknown blob format.");
312 static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey)
315 throw new ArgumentNullException ("keypair");
317 // check between RSA and DSA (and potentially others like DH)
319 return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
324 static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey)
327 throw new ArgumentNullException ("rsa");
329 RSAParameters p = rsa.ExportParameters (includePrivateKey);
330 if (includePrivateKey)
331 return ToCapiPrivateKeyBlob (rsa);
333 return ToCapiPublicKeyBlob (rsa);
336 static public string ToHex (byte[] input)
341 StringBuilder sb = new StringBuilder (input.Length * 2);
342 foreach (byte b in input) {
343 sb.Append (b.ToString ("X2"));
345 return sb.ToString ();
348 static private byte FromHexChar (char c)
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");
359 static public byte[] FromHex (string hex)
363 if ((hex.Length & 0x1) == 0x1)
364 throw new ArgumentException ("Length must be a multiple of 2");
366 byte[] result = new byte [hex.Length >> 1];
369 while (n < result.Length) {
370 result [n] = (byte) (FromHexChar (hex [i++]) << 4);
371 result [n++] += FromHexChar (hex [i++]);