/Mono.Security.Cryptography/CryptoConvert.cs

http://github.com/jbevain/cecil · C# · 290 lines · 196 code · 33 blank · 61 comment · 24 complexity · cc49d0fd09b40f12bd4b84d2ffe28de4 MD5 · raw file

  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. // Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.Security.Cryptography;
  31. namespace Mono.Security.Cryptography {
  32. static class CryptoConvert {
  33. static private int ToInt32LE (byte [] bytes, int offset)
  34. {
  35. return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
  36. }
  37. static private uint ToUInt32LE (byte [] bytes, int offset)
  38. {
  39. return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
  40. }
  41. static private byte [] GetBytesLE (int val)
  42. {
  43. return new byte [] {
  44. (byte) (val & 0xff),
  45. (byte) ((val >> 8) & 0xff),
  46. (byte) ((val >> 16) & 0xff),
  47. (byte) ((val >> 24) & 0xff)
  48. };
  49. }
  50. static private byte[] Trim (byte[] array)
  51. {
  52. for (int i=0; i < array.Length; i++) {
  53. if (array [i] != 0x00) {
  54. byte[] result = new byte [array.Length - i];
  55. Buffer.BlockCopy (array, i, result, 0, result.Length);
  56. return result;
  57. }
  58. }
  59. return null;
  60. }
  61. static RSA FromCapiPrivateKeyBlob (byte[] blob, int offset)
  62. {
  63. RSAParameters rsap = new RSAParameters ();
  64. try {
  65. if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
  66. (blob [offset+1] != 0x02) || // Version (0x02)
  67. (blob [offset+2] != 0x00) || // Reserved (word)
  68. (blob [offset+3] != 0x00) ||
  69. (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2
  70. throw new CryptographicException ("Invalid blob header");
  71. // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
  72. // int algId = ToInt32LE (blob, offset+4);
  73. // DWORD bitlen
  74. int bitLen = ToInt32LE (blob, offset+12);
  75. // DWORD public exponent
  76. byte[] exp = new byte [4];
  77. Buffer.BlockCopy (blob, offset+16, exp, 0, 4);
  78. Array.Reverse (exp);
  79. rsap.Exponent = Trim (exp);
  80. int pos = offset+20;
  81. // BYTE modulus[rsapubkey.bitlen/8];
  82. int byteLen = (bitLen >> 3);
  83. rsap.Modulus = new byte [byteLen];
  84. Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
  85. Array.Reverse (rsap.Modulus);
  86. pos += byteLen;
  87. // BYTE prime1[rsapubkey.bitlen/16];
  88. int byteHalfLen = (byteLen >> 1);
  89. rsap.P = new byte [byteHalfLen];
  90. Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen);
  91. Array.Reverse (rsap.P);
  92. pos += byteHalfLen;
  93. // BYTE prime2[rsapubkey.bitlen/16];
  94. rsap.Q = new byte [byteHalfLen];
  95. Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen);
  96. Array.Reverse (rsap.Q);
  97. pos += byteHalfLen;
  98. // BYTE exponent1[rsapubkey.bitlen/16];
  99. rsap.DP = new byte [byteHalfLen];
  100. Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen);
  101. Array.Reverse (rsap.DP);
  102. pos += byteHalfLen;
  103. // BYTE exponent2[rsapubkey.bitlen/16];
  104. rsap.DQ = new byte [byteHalfLen];
  105. Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen);
  106. Array.Reverse (rsap.DQ);
  107. pos += byteHalfLen;
  108. // BYTE coefficient[rsapubkey.bitlen/16];
  109. rsap.InverseQ = new byte [byteHalfLen];
  110. Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen);
  111. Array.Reverse (rsap.InverseQ);
  112. pos += byteHalfLen;
  113. // ok, this is hackish but CryptoAPI support it so...
  114. // note: only works because CRT is used by default
  115. // http://bugzilla.ximian.com/show_bug.cgi?id=57941
  116. rsap.D = new byte [byteLen]; // must be allocated
  117. if (pos + byteLen + offset <= blob.Length) {
  118. // BYTE privateExponent[rsapubkey.bitlen/8];
  119. Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen);
  120. Array.Reverse (rsap.D);
  121. }
  122. }
  123. catch (Exception e) {
  124. throw new CryptographicException ("Invalid blob.", e);
  125. }
  126. RSA rsa = null;
  127. try {
  128. rsa = RSA.Create ();
  129. rsa.ImportParameters (rsap);
  130. }
  131. catch (CryptographicException) {
  132. // this may cause problem when this code is run under
  133. // the SYSTEM identity on Windows (e.g. ASP.NET). See
  134. // http://bugzilla.ximian.com/show_bug.cgi?id=77559
  135. bool throws = false;
  136. try {
  137. CspParameters csp = new CspParameters ();
  138. csp.Flags = CspProviderFlags.UseMachineKeyStore;
  139. rsa = new RSACryptoServiceProvider (csp);
  140. rsa.ImportParameters (rsap);
  141. }
  142. catch {
  143. throws = true;
  144. }
  145. if (throws) {
  146. // rethrow original, not the latter, exception if this fails
  147. throw;
  148. }
  149. }
  150. return rsa;
  151. }
  152. static RSA FromCapiPublicKeyBlob (byte[] blob, int offset)
  153. {
  154. try {
  155. if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
  156. (blob [offset+1] != 0x02) || // Version (0x02)
  157. (blob [offset+2] != 0x00) || // Reserved (word)
  158. (blob [offset+3] != 0x00) ||
  159. (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1
  160. throw new CryptographicException ("Invalid blob header");
  161. // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
  162. // int algId = ToInt32LE (blob, offset+4);
  163. // DWORD bitlen
  164. int bitLen = ToInt32LE (blob, offset+12);
  165. // DWORD public exponent
  166. RSAParameters rsap = new RSAParameters ();
  167. rsap.Exponent = new byte [3];
  168. rsap.Exponent [0] = blob [offset+18];
  169. rsap.Exponent [1] = blob [offset+17];
  170. rsap.Exponent [2] = blob [offset+16];
  171. int pos = offset+20;
  172. // BYTE modulus[rsapubkey.bitlen/8];
  173. int byteLen = (bitLen >> 3);
  174. rsap.Modulus = new byte [byteLen];
  175. Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
  176. Array.Reverse (rsap.Modulus);
  177. RSA rsa = null;
  178. try {
  179. rsa = RSA.Create ();
  180. rsa.ImportParameters (rsap);
  181. }
  182. catch (CryptographicException) {
  183. // this may cause problem when this code is run under
  184. // the SYSTEM identity on Windows (e.g. ASP.NET). See
  185. // http://bugzilla.ximian.com/show_bug.cgi?id=77559
  186. CspParameters csp = new CspParameters ();
  187. csp.Flags = CspProviderFlags.UseMachineKeyStore;
  188. rsa = new RSACryptoServiceProvider (csp);
  189. rsa.ImportParameters (rsap);
  190. }
  191. return rsa;
  192. }
  193. catch (Exception e) {
  194. throw new CryptographicException ("Invalid blob.", e);
  195. }
  196. }
  197. // PRIVATEKEYBLOB
  198. // PUBLICKEYBLOB
  199. static public RSA FromCapiKeyBlob (byte[] blob)
  200. {
  201. return FromCapiKeyBlob (blob, 0);
  202. }
  203. static public RSA FromCapiKeyBlob (byte[] blob, int offset)
  204. {
  205. if (blob == null)
  206. throw new ArgumentNullException ("blob");
  207. if (offset >= blob.Length)
  208. throw new ArgumentException ("blob is too small.");
  209. switch (blob [offset]) {
  210. case 0x00:
  211. // this could be a public key inside an header
  212. // like "sn -e" would produce
  213. if (blob [offset + 12] == 0x06) {
  214. return FromCapiPublicKeyBlob (blob, offset + 12);
  215. }
  216. break;
  217. case 0x06:
  218. return FromCapiPublicKeyBlob (blob, offset);
  219. case 0x07:
  220. return FromCapiPrivateKeyBlob (blob, offset);
  221. }
  222. throw new CryptographicException ("Unknown blob format.");
  223. }
  224. static public byte[] ToCapiPublicKeyBlob (RSA rsa)
  225. {
  226. RSAParameters p = rsa.ExportParameters (false);
  227. int keyLength = p.Modulus.Length; // in bytes
  228. byte[] blob = new byte [20 + keyLength];
  229. blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
  230. blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
  231. // [2], [3] // RESERVED - Always 0
  232. blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
  233. blob [8] = 0x52; // Magic - RSA1 (ASCII in hex)
  234. blob [9] = 0x53;
  235. blob [10] = 0x41;
  236. blob [11] = 0x31;
  237. byte[] bitlen = GetBytesLE (keyLength << 3);
  238. blob [12] = bitlen [0]; // bitlen
  239. blob [13] = bitlen [1];
  240. blob [14] = bitlen [2];
  241. blob [15] = bitlen [3];
  242. // public exponent (DWORD)
  243. int pos = 16;
  244. int n = p.Exponent.Length;
  245. while (n > 0)
  246. blob [pos++] = p.Exponent [--n];
  247. // modulus
  248. pos = 20;
  249. byte[] part = p.Modulus;
  250. int len = part.Length;
  251. Array.Reverse (part, 0, len);
  252. Buffer.BlockCopy (part, 0, blob, pos, len);
  253. pos += len;
  254. return blob;
  255. }
  256. }
  257. }