2 // RSAManaged.cs - Implements the RSA algorithm.
5 // Sebastien Pouliot (sebastien@ximian.com)
6 // Ben Maurer (bmaurer@users.sf.net)
8 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Portions (C) 2003 Ben Maurer
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
13 // See bouncycastle.txt for license.
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Security.Cryptography;
41 // Big chunks of code are coming from the original RSACryptoServiceProvider class.
42 // The class was refactored to :
43 // a. ease integration of new hash algorithm (like MD2, RIPEMD160, ...);
44 // b. provide better support for the coming SSL implementation (requires
45 // EncryptValue/DecryptValue) with, or without, Mono runtime/corlib;
46 // c. provide an alternative RSA implementation for all Windows (like using
47 // OAEP without Windows XP).
49 namespace Mono.Security.Cryptography {
56 class RSAManaged : RSA {
58 private const int defaultKeySize = 1024;
60 private bool isCRTpossible = false;
61 private bool keyBlinding = true;
62 private bool keypairGenerated = false;
63 private bool m_disposed = false;
68 private BigInteger dp;
69 private BigInteger dq;
70 private BigInteger qInv;
71 private BigInteger n; // modulus
74 public RSAManaged () : this (defaultKeySize)
78 public RSAManaged (int keySize)
80 LegalKeySizesValue = new KeySizes [1];
81 LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
82 base.KeySize = keySize;
87 // Zeroize private key
91 private void GenerateKeyPair ()
93 // p and q values should have a length of half the strength in bits
94 int pbitlength = ((KeySize + 1) >> 1);
95 int qbitlength = (KeySize - pbitlength);
96 const uint uint_e = 17;
99 // generate p, prime and (p-1) relatively prime to e
101 p = BigInteger.GeneratePseudoPrime (pbitlength);
105 // generate a modulus of the required length
107 // generate q, prime and (q-1) relatively prime to e,
108 // and not equal to p
110 q = BigInteger.GeneratePseudoPrime (qbitlength);
111 if ((q % uint_e != 1) && (p != q))
115 // calculate the modulus
117 if (n.BitCount () == KeySize)
120 // if we get here our primes aren't big enough, make the largest
121 // of the two p and try again
126 BigInteger pSub1 = (p - 1);
127 BigInteger qSub1 = (q - 1);
128 BigInteger phi = pSub1 * qSub1;
130 // calculate the private exponent
131 d = e.ModInverse (phi);
133 // calculate the CRT factors
136 qInv = q.ModInverse (p);
138 keypairGenerated = true;
139 isCRTpossible = true;
141 if (KeyGenerated != null)
142 KeyGenerated (this, null);
145 // overrides from RSA class
147 public override int KeySize {
149 // in case keypair hasn't been (yet) generated
150 if (keypairGenerated) {
151 int ks = n.BitCount ();
153 ks = ks + (8 - (ks & 7));
160 public override string KeyExchangeAlgorithm {
161 get { return "RSA-PKCS1-KeyEx"; }
164 // note: when (if) we generate a keypair then it will have both
165 // the public and private keys
166 public bool PublicOnly {
167 get { return (keypairGenerated && ((d == null) || (n == null))); }
170 public override string SignatureAlgorithm {
171 get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
174 public override byte[] DecryptValue (byte[] rgb)
177 throw new ObjectDisposedException ("private key");
179 // decrypt operation is used for signature
180 if (!keypairGenerated)
183 BigInteger input = new BigInteger (rgb);
186 // we use key blinding (by default) against timing attacks
188 // x = (r^e * g) mod n
189 // *new* random number (so it's timing is also random)
190 r = BigInteger.GenerateRandom (n.BitCount ());
191 input = r.ModPow (e, n) * input % n;
195 // decrypt (which uses the private key) can be
196 // optimized by using CRT (Chinese Remainder Theorem)
199 BigInteger m1 = input.ModPow (dp, p);
201 BigInteger m2 = input.ModPow (dq, q);
205 h = p - ((m2 - m1) * qInv % p);
208 // h = (m1 - m2) * qInv mod p
209 h = (m1 - m2) * qInv % p;
215 output = input.ModPow (d, n);
221 output = output * r.ModInverse (n) % n;
225 byte[] result = output.GetBytes ();
232 public override byte[] EncryptValue (byte[] rgb)
235 throw new ObjectDisposedException ("public key");
237 if (!keypairGenerated)
240 BigInteger input = new BigInteger (rgb);
241 BigInteger output = input.ModPow (e, n);
242 byte[] result = output.GetBytes ();
249 public override RSAParameters ExportParameters (bool includePrivateParameters)
252 throw new ObjectDisposedException ("");
254 if (!keypairGenerated)
257 RSAParameters param = new RSAParameters ();
258 param.Exponent = e.GetBytes ();
259 param.Modulus = n.GetBytes ();
260 if (includePrivateParameters) {
261 // some parameters are required for exporting the private key
263 throw new CryptographicException ("Missing private key");
264 param.D = d.GetBytes ();
265 // hack for bugzilla #57941 where D wasn't provided
266 if (param.D.Length != param.Modulus.Length) {
267 byte[] normalizedD = new byte [param.Modulus.Length];
268 Buffer.BlockCopy (param.D, 0, normalizedD, (normalizedD.Length - param.D.Length), param.D.Length);
269 param.D = normalizedD;
271 // but CRT parameters are optionals
272 if ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null)) {
273 // and we include them only if we have them all
274 param.P = p.GetBytes ();
275 param.Q = q.GetBytes ();
276 param.DP = dp.GetBytes ();
277 param.DQ = dq.GetBytes ();
278 param.InverseQ = qInv.GetBytes ();
284 public override void ImportParameters (RSAParameters parameters)
287 throw new ObjectDisposedException ("");
289 // if missing "mandatory" parameters
290 if (parameters.Exponent == null)
291 throw new CryptographicException ("Missing Exponent");
292 if (parameters.Modulus == null)
293 throw new CryptographicException ("Missing Modulus");
295 e = new BigInteger (parameters.Exponent);
296 n = new BigInteger (parameters.Modulus);
297 // only if the private key is present
298 if (parameters.D != null)
299 d = new BigInteger (parameters.D);
300 if (parameters.DP != null)
301 dp = new BigInteger (parameters.DP);
302 if (parameters.DQ != null)
303 dq = new BigInteger (parameters.DQ);
304 if (parameters.InverseQ != null)
305 qInv = new BigInteger (parameters.InverseQ);
306 if (parameters.P != null)
307 p = new BigInteger (parameters.P);
308 if (parameters.Q != null)
309 q = new BigInteger (parameters.Q);
311 // we now have a keypair
312 keypairGenerated = true;
313 isCRTpossible = ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null));
316 protected override void Dispose (bool disposing)
319 // Always zeroize private key
358 // no need as they all are abstract before us
362 public delegate void KeyGeneratedEventHandler (object sender, EventArgs e);
364 public event KeyGeneratedEventHandler KeyGenerated;
366 public override string ToXmlString (bool includePrivateParameters)
368 StringBuilder sb = new StringBuilder ();
369 RSAParameters rsaParams = ExportParameters (includePrivateParameters);
371 sb.Append ("<RSAKeyValue>");
373 sb.Append ("<Modulus>");
374 sb.Append (Convert.ToBase64String (rsaParams.Modulus));
375 sb.Append ("</Modulus>");
377 sb.Append ("<Exponent>");
378 sb.Append (Convert.ToBase64String (rsaParams.Exponent));
379 sb.Append ("</Exponent>");
381 if (includePrivateParameters) {
382 if (rsaParams.P != null) {
384 sb.Append (Convert.ToBase64String (rsaParams.P));
387 if (rsaParams.Q != null) {
389 sb.Append (Convert.ToBase64String (rsaParams.Q));
392 if (rsaParams.DP != null) {
394 sb.Append (Convert.ToBase64String (rsaParams.DP));
397 if (rsaParams.DQ != null) {
399 sb.Append (Convert.ToBase64String (rsaParams.DQ));
402 if (rsaParams.InverseQ != null) {
403 sb.Append ("<InverseQ>");
404 sb.Append (Convert.ToBase64String (rsaParams.InverseQ));
405 sb.Append ("</InverseQ>");
408 sb.Append (Convert.ToBase64String (rsaParams.D));
412 sb.Append ("</RSAKeyValue>");
415 if (rsaParams.P != null)
416 Array.Clear (rsaParams.P, 0, rsaParams.P.Length);
417 if (rsaParams.Q != null)
418 Array.Clear (rsaParams.Q, 0, rsaParams.Q.Length);
419 if (rsaParams.DP != null)
420 Array.Clear (rsaParams.DP, 0, rsaParams.DP.Length);
421 if (rsaParams.DQ != null)
422 Array.Clear (rsaParams.DQ, 0, rsaParams.DQ.Length);
423 if (rsaParams.InverseQ != null)
424 Array.Clear (rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
425 if (rsaParams.D != null)
426 Array.Clear (rsaParams.D, 0, rsaParams.D.Length);
430 return sb.ToString ();
433 // internal for Mono 1.0.x in order to preserve public contract
434 // they are public for Mono 1.1.x (for 1.2) as the API isn't froze ATM
441 bool UseKeyBlinding {
442 get { return keyBlinding; }
443 // you REALLY shoudn't touch this (true is fine ;-)
444 set { keyBlinding = value; }
453 // either the key pair isn't generated (and will be
454 // generated with CRT parameters) or CRT is (or isn't)
455 // possible (in case the key was imported)
456 get { return (!keypairGenerated || isCRTpossible); }