DSACryptoServiceProvider.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. //
  2. // System.Security.Cryptography.DSACryptoServiceProvider.cs
  3. //
  4. // Authors:
  5. // Dan Lewis ([email protected])
  6. // Sebastien Pouliot ([email protected])
  7. //
  8. // (C) 2002
  9. // Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com)
  10. // Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
  11. // See bouncycastle.txt for license.
  12. //
  13. using System;
  14. using System.IO;
  15. namespace System.Security.Cryptography {
  16. public class DSACryptoServiceProvider : DSA {
  17. private CspParameters cspParams;
  18. private RandomNumberGenerator rng;
  19. private bool privateKeyExportable = true;
  20. private bool m_disposed = false;
  21. private bool keypairGenerated = false;
  22. private bool persistKey = false;
  23. private BigInteger p;
  24. private BigInteger q;
  25. private BigInteger g;
  26. private BigInteger x; // private key
  27. private BigInteger y;
  28. private BigInteger j;
  29. private BigInteger seed;
  30. private int counter;
  31. public DSACryptoServiceProvider ()
  32. {
  33. // Here it's not clear if we need to generate a keypair
  34. // (note: MS implementation generates a keypair in this case).
  35. // However we:
  36. // (a) often use this constructor to import an existing keypair.
  37. // (b) take a LOT of time to generate the DSA group
  38. // So we'll generate the keypair only when (and if) it's being
  39. // used (or exported). This should save us a lot of time (at
  40. // least in the unit tests).
  41. Common (null);
  42. }
  43. public DSACryptoServiceProvider (CspParameters parameters)
  44. {
  45. Common (parameters);
  46. // no keypair generation done at this stage
  47. }
  48. // This constructor will generate a new keypair
  49. public DSACryptoServiceProvider (int dwKeySize)
  50. {
  51. // Here it's clear that we need to generate a new keypair
  52. Common (null);
  53. Generate (dwKeySize);
  54. }
  55. // This constructor will generate a new keypair
  56. public DSACryptoServiceProvider (int dwKeySize, CspParameters parameters)
  57. {
  58. Common (parameters);
  59. Generate (dwKeySize);
  60. }
  61. ~DSACryptoServiceProvider ()
  62. {
  63. Dispose (false);
  64. }
  65. [MonoTODO("Persistance")]
  66. private void Common (CspParameters p)
  67. {
  68. rng = RandomNumberGenerator.Create ();
  69. cspParams = new CspParameters ();
  70. if (p == null) {
  71. // TODO: set default values (for keypair persistance)
  72. }
  73. else {
  74. cspParams = p;
  75. // FIXME: We'll need this to support some kind of persistance
  76. throw new NotSupportedException ("CspParameters not supported");
  77. }
  78. LegalKeySizesValue = new KeySizes [1];
  79. LegalKeySizesValue [0] = new KeySizes (512, 1024, 64);
  80. }
  81. // generate both the group and the keypair
  82. private void Generate (int keyLength)
  83. {
  84. // will throw an exception is key size isn't supported
  85. base.KeySize = keyLength;
  86. GenerateParams (keyLength);
  87. GenerateKeyPair ();
  88. keypairGenerated = true;
  89. }
  90. // this part is quite fast
  91. private void GenerateKeyPair ()
  92. {
  93. x = new BigInteger ();
  94. do {
  95. // size of x (private key) isn't affected by the keysize (512-1024)
  96. x.genRandomBits (160);
  97. }
  98. while ((x == 0) || (x >= q));
  99. // calculate the public key y = g^x % p
  100. y = g.modPow (x, p);
  101. }
  102. private void add (byte[] a, byte[] b, int value)
  103. {
  104. uint x = (uint) ((b [b.Length - 1] & 0xff) + value);
  105. a [b.Length - 1] = (byte)x;
  106. x >>= 8;
  107. for (int i = b.Length - 2; i >= 0; i--) {
  108. x += (uint) (b [i] & 0xff);
  109. a [i] = (byte)x;
  110. x >>= 8;
  111. }
  112. }
  113. private void GenerateParams (int keyLength)
  114. {
  115. byte[] seed = new byte[20];
  116. byte[] part1 = new byte[20];
  117. byte[] part2 = new byte[20];
  118. byte[] u = new byte[20];
  119. SHA1 sha = SHA1.Create ();
  120. int n = (keyLength - 1) / 160;
  121. byte[] w = new byte [keyLength / 8];
  122. bool primesFound = false;
  123. int certainty = 80; // FIPS186-2
  124. while (!primesFound) {
  125. do {
  126. rng.GetBytes (seed);
  127. part1 = sha.ComputeHash (seed);
  128. Array.Copy(seed, 0, part2, 0, seed.Length);
  129. add (part2, seed, 1);
  130. part2 = sha.ComputeHash (part2);
  131. for (int i = 0; i != u.Length; i++)
  132. u [i] = (byte)(part1 [i] ^ part2 [i]);
  133. // first bit must be set (to respect key length)
  134. u[0] |= (byte)0x80;
  135. // last bit must be set (prime are all odds - except 2)
  136. u[19] |= (byte)0x01;
  137. q = new BigInteger (u);
  138. }
  139. while (!q.isProbablePrime (certainty));
  140. counter = 0;
  141. int offset = 2;
  142. while (counter < 4096) {
  143. for (int k = 0; k < n; k++) {
  144. add(part1, seed, offset + k);
  145. part1 = sha.ComputeHash (part1);
  146. Array.Copy (part1, 0, w, w.Length - (k + 1) * part1.Length, part1.Length);
  147. }
  148. add(part1, seed, offset + n);
  149. part1 = sha.ComputeHash (part1);
  150. Array.Copy (part1, part1.Length - ((w.Length - (n) * part1.Length)), w, 0, w.Length - n * part1.Length);
  151. w[0] |= (byte)0x80;
  152. BigInteger x = new BigInteger (w);
  153. BigInteger c = x % (q * 2);
  154. p = x - (c - 1);
  155. if (p.testBit ((uint)(keyLength - 1))) {
  156. if (p.isProbablePrime (certainty)) {
  157. primesFound = true;
  158. break;
  159. }
  160. }
  161. counter += 1;
  162. offset += n + 1;
  163. }
  164. }
  165. // calculate the generator g
  166. BigInteger pMinusOneOverQ = (p - 1) / q;
  167. for (;;) {
  168. BigInteger h = new BigInteger ();
  169. h.genRandomBits (keyLength);
  170. if ((h <= 1) || (h >= (p - 1)))
  171. continue;
  172. g = h.modPow (pMinusOneOverQ, p);
  173. if (g <= 1)
  174. continue;
  175. break;
  176. }
  177. this.seed = new BigInteger (seed);
  178. j = (p - 1) / q;
  179. }
  180. [MonoTODO()]
  181. private bool Validate ()
  182. {
  183. // J is optional
  184. bool okJ = ((j == 0) || (j == ((p - 1) / q)));
  185. // TODO: Validate the key parameters (P, Q, G, J) using the Seed and Counter
  186. return okJ;
  187. }
  188. // DSA isn't used for key exchange
  189. public override string KeyExchangeAlgorithm {
  190. get { return ""; }
  191. }
  192. public override int KeySize {
  193. get { return p.bitCount (); }
  194. }
  195. public override KeySizes[] LegalKeySizes {
  196. get { return LegalKeySizesValue; }
  197. }
  198. public override string SignatureAlgorithm {
  199. get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }
  200. }
  201. [MonoTODO("Persistance")]
  202. public bool PersistKeyInCsp {
  203. get { return persistKey; }
  204. set {
  205. persistKey = value;
  206. // FIXME: We'll need this to support some kind of persistance
  207. if (value)
  208. throw new NotSupportedException ("CspParameters not supported");
  209. }
  210. }
  211. protected override void Dispose (bool disposing)
  212. {
  213. if (!m_disposed) {
  214. // TODO: always zeroize private key
  215. if(disposing) {
  216. // TODO: Dispose managed resources
  217. }
  218. // TODO: Dispose unmanaged resources
  219. }
  220. // call base class
  221. // no need as they all are abstract before us
  222. m_disposed = true;
  223. }
  224. public override byte[] CreateSignature (byte[] rgbHash)
  225. {
  226. return SignHash (rgbHash, "SHA1");
  227. }
  228. public byte[] SignData (byte[] data)
  229. {
  230. return SignData (data, 0, data.Length);
  231. }
  232. public byte[] SignData (byte[] data, int offset, int count)
  233. {
  234. // right now only SHA1 is supported by FIPS186-2
  235. HashAlgorithm hash = SHA1.Create ();
  236. byte[] toBeSigned = hash.ComputeHash (data, offset, count);
  237. return SignHash (toBeSigned, "SHA1");
  238. }
  239. public byte[] SignData (Stream inputStream)
  240. {
  241. // right now only SHA1 is supported by FIPS186-2
  242. HashAlgorithm hash = SHA1.Create ();
  243. byte[] toBeSigned = hash.ComputeHash (inputStream);
  244. return SignHash (toBeSigned, "SHA1");
  245. }
  246. public byte[] SignHash (byte[] rgbHash, string str)
  247. {
  248. if (rgbHash == null)
  249. throw new ArgumentNullException ();
  250. // right now only SHA1 is supported by FIPS186-2
  251. if (str.ToUpper () != "SHA1")
  252. throw new Exception( ); // not documented
  253. if (rgbHash.Length != 20)
  254. throw new Exception (); // not documented
  255. if (!keypairGenerated)
  256. Generate (1024);
  257. BigInteger m = new BigInteger (rgbHash);
  258. // (a) Select a random secret integer k; 0 < k < q.
  259. BigInteger k = new BigInteger ();
  260. k.genRandomBits (160);
  261. while (k >= q)
  262. k.genRandomBits (160);
  263. // (b) Compute r = ( k mod p) mod q
  264. BigInteger r = (g.modPow (k, p)) % q;
  265. // (c) Compute k -1 mod q (e.g., using Algorithm 2.142).
  266. // (d) Compute s = k -1 fh(m) +arg mod q.
  267. BigInteger s = (k.modInverse (q) * (m + x * r)) % q;
  268. // (e) A’s signature for m is the pair (r; s).
  269. byte[] signature = new byte [40];
  270. byte[] part1 = r.getBytes ();
  271. byte[] part2 = s.getBytes ();
  272. Array.Copy (part1, 0, signature, 0, 20);
  273. Array.Copy (part2, 0, signature, 20, 20);
  274. return signature;
  275. }
  276. public bool VerifyData (byte[] rgbData, byte[] rgbSignature)
  277. {
  278. // signature is always 40 bytes (no matter the size of the
  279. // public key). In fact it is 2 times the size of the private
  280. // key (which is 20 bytes for 512 to 1024 bits DSA keypairs)
  281. if (rgbSignature.Length != 40)
  282. throw new Exception(); // not documented
  283. // right now only SHA1 is supported by FIPS186-2
  284. HashAlgorithm hash = SHA1.Create();
  285. byte[] toBeVerified = hash.ComputeHash (rgbData);
  286. return VerifyHash (toBeVerified, "SHA1", rgbSignature);
  287. }
  288. // LAMESPEC: MD5 isn't allowed with DSA
  289. public bool VerifyHash (byte[] rgbHash, string str, byte[] rgbSignature)
  290. {
  291. if (rgbHash == null)
  292. throw new ArgumentNullException ("rgbHash");
  293. if (rgbSignature == null)
  294. throw new ArgumentNullException ("rgbSignature");
  295. if (str == null)
  296. str = "SHA1"; // default value
  297. if (str != "SHA1")
  298. throw new CryptographicException ();
  299. // it would be stupid to verify a signature with a newly
  300. // generated keypair - so we return false
  301. if (!keypairGenerated)
  302. return false;
  303. try {
  304. BigInteger m = new BigInteger (rgbHash);
  305. byte[] half = new byte [20];
  306. Array.Copy (rgbSignature, 0, half, 0, 20);
  307. BigInteger r = new BigInteger (half);
  308. Array.Copy (rgbSignature, 20, half, 0, 20);
  309. BigInteger s = new BigInteger (half);
  310. if ((r < 0) || (q <= r))
  311. return false;
  312. if ((s < 0) || (q <= s))
  313. return false;
  314. BigInteger w = s.modInverse(q);
  315. BigInteger u1 = m * w % q;
  316. BigInteger u2 = r * w % q;
  317. u1 = g.modPow(u1, p);
  318. u2 = y.modPow(u2, p);
  319. BigInteger v = ((u1 * u2 % p) % q);
  320. return (v == r);
  321. }
  322. catch {
  323. throw new CryptographicException ();
  324. }
  325. }
  326. public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature)
  327. {
  328. return VerifyHash (rgbHash, "SHA1", rgbSignature);
  329. }
  330. private byte[] NormalizeArray (byte[] array)
  331. {
  332. int n = (array.Length % 4);
  333. if (n > 0) {
  334. byte[] temp = new byte [array.Length + 4 - n];
  335. Array.Copy (array, 0, temp, (4 - n), array.Length);
  336. return temp;
  337. }
  338. else
  339. return array;
  340. }
  341. public override DSAParameters ExportParameters (bool includePrivateParameters)
  342. {
  343. if ((includePrivateParameters) && (!privateKeyExportable))
  344. throw new CryptographicException ("cannot export private key");
  345. if (!keypairGenerated)
  346. Generate (1024);
  347. DSAParameters param = new DSAParameters();
  348. // all parameters must be in multiple of 4 bytes arrays
  349. // this isn't (generally) a problem for most of the parameters
  350. // except for J (but we won't take a chance)
  351. param.P = NormalizeArray (p.getBytes ());
  352. param.Q = NormalizeArray (q.getBytes ());
  353. param.G = NormalizeArray (g.getBytes ());
  354. param.Y = NormalizeArray (y.getBytes ());
  355. param.J = NormalizeArray (j.getBytes ());
  356. if (seed != 0) {
  357. param.Seed = NormalizeArray (seed.getBytes ());
  358. param.Counter = counter;
  359. }
  360. if (includePrivateParameters)
  361. param.X = NormalizeArray (x.getBytes ());
  362. return param;
  363. }
  364. public override void ImportParameters (DSAParameters parameters)
  365. {
  366. // if missing "mandatory" parameters
  367. if ((parameters.P == null) || (parameters.Q == null) || (parameters.G == null) || (parameters.Y == null))
  368. throw new CryptographicException ();
  369. p = new BigInteger (parameters.P);
  370. q = new BigInteger (parameters.Q);
  371. g = new BigInteger (parameters.G);
  372. y = new BigInteger (parameters.Y);
  373. // optional parameter - private key
  374. if (parameters.X != null)
  375. x = new BigInteger (parameters.X);
  376. else
  377. x = new BigInteger (0);
  378. // optional parameter - pre-computation
  379. if (parameters.J != null)
  380. j = new BigInteger (parameters.J);
  381. else
  382. j = (p - 1) / q;
  383. // optional - seed and counter must both be present (or absent)
  384. if (parameters.Seed != null) {
  385. seed = new BigInteger (parameters.Seed);
  386. counter = parameters.Counter;
  387. }
  388. else
  389. seed = new BigInteger (0);
  390. // we now have a keypair
  391. keypairGenerated = true;
  392. }
  393. }
  394. }