CryptoConvert.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  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.Globalization;
  31. using System.Security.Cryptography;
  32. using System.Text;
  33. namespace Mono.Security.Cryptography {
  34. #if INSIDE_CORLIB
  35. internal
  36. #else
  37. public
  38. #endif
  39. sealed class CryptoConvert {
  40. private CryptoConvert ()
  41. {
  42. }
  43. static private int ToInt32LE (byte [] bytes, int offset)
  44. {
  45. return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
  46. }
  47. static private uint ToUInt32LE (byte [] bytes, int offset)
  48. {
  49. return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
  50. }
  51. static private byte [] GetBytesLE (int val)
  52. {
  53. return new byte [] {
  54. (byte) (val & 0xff),
  55. (byte) ((val >> 8) & 0xff),
  56. (byte) ((val >> 16) & 0xff),
  57. (byte) ((val >> 24) & 0xff)
  58. };
  59. }
  60. static private byte[] Trim (byte[] array)
  61. {
  62. for (int i=0; i < array.Length; i++) {
  63. if (array [i] != 0x00) {
  64. byte[] result = new byte [array.Length - i];
  65. Buffer.BlockCopy (array, i, result, 0, result.Length);
  66. return result;
  67. }
  68. }
  69. return null;
  70. }
  71. // convert the key from PRIVATEKEYBLOB to RSA
  72. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
  73. // e.g. SNK files, PVK files
  74. static public RSA FromCapiPrivateKeyBlob (byte[] blob)
  75. {
  76. return FromCapiPrivateKeyBlob (blob, 0);
  77. }
  78. static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset)
  79. {
  80. if (blob == null)
  81. throw new ArgumentNullException ("blob");
  82. if (offset >= blob.Length)
  83. throw new ArgumentException ("blob is too small.");
  84. RSAParameters rsap = new RSAParameters ();
  85. try {
  86. if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
  87. (blob [offset+1] != 0x02) || // Version (0x02)
  88. (blob [offset+2] != 0x00) || // Reserved (word)
  89. (blob [offset+3] != 0x00) ||
  90. (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2
  91. throw new CryptographicException ("Invalid blob header");
  92. // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
  93. // int algId = ToInt32LE (blob, offset+4);
  94. // DWORD bitlen
  95. int bitLen = ToInt32LE (blob, offset+12);
  96. // DWORD public exponent
  97. byte[] exp = new byte [4];
  98. Buffer.BlockCopy (blob, offset+16, exp, 0, 4);
  99. Array.Reverse (exp);
  100. rsap.Exponent = Trim (exp);
  101. int pos = offset+20;
  102. // BYTE modulus[rsapubkey.bitlen/8];
  103. int byteLen = (bitLen >> 3);
  104. rsap.Modulus = new byte [byteLen];
  105. Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
  106. Array.Reverse (rsap.Modulus);
  107. pos += byteLen;
  108. // BYTE prime1[rsapubkey.bitlen/16];
  109. int byteHalfLen = (byteLen >> 1);
  110. rsap.P = new byte [byteHalfLen];
  111. Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen);
  112. Array.Reverse (rsap.P);
  113. pos += byteHalfLen;
  114. // BYTE prime2[rsapubkey.bitlen/16];
  115. rsap.Q = new byte [byteHalfLen];
  116. Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen);
  117. Array.Reverse (rsap.Q);
  118. pos += byteHalfLen;
  119. // BYTE exponent1[rsapubkey.bitlen/16];
  120. rsap.DP = new byte [byteHalfLen];
  121. Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen);
  122. Array.Reverse (rsap.DP);
  123. pos += byteHalfLen;
  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);
  128. pos += byteHalfLen;
  129. // BYTE coefficient[rsapubkey.bitlen/16];
  130. rsap.InverseQ = new byte [byteHalfLen];
  131. Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen);
  132. Array.Reverse (rsap.InverseQ);
  133. pos += byteHalfLen;
  134. // ok, this is hackish but CryptoAPI support it so...
  135. // note: only works because CRT is used by default
  136. // http://bugzilla.ximian.com/show_bug.cgi?id=57941
  137. rsap.D = new byte [byteLen]; // must be allocated
  138. if (pos + byteLen + offset <= blob.Length) {
  139. // BYTE privateExponent[rsapubkey.bitlen/8];
  140. Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen);
  141. Array.Reverse (rsap.D);
  142. }
  143. }
  144. catch (Exception e) {
  145. throw new CryptographicException ("Invalid blob.", e);
  146. }
  147. #if INSIDE_CORLIB && MOBILE
  148. RSA rsa = RSA.Create ();
  149. rsa.ImportParameters (rsap);
  150. #else
  151. RSA rsa = null;
  152. try {
  153. rsa = RSA.Create ();
  154. rsa.ImportParameters (rsap);
  155. }
  156. catch (CryptographicException ce) {
  157. // this may cause problem when this code is run under
  158. // the SYSTEM identity on Windows (e.g. ASP.NET). See
  159. // http://bugzilla.ximian.com/show_bug.cgi?id=77559
  160. try {
  161. CspParameters csp = new CspParameters ();
  162. csp.Flags = CspProviderFlags.UseMachineKeyStore;
  163. rsa = new RSACryptoServiceProvider (csp);
  164. rsa.ImportParameters (rsap);
  165. }
  166. catch {
  167. // rethrow original, not the later, exception if this fails
  168. throw ce;
  169. }
  170. }
  171. #endif
  172. return rsa;
  173. }
  174. static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob)
  175. {
  176. return FromCapiPrivateKeyBlobDSA (blob, 0);
  177. }
  178. static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset)
  179. {
  180. if (blob == null)
  181. throw new ArgumentNullException ("blob");
  182. if (offset >= blob.Length)
  183. throw new ArgumentException ("blob is too small.");
  184. DSAParameters dsap = new DSAParameters ();
  185. try {
  186. if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
  187. (blob [offset + 1] != 0x02) || // Version (0x02)
  188. (blob [offset + 2] != 0x00) || // Reserved (word)
  189. (blob [offset + 3] != 0x00) ||
  190. (ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic
  191. throw new CryptographicException ("Invalid blob header");
  192. int bitlen = ToInt32LE (blob, offset + 12);
  193. int bytelen = bitlen >> 3;
  194. int pos = offset + 16;
  195. dsap.P = new byte [bytelen];
  196. Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen);
  197. Array.Reverse (dsap.P);
  198. pos += bytelen;
  199. dsap.Q = new byte [20];
  200. Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20);
  201. Array.Reverse (dsap.Q);
  202. pos += 20;
  203. dsap.G = new byte [bytelen];
  204. Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen);
  205. Array.Reverse (dsap.G);
  206. pos += bytelen;
  207. dsap.X = new byte [20];
  208. Buffer.BlockCopy (blob, pos, dsap.X, 0, 20);
  209. Array.Reverse (dsap.X);
  210. pos += 20;
  211. dsap.Counter = ToInt32LE (blob, pos);
  212. pos += 4;
  213. dsap.Seed = new byte [20];
  214. Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20);
  215. Array.Reverse (dsap.Seed);
  216. pos += 20;
  217. }
  218. catch (Exception e) {
  219. throw new CryptographicException ("Invalid blob.", e);
  220. }
  221. #if INSIDE_CORLIB && MOBILE
  222. DSA dsa = (DSA)DSA.Create ();
  223. dsa.ImportParameters (dsap);
  224. #else
  225. DSA dsa = null;
  226. try {
  227. dsa = (DSA)DSA.Create ();
  228. dsa.ImportParameters (dsap);
  229. }
  230. catch (CryptographicException ce) {
  231. // this may cause problem when this code is run under
  232. // the SYSTEM identity on Windows (e.g. ASP.NET). See
  233. // http://bugzilla.ximian.com/show_bug.cgi?id=77559
  234. try {
  235. CspParameters csp = new CspParameters ();
  236. csp.Flags = CspProviderFlags.UseMachineKeyStore;
  237. dsa = new DSACryptoServiceProvider (csp);
  238. dsa.ImportParameters (dsap);
  239. }
  240. catch {
  241. // rethrow original, not the later, exception if this fails
  242. throw ce;
  243. }
  244. }
  245. #endif
  246. return dsa;
  247. }
  248. static public byte[] ToCapiPrivateKeyBlob (RSA rsa)
  249. {
  250. RSAParameters p = rsa.ExportParameters (true);
  251. int keyLength = p.Modulus.Length; // in bytes
  252. byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
  253. blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
  254. blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
  255. // [2], [3] // RESERVED - Always 0
  256. blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
  257. blob [8] = 0x52; // Magic - RSA2 (ASCII in hex)
  258. blob [9] = 0x53;
  259. blob [10] = 0x41;
  260. blob [11] = 0x32;
  261. byte[] bitlen = GetBytesLE (keyLength << 3);
  262. blob [12] = bitlen [0]; // bitlen
  263. blob [13] = bitlen [1];
  264. blob [14] = bitlen [2];
  265. blob [15] = bitlen [3];
  266. // public exponent (DWORD)
  267. int pos = 16;
  268. int n = p.Exponent.Length;
  269. while (n > 0)
  270. blob [pos++] = p.Exponent [--n];
  271. // modulus
  272. pos = 20;
  273. byte[] part = p.Modulus;
  274. int len = part.Length;
  275. Array.Reverse (part, 0, len);
  276. Buffer.BlockCopy (part, 0, blob, pos, len);
  277. pos += len;
  278. // private key
  279. part = p.P;
  280. len = part.Length;
  281. Array.Reverse (part, 0, len);
  282. Buffer.BlockCopy (part, 0, blob, pos, len);
  283. pos += len;
  284. part = p.Q;
  285. len = part.Length;
  286. Array.Reverse (part, 0, len);
  287. Buffer.BlockCopy (part, 0, blob, pos, len);
  288. pos += len;
  289. part = p.DP;
  290. len = part.Length;
  291. Array.Reverse (part, 0, len);
  292. Buffer.BlockCopy (part, 0, blob, pos, len);
  293. pos += len;
  294. part = p.DQ;
  295. len = part.Length;
  296. Array.Reverse (part, 0, len);
  297. Buffer.BlockCopy (part, 0, blob, pos, len);
  298. pos += len;
  299. part = p.InverseQ;
  300. len = part.Length;
  301. Array.Reverse (part, 0, len);
  302. Buffer.BlockCopy (part, 0, blob, pos, len);
  303. pos += len;
  304. part = p.D;
  305. len = part.Length;
  306. Array.Reverse (part, 0, len);
  307. Buffer.BlockCopy (part, 0, blob, pos, len);
  308. return blob;
  309. }
  310. static public byte[] ToCapiPrivateKeyBlob (DSA dsa)
  311. {
  312. DSAParameters p = dsa.ExportParameters (true);
  313. int keyLength = p.P.Length; // in bytes
  314. // header + P + Q + G + X + count + seed
  315. byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20];
  316. blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
  317. blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
  318. // [2], [3] // RESERVED - Always 0
  319. blob [5] = 0x22; // ALGID
  320. blob [8] = 0x44; // Magic
  321. blob [9] = 0x53;
  322. blob [10] = 0x53;
  323. blob [11] = 0x32;
  324. byte[] bitlen = GetBytesLE (keyLength << 3);
  325. blob [12] = bitlen [0];
  326. blob [13] = bitlen [1];
  327. blob [14] = bitlen [2];
  328. blob [15] = bitlen [3];
  329. int pos = 16;
  330. byte[] part = p.P;
  331. Array.Reverse (part);
  332. Buffer.BlockCopy (part, 0, blob, pos, keyLength);
  333. pos += keyLength;
  334. part = p.Q;
  335. Array.Reverse (part);
  336. Buffer.BlockCopy (part, 0, blob, pos, 20);
  337. pos += 20;
  338. part = p.G;
  339. Array.Reverse (part);
  340. Buffer.BlockCopy (part, 0, blob, pos, keyLength);
  341. pos += keyLength;
  342. part = p.X;
  343. Array.Reverse (part);
  344. Buffer.BlockCopy (part, 0, blob, pos, 20);
  345. pos += 20;
  346. Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4);
  347. pos += 4;
  348. part = p.Seed;
  349. Array.Reverse (part);
  350. Buffer.BlockCopy (part, 0, blob, pos, 20);
  351. return blob;
  352. }
  353. static public RSA FromCapiPublicKeyBlob (byte[] blob)
  354. {
  355. return FromCapiPublicKeyBlob (blob, 0);
  356. }
  357. static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset)
  358. {
  359. if (blob == null)
  360. throw new ArgumentNullException ("blob");
  361. if (offset >= blob.Length)
  362. throw new ArgumentException ("blob is too small.");
  363. try {
  364. if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
  365. (blob [offset+1] != 0x02) || // Version (0x02)
  366. (blob [offset+2] != 0x00) || // Reserved (word)
  367. (blob [offset+3] != 0x00) ||
  368. (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1
  369. throw new CryptographicException ("Invalid blob header");
  370. // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
  371. // int algId = ToInt32LE (blob, offset+4);
  372. // DWORD bitlen
  373. int bitLen = ToInt32LE (blob, offset+12);
  374. // DWORD public exponent
  375. RSAParameters rsap = new RSAParameters ();
  376. rsap.Exponent = new byte [3];
  377. rsap.Exponent [0] = blob [offset+18];
  378. rsap.Exponent [1] = blob [offset+17];
  379. rsap.Exponent [2] = blob [offset+16];
  380. int pos = offset+20;
  381. // BYTE modulus[rsapubkey.bitlen/8];
  382. int byteLen = (bitLen >> 3);
  383. rsap.Modulus = new byte [byteLen];
  384. Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
  385. Array.Reverse (rsap.Modulus);
  386. #if INSIDE_CORLIB && MOBILE
  387. RSA rsa = RSA.Create ();
  388. rsa.ImportParameters (rsap);
  389. #else
  390. RSA rsa = null;
  391. try {
  392. rsa = RSA.Create ();
  393. rsa.ImportParameters (rsap);
  394. }
  395. catch (CryptographicException) {
  396. // this may cause problem when this code is run under
  397. // the SYSTEM identity on Windows (e.g. ASP.NET). See
  398. // http://bugzilla.ximian.com/show_bug.cgi?id=77559
  399. CspParameters csp = new CspParameters ();
  400. csp.Flags = CspProviderFlags.UseMachineKeyStore;
  401. rsa = new RSACryptoServiceProvider (csp);
  402. rsa.ImportParameters (rsap);
  403. }
  404. #endif
  405. return rsa;
  406. }
  407. catch (Exception e) {
  408. throw new CryptographicException ("Invalid blob.", e);
  409. }
  410. }
  411. static public DSA FromCapiPublicKeyBlobDSA (byte[] blob)
  412. {
  413. return FromCapiPublicKeyBlobDSA (blob, 0);
  414. }
  415. static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset)
  416. {
  417. if (blob == null)
  418. throw new ArgumentNullException ("blob");
  419. if (offset >= blob.Length)
  420. throw new ArgumentException ("blob is too small.");
  421. try {
  422. if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
  423. (blob [offset + 1] != 0x02) || // Version (0x02)
  424. (blob [offset + 2] != 0x00) || // Reserved (word)
  425. (blob [offset + 3] != 0x00) ||
  426. (ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic
  427. throw new CryptographicException ("Invalid blob header");
  428. int bitlen = ToInt32LE (blob, offset + 12);
  429. DSAParameters dsap = new DSAParameters ();
  430. int bytelen = bitlen >> 3;
  431. int pos = offset + 16;
  432. dsap.P = new byte [bytelen];
  433. Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen);
  434. Array.Reverse (dsap.P);
  435. pos += bytelen;
  436. dsap.Q = new byte [20];
  437. Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20);
  438. Array.Reverse (dsap.Q);
  439. pos += 20;
  440. dsap.G = new byte [bytelen];
  441. Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen);
  442. Array.Reverse (dsap.G);
  443. pos += bytelen;
  444. dsap.Y = new byte [bytelen];
  445. Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen);
  446. Array.Reverse (dsap.Y);
  447. pos += bytelen;
  448. dsap.Counter = ToInt32LE (blob, pos);
  449. pos += 4;
  450. dsap.Seed = new byte [20];
  451. Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20);
  452. Array.Reverse (dsap.Seed);
  453. pos += 20;
  454. DSA dsa = (DSA)DSA.Create ();
  455. dsa.ImportParameters (dsap);
  456. return dsa;
  457. }
  458. catch (Exception e) {
  459. throw new CryptographicException ("Invalid blob.", e);
  460. }
  461. }
  462. static public byte[] ToCapiPublicKeyBlob (RSA rsa)
  463. {
  464. RSAParameters p = rsa.ExportParameters (false);
  465. int keyLength = p.Modulus.Length; // in bytes
  466. byte[] blob = new byte [20 + keyLength];
  467. blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
  468. blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
  469. // [2], [3] // RESERVED - Always 0
  470. blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
  471. blob [8] = 0x52; // Magic - RSA1 (ASCII in hex)
  472. blob [9] = 0x53;
  473. blob [10] = 0x41;
  474. blob [11] = 0x31;
  475. byte[] bitlen = GetBytesLE (keyLength << 3);
  476. blob [12] = bitlen [0]; // bitlen
  477. blob [13] = bitlen [1];
  478. blob [14] = bitlen [2];
  479. blob [15] = bitlen [3];
  480. // public exponent (DWORD)
  481. int pos = 16;
  482. int n = p.Exponent.Length;
  483. while (n > 0)
  484. blob [pos++] = p.Exponent [--n];
  485. // modulus
  486. pos = 20;
  487. byte[] part = p.Modulus;
  488. int len = part.Length;
  489. Array.Reverse (part, 0, len);
  490. Buffer.BlockCopy (part, 0, blob, pos, len);
  491. pos += len;
  492. return blob;
  493. }
  494. static public byte[] ToCapiPublicKeyBlob (DSA dsa)
  495. {
  496. DSAParameters p = dsa.ExportParameters (false);
  497. int keyLength = p.P.Length; // in bytes
  498. // header + P + Q + G + Y + count + seed
  499. byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20];
  500. blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
  501. blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
  502. // [2], [3] // RESERVED - Always 0
  503. blob [5] = 0x22; // ALGID
  504. blob [8] = 0x44; // Magic
  505. blob [9] = 0x53;
  506. blob [10] = 0x53;
  507. blob [11] = 0x31;
  508. byte[] bitlen = GetBytesLE (keyLength << 3);
  509. blob [12] = bitlen [0];
  510. blob [13] = bitlen [1];
  511. blob [14] = bitlen [2];
  512. blob [15] = bitlen [3];
  513. int pos = 16;
  514. byte[] part;
  515. part = p.P;
  516. Array.Reverse (part);
  517. Buffer.BlockCopy (part, 0, blob, pos, keyLength);
  518. pos += keyLength;
  519. part = p.Q;
  520. Array.Reverse (part);
  521. Buffer.BlockCopy (part, 0, blob, pos, 20);
  522. pos += 20;
  523. part = p.G;
  524. Array.Reverse (part);
  525. Buffer.BlockCopy (part, 0, blob, pos, keyLength);
  526. pos += keyLength;
  527. part = p.Y;
  528. Array.Reverse (part);
  529. Buffer.BlockCopy (part, 0, blob, pos, keyLength);
  530. pos += keyLength;
  531. Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4);
  532. pos += 4;
  533. part = p.Seed;
  534. Array.Reverse (part);
  535. Buffer.BlockCopy (part, 0, blob, pos, 20);
  536. return blob;
  537. }
  538. // PRIVATEKEYBLOB
  539. // PUBLICKEYBLOB
  540. static public RSA FromCapiKeyBlob (byte[] blob)
  541. {
  542. return FromCapiKeyBlob (blob, 0);
  543. }
  544. static public RSA FromCapiKeyBlob (byte[] blob, int offset)
  545. {
  546. if (blob == null)
  547. throw new ArgumentNullException ("blob");
  548. if (offset >= blob.Length)
  549. throw new ArgumentException ("blob is too small.");
  550. switch (blob [offset]) {
  551. case 0x00:
  552. // this could be a public key inside an header
  553. // like "sn -e" would produce
  554. if (blob [offset + 12] == 0x06) {
  555. return FromCapiPublicKeyBlob (blob, offset + 12);
  556. }
  557. break;
  558. case 0x06:
  559. return FromCapiPublicKeyBlob (blob, offset);
  560. case 0x07:
  561. return FromCapiPrivateKeyBlob (blob, offset);
  562. }
  563. throw new CryptographicException ("Unknown blob format.");
  564. }
  565. static public DSA FromCapiKeyBlobDSA (byte[] blob)
  566. {
  567. return FromCapiKeyBlobDSA (blob, 0);
  568. }
  569. static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset)
  570. {
  571. if (blob == null)
  572. throw new ArgumentNullException ("blob");
  573. if (offset >= blob.Length)
  574. throw new ArgumentException ("blob is too small.");
  575. switch (blob [offset]) {
  576. case 0x06:
  577. return FromCapiPublicKeyBlobDSA (blob, offset);
  578. case 0x07:
  579. return FromCapiPrivateKeyBlobDSA (blob, offset);
  580. }
  581. throw new CryptographicException ("Unknown blob format.");
  582. }
  583. static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey)
  584. {
  585. if (keypair == null)
  586. throw new ArgumentNullException ("keypair");
  587. // check between RSA and DSA (and potentially others like DH)
  588. if (keypair is RSA)
  589. return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
  590. else if (keypair is DSA)
  591. return ToCapiKeyBlob ((DSA)keypair, includePrivateKey);
  592. else
  593. return null; // TODO
  594. }
  595. static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey)
  596. {
  597. if (rsa == null)
  598. throw new ArgumentNullException ("rsa");
  599. if (includePrivateKey)
  600. return ToCapiPrivateKeyBlob (rsa);
  601. else
  602. return ToCapiPublicKeyBlob (rsa);
  603. }
  604. static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey)
  605. {
  606. if (dsa == null)
  607. throw new ArgumentNullException ("dsa");
  608. if (includePrivateKey)
  609. return ToCapiPrivateKeyBlob (dsa);
  610. else
  611. return ToCapiPublicKeyBlob (dsa);
  612. }
  613. static public string ToHex (byte[] input)
  614. {
  615. if (input == null)
  616. return null;
  617. StringBuilder sb = new StringBuilder (input.Length * 2);
  618. foreach (byte b in input) {
  619. sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture));
  620. }
  621. return sb.ToString ();
  622. }
  623. static private byte FromHexChar (char c)
  624. {
  625. if ((c >= 'a') && (c <= 'f'))
  626. return (byte) (c - 'a' + 10);
  627. if ((c >= 'A') && (c <= 'F'))
  628. return (byte) (c - 'A' + 10);
  629. if ((c >= '0') && (c <= '9'))
  630. return (byte) (c - '0');
  631. throw new ArgumentException ("invalid hex char");
  632. }
  633. static public byte[] FromHex (string hex)
  634. {
  635. if (hex == null)
  636. return null;
  637. if ((hex.Length & 0x1) == 0x1)
  638. throw new ArgumentException ("Length must be a multiple of 2");
  639. byte[] result = new byte [hex.Length >> 1];
  640. int n = 0;
  641. int i = 0;
  642. while (n < result.Length) {
  643. result [n] = (byte) (FromHexChar (hex [i++]) << 4);
  644. result [n++] += FromHexChar (hex [i++]);
  645. }
  646. return result;
  647. }
  648. }
  649. }