RSACryptoServiceProvider.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. //
  2. // RSACryptoServiceProvider.cs: Handles an RSA implementation.
  3. //
  4. // Author:
  5. // Sebastien Pouliot ([email protected])
  6. //
  7. // (C) 2002 Motus Technologies Inc. (http://www.motus.com)
  8. // Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
  9. // See bouncycastle.txt for license.
  10. //
  11. using System;
  12. using System.IO;
  13. namespace System.Security.Cryptography {
  14. public sealed class RSACryptoServiceProvider : RSA {
  15. private CspParameters cspParams;
  16. private bool privateKeyExportable = true;
  17. private bool keypairGenerated = false;
  18. private bool persistKey = false;
  19. private bool m_disposed = false;
  20. private BigInteger d;
  21. private BigInteger p;
  22. private BigInteger q;
  23. private BigInteger dp;
  24. private BigInteger dq;
  25. private BigInteger qInv;
  26. private BigInteger n; // modulus
  27. private BigInteger e;
  28. public RSACryptoServiceProvider ()
  29. {
  30. // Here it's not clear if we need to generate a keypair
  31. // (note: MS implementation generates a keypair in this case).
  32. // However we:
  33. // (a) often use this constructor to import an existing keypair.
  34. // (b) take a LOT of time to generate the RSA keypair
  35. // So we'll generate the keypair only when (and if) it's being
  36. // used (or exported). This should save us a lot of time (at
  37. // least in the unit tests).
  38. Common (null);
  39. }
  40. public RSACryptoServiceProvider (CspParameters parameters)
  41. {
  42. Common (parameters);
  43. // no keypair generation done at this stage
  44. }
  45. public RSACryptoServiceProvider (int dwKeySize)
  46. {
  47. // Here it's clear that we need to generate a new keypair
  48. Common (null);
  49. GenerateKeyPair (dwKeySize);
  50. }
  51. // FIXME: We currently dont link with MS CAPI. Anyway this makes
  52. // only sense in Windows - what do we do elsewhere ?
  53. public RSACryptoServiceProvider (int dwKeySize, CspParameters parameters)
  54. {
  55. Common (parameters);
  56. GenerateKeyPair (dwKeySize);
  57. }
  58. [MonoTODO("Persistance")]
  59. // FIXME: We currently dont link with MS CAPI. Anyway this makes
  60. // only sense in Windows - what do we do elsewhere ?
  61. private void Common (CspParameters p)
  62. {
  63. cspParams = new CspParameters ();
  64. if (p == null) {
  65. // TODO: set default values (for keypair persistance)
  66. }
  67. else {
  68. cspParams = p;
  69. // FIXME: We'll need this to support some kind of persistance
  70. throw new NotSupportedException ("CspParameters not supported");
  71. }
  72. // Microsoft RSA CSP can do between 384 and 16384 bits keypair
  73. // we limit ourselve to 2048 because (a) BigInteger limits and (b) it's so SLOW
  74. LegalKeySizesValue = new KeySizes [1];
  75. LegalKeySizesValue [0] = new KeySizes (384, 2048, 8);
  76. }
  77. private void GenerateKeyPair (int dwKeySize)
  78. {
  79. // will throw an exception is key size isn't supported
  80. base.KeySize = dwKeySize;
  81. // p and q values should have a length of half the strength in bits
  82. int pbitlength = ((dwKeySize + 1) >> 1);
  83. int qbitlength = (dwKeySize - pbitlength);
  84. e = new BigInteger (17); // fixed
  85. // generate p, prime and (p-1) relatively prime to e
  86. for (;;) {
  87. p = BigInteger.genPseudoPrime (pbitlength, 80);
  88. if (e.gcd (p - 1) == 1)
  89. break;
  90. }
  91. // generate a modulus of the required length
  92. for (;;) {
  93. // generate q, prime and (q-1) relatively prime to e,
  94. // and not equal to p
  95. for (;;) {
  96. q = BigInteger.genPseudoPrime (qbitlength, 80);
  97. if ((e.gcd (q - 1) == 1) && (p != q))
  98. break;
  99. }
  100. // calculate the modulus
  101. n = p * q;
  102. if (n.bitCount () == dwKeySize)
  103. break;
  104. // if we get here our primes aren't big enough, make the largest
  105. // of the two p and try again
  106. p = p.max (q);
  107. }
  108. BigInteger pSub1 = (p - 1);
  109. BigInteger qSub1 = (q - 1);
  110. BigInteger phi = pSub1 * qSub1;
  111. // calculate the private exponent
  112. d = e.modInverse (phi);
  113. // calculate the CRT factors
  114. dp = d % pSub1;
  115. dq = d % qSub1;
  116. qInv = q.modInverse (p);
  117. keypairGenerated = true;
  118. }
  119. // Zeroize private key
  120. ~RSACryptoServiceProvider()
  121. {
  122. Dispose (false);
  123. }
  124. public override string KeyExchangeAlgorithm {
  125. get { return "RSA-PKCS1-KeyEx"; }
  126. }
  127. public override int KeySize {
  128. get { return n.bitCount(); }
  129. }
  130. [MonoTODO("Persistance")]
  131. public bool PersistKeyInCsp {
  132. get { return false; }
  133. set { throw new NotSupportedException (); }
  134. }
  135. public override string SignatureAlgorithm {
  136. get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
  137. }
  138. public byte[] Decrypt (byte[] rgb, bool fOAEP)
  139. {
  140. // choose between OAEP or PKCS#1 v.1.5 padding
  141. if (fOAEP) {
  142. SHA1 sha1 = SHA1.Create ();
  143. return PKCS1.Decrypt_OAEP (this, sha1, null);
  144. }
  145. else {
  146. return PKCS1.Decrypt_v15 (this, rgb);
  147. }
  148. }
  149. // NOTE: Unlike MS we need this method
  150. // LAMESPEC: Not available from MS .NET framework but MS don't tell
  151. // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
  152. // only encrypt/decrypt session (secret) key using asymmetric keys.
  153. // Using this method to decrypt data IS dangerous (and very slow).
  154. public override byte[] DecryptValue (byte[] rgb)
  155. {
  156. // it would be stupid to decrypt data with a newly
  157. // generated keypair - so we return false
  158. if (!keypairGenerated)
  159. return null;
  160. BigInteger input = new BigInteger (rgb);
  161. BigInteger output = input.modPow (d, n);
  162. return output.getBytes ();
  163. }
  164. public byte[] Encrypt (byte[] rgb, bool fOAEP)
  165. {
  166. RandomNumberGenerator rng = RandomNumberGenerator.Create ();
  167. // choose between OAEP or PKCS#1 v.1.5 padding
  168. if (fOAEP) {
  169. SHA1 sha1 = SHA1.Create ();
  170. return PKCS1.Encrypt_OAEP (this, sha1, rng, rgb);
  171. }
  172. else {
  173. return PKCS1.Encrypt_v15 (this, rng, rgb) ;
  174. }
  175. }
  176. // NOTE: Unlike MS we need this method
  177. // LAMESPEC: Not available from MS .NET framework but MS don't tell
  178. // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
  179. // only encrypt/decrypt session (secret) key using asymmetric keys.
  180. // Using this method to encrypt data IS dangerous (and very slow).
  181. public override byte[] EncryptValue (byte[] rgb)
  182. {
  183. if (!keypairGenerated)
  184. GenerateKeyPair (1024);
  185. // TODO: With CRT
  186. // without CRT
  187. BigInteger input = new BigInteger (rgb);
  188. BigInteger output = input.modPow (e, n);
  189. return output.getBytes ();
  190. }
  191. public override RSAParameters ExportParameters (bool includePrivateParameters)
  192. {
  193. if ((includePrivateParameters) && (!privateKeyExportable))
  194. throw new CryptographicException ("cannot export private key");
  195. if (!keypairGenerated)
  196. GenerateKeyPair (1024);
  197. RSAParameters param = new RSAParameters();
  198. param.Exponent = e.getBytes ();
  199. param.Modulus = n.getBytes ();
  200. if (includePrivateParameters) {
  201. param.D = d.getBytes ();
  202. param.DP = dp.getBytes ();
  203. param.DQ = dq.getBytes ();
  204. param.InverseQ = qInv.getBytes ();
  205. param.P = p.getBytes ();
  206. param.Q = q.getBytes ();
  207. }
  208. return param;
  209. }
  210. public override void ImportParameters (RSAParameters parameters)
  211. {
  212. // if missing "mandatory" parameters
  213. if ((parameters.Exponent == null) || (parameters.Modulus == null))
  214. throw new CryptographicException ();
  215. e = new BigInteger (parameters.Exponent);
  216. n = new BigInteger (parameters.Modulus);
  217. // only if the private key is present
  218. if (parameters.D != null)
  219. d = new BigInteger (parameters.D);
  220. if (parameters.DP != null)
  221. dp = new BigInteger (parameters.DP);
  222. if (parameters.DQ != null)
  223. dq = new BigInteger (parameters.DQ);
  224. if (parameters.InverseQ != null)
  225. qInv = new BigInteger (parameters.InverseQ);
  226. if (parameters.P != null)
  227. p = new BigInteger (parameters.P);
  228. if (parameters.Q != null)
  229. q = new BigInteger (parameters.Q);
  230. // we now have a keypair
  231. keypairGenerated = true;
  232. }
  233. private HashAlgorithm GetHash (object halg)
  234. {
  235. if (halg == null)
  236. throw new ArgumentNullException ();
  237. HashAlgorithm hash = null;
  238. if (halg is String)
  239. hash = HashAlgorithm.Create ((String)halg);
  240. else if (halg is HashAlgorithm)
  241. hash = (HashAlgorithm) halg;
  242. else if (halg is Type)
  243. hash = (HashAlgorithm) Activator.CreateInstance ((Type)halg);
  244. else
  245. throw new ArgumentException ();
  246. return hash;
  247. }
  248. public byte[] SignData (byte[] buffer, object halg)
  249. {
  250. return SignData (buffer, 0, buffer.Length, halg);
  251. }
  252. public byte[] SignData (Stream inputStream, object halg)
  253. {
  254. HashAlgorithm hash = GetHash (halg);
  255. byte[] toBeSigned = hash.ComputeHash (inputStream);
  256. string oid = CryptoConfig.MapNameToOID (hash.ToString ());
  257. return SignHash (toBeSigned, oid);
  258. }
  259. public byte[] SignData (byte[] buffer, int offset, int count, object halg)
  260. {
  261. HashAlgorithm hash = GetHash (halg);
  262. byte[] toBeSigned = hash.ComputeHash (buffer, offset, count);
  263. string oid = CryptoConfig.MapNameToOID (hash.ToString ());
  264. return SignHash (toBeSigned, oid);
  265. }
  266. private void ValidateHash (string oid, int length)
  267. {
  268. if (oid == "1.3.14.3.2.26") {
  269. if (length != 20)
  270. throw new CryptographicException ("wrong hash size for SHA1");
  271. }
  272. else if (oid == "1.2.840.113549.2.5") {
  273. if (length != 16)
  274. throw new CryptographicException ("wrong hash size for MD5");
  275. }
  276. else
  277. throw new NotSupportedException (oid + " is an unsupported hash algorithm for RSA signing");
  278. }
  279. public byte[] SignHash (byte[] rgbHash, string str)
  280. {
  281. if (rgbHash == null)
  282. throw new ArgumentNullException ();
  283. if (!keypairGenerated)
  284. GenerateKeyPair (1024);
  285. ValidateHash (str, rgbHash.Length);
  286. return PKCS1.Sign_v15 (this, str, rgbHash);
  287. }
  288. public bool VerifyData (byte[] buffer, object halg, byte[] signature)
  289. {
  290. HashAlgorithm hash = GetHash (halg);
  291. byte[] toBeVerified = hash.ComputeHash (buffer);
  292. string oid = CryptoConfig.MapNameToOID (hash.ToString ());
  293. return VerifyHash (toBeVerified, oid, signature);
  294. }
  295. public bool VerifyHash (byte[] rgbHash, string str, byte[] rgbSignature)
  296. {
  297. if ((rgbHash == null) || (rgbSignature == null))
  298. throw new ArgumentNullException ();
  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. ValidateHash (str, rgbHash.Length);
  304. return PKCS1.Verify_v15 (this, str, rgbHash, rgbSignature);
  305. }
  306. [MonoTODO()]
  307. protected override void Dispose (bool disposing)
  308. {
  309. if (!m_disposed) {
  310. // TODO: always zeroize private key
  311. if(disposing) {
  312. // TODO: Dispose managed resources
  313. }
  314. // TODO: Dispose unmanaged resources
  315. }
  316. // call base class
  317. // no need as they all are abstract before us
  318. m_disposed = true;
  319. }
  320. }
  321. }