Merge pull request #645 from knocte/nitpicks
[mono-project.git] / mcs / class / corlib / Mono.Security.Cryptography / RSAManaged.cs
blob9c406a200c73b9c557c78bac455378663640b2a3
1 //
2 // RSAManaged.cs - Implements the RSA algorithm.
3 //
4 // Authors:
5 // Sebastien Pouliot (sebastien@ximian.com)
6 // Ben Maurer (bmaurer@users.sf.net)
7 //
8 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Portions (C) 2003 Ben Maurer
10 // Copyright (C) 2004,2006 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:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
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.
35 using System;
36 using System.Security.Cryptography;
37 using System.Text;
39 using Mono.Math;
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 {
51 #if INSIDE_CORLIB
52 internal
53 #else
54 public
55 #endif
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;
65 private BigInteger d;
66 private BigInteger p;
67 private BigInteger q;
68 private BigInteger dp;
69 private BigInteger dq;
70 private BigInteger qInv;
71 private BigInteger n; // modulus
72 private BigInteger e;
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;
85 ~RSAManaged ()
87 // Zeroize private key
88 Dispose (false);
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;
97 e = uint_e; // fixed
99 // generate p, prime and (p-1) relatively prime to e
100 for (;;) {
101 p = BigInteger.GeneratePseudoPrime (pbitlength);
102 if (p % uint_e != 1)
103 break;
105 // generate a modulus of the required length
106 for (;;) {
107 // generate q, prime and (q-1) relatively prime to e,
108 // and not equal to p
109 for (;;) {
110 q = BigInteger.GeneratePseudoPrime (qbitlength);
111 if ((q % uint_e != 1) && (p != q))
112 break;
115 // calculate the modulus
116 n = p * q;
117 if (n.BitCount () == KeySize)
118 break;
120 // if we get here our primes aren't big enough, make the largest
121 // of the two p and try again
122 if (p < q)
123 p = q;
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
134 dp = d % pSub1;
135 dq = d % qSub1;
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 {
148 get {
149 // in case keypair hasn't been (yet) generated
150 if (keypairGenerated) {
151 int ks = n.BitCount ();
152 if ((ks & 7) != 0)
153 ks = ks + (8 - (ks & 7));
154 return ks;
156 else
157 return base.KeySize;
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)
176 if (m_disposed)
177 throw new ObjectDisposedException ("private key");
179 // decrypt operation is used for signature
180 if (!keypairGenerated)
181 GenerateKeyPair ();
183 BigInteger input = new BigInteger (rgb);
184 BigInteger r = null;
186 // we use key blinding (by default) against timing attacks
187 if (keyBlinding) {
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;
194 BigInteger output;
195 // decrypt (which uses the private key) can be
196 // optimized by using CRT (Chinese Remainder Theorem)
197 if (isCRTpossible) {
198 // m1 = c^dp mod p
199 BigInteger m1 = input.ModPow (dp, p);
200 // m2 = c^dq mod q
201 BigInteger m2 = input.ModPow (dq, q);
202 BigInteger h;
203 if (m2 > m1) {
204 // thanks to benm!
205 h = p - ((m2 - m1) * qInv % p);
206 output = m2 + q * h;
207 } else {
208 // h = (m1 - m2) * qInv mod p
209 h = (m1 - m2) * qInv % p;
210 // m = m2 + q * h;
211 output = m2 + q * h;
213 } else if (!PublicOnly) {
214 // m = c^d mod n
215 output = input.ModPow (d, n);
216 } else {
217 throw new CryptographicException (Locale.GetText ("Missing private key to decrypt value."));
220 if (keyBlinding) {
221 // Complete blinding
222 // x^e / r mod n
223 output = output * r.ModInverse (n) % n;
224 r.Clear ();
227 // it's sometimes possible for the results to be a byte short
228 // and this can break some software (see #79502) so we 0x00 pad the result
229 byte[] result = GetPaddedValue (output, (KeySize >> 3));
230 // zeroize values
231 input.Clear ();
232 output.Clear ();
233 return result;
236 public override byte[] EncryptValue (byte[] rgb)
238 if (m_disposed)
239 throw new ObjectDisposedException ("public key");
241 if (!keypairGenerated)
242 GenerateKeyPair ();
244 BigInteger input = new BigInteger (rgb);
245 BigInteger output = input.ModPow (e, n);
246 // it's sometimes possible for the results to be a byte short
247 // and this can break some software (see #79502) so we 0x00 pad the result
248 byte[] result = GetPaddedValue (output, (KeySize >> 3));
249 // zeroize value
250 input.Clear ();
251 output.Clear ();
252 return result;
257 public override RSAParameters ExportParameters (bool includePrivateParameters)
259 if (m_disposed)
260 throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
262 if (!keypairGenerated)
263 GenerateKeyPair ();
265 RSAParameters param = new RSAParameters ();
266 param.Exponent = e.GetBytes ();
267 param.Modulus = n.GetBytes ();
268 if (includePrivateParameters) {
269 // some parameters are required for exporting the private key
270 if (d == null)
271 throw new CryptographicException ("Missing private key");
272 param.D = d.GetBytes ();
273 // hack for bugzilla #57941 where D wasn't provided
274 if (param.D.Length != param.Modulus.Length) {
275 byte[] normalizedD = new byte [param.Modulus.Length];
276 Buffer.BlockCopy (param.D, 0, normalizedD, (normalizedD.Length - param.D.Length), param.D.Length);
277 param.D = normalizedD;
279 // but CRT parameters are optionals
280 if ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null)) {
281 // and we include them only if we have them all
282 int length = (KeySize >> 4);
283 param.P = GetPaddedValue (p, length);
284 param.Q = GetPaddedValue (q, length);
285 param.DP = GetPaddedValue (dp, length);
286 param.DQ = GetPaddedValue (dq, length);
287 param.InverseQ = GetPaddedValue (qInv, length);
290 return param;
293 public override void ImportParameters (RSAParameters parameters)
295 if (m_disposed)
296 throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
298 // if missing "mandatory" parameters
299 if (parameters.Exponent == null)
300 throw new CryptographicException (Locale.GetText ("Missing Exponent"));
301 if (parameters.Modulus == null)
302 throw new CryptographicException (Locale.GetText ("Missing Modulus"));
304 e = new BigInteger (parameters.Exponent);
305 n = new BigInteger (parameters.Modulus);
306 // only if the private key is present
307 if (parameters.D != null)
308 d = new BigInteger (parameters.D);
309 if (parameters.DP != null)
310 dp = new BigInteger (parameters.DP);
311 if (parameters.DQ != null)
312 dq = new BigInteger (parameters.DQ);
313 if (parameters.InverseQ != null)
314 qInv = new BigInteger (parameters.InverseQ);
315 if (parameters.P != null)
316 p = new BigInteger (parameters.P);
317 if (parameters.Q != null)
318 q = new BigInteger (parameters.Q);
320 // we now have a keypair
321 keypairGenerated = true;
322 bool privateKey = ((p != null) && (q != null) && (dp != null));
323 isCRTpossible = (privateKey && (dq != null) && (qInv != null));
325 // check if the public/private keys match
326 // the way the check is made allows a bad D to work if CRT is available (like MS does, see unit tests)
327 if (!privateKey)
328 return;
330 // always check n == p * q
331 bool ok = (n == (p * q));
332 if (ok) {
333 // we now know that p and q are correct, so (p - 1), (q - 1) and phi will be ok too
334 BigInteger pSub1 = (p - 1);
335 BigInteger qSub1 = (q - 1);
336 BigInteger phi = pSub1 * qSub1;
337 // e is fairly static but anyway we can ensure it makes sense by recomputing d
338 BigInteger dcheck = e.ModInverse (phi);
340 // now if our new d(check) is different than the d we're provided then we cannot
341 // be sure if 'd' or 'e' is invalid... (note that, from experience, 'd' is more
342 // likely to be invalid since it's twice as large as DP (or DQ) and sits at the
343 // end of the structure (e.g. truncation).
344 ok = (d == dcheck);
346 // ... unless we have the pre-computed CRT parameters
347 if (!ok && isCRTpossible) {
348 // we can override the previous decision since Mono always prefer, for
349 // performance reasons, using the CRT algorithm
350 ok = (dp == (dcheck % pSub1)) && (dq == (dcheck % qSub1)) &&
351 (qInv == q.ModInverse (p));
355 if (!ok)
356 throw new CryptographicException (Locale.GetText ("Private/public key mismatch"));
359 protected override void Dispose (bool disposing)
361 if (!m_disposed) {
362 // Always zeroize private key
363 if (d != null) {
364 d.Clear ();
365 d = null;
367 if (p != null) {
368 p.Clear ();
369 p = null;
371 if (q != null) {
372 q.Clear ();
373 q = null;
375 if (dp != null) {
376 dp.Clear ();
377 dp = null;
379 if (dq != null) {
380 dq.Clear ();
381 dq = null;
383 if (qInv != null) {
384 qInv.Clear ();
385 qInv = null;
388 if (disposing) {
389 // clear public key
390 if (e != null) {
391 e.Clear ();
392 e = null;
394 if (n != null) {
395 n.Clear ();
396 n = null;
400 // call base class
401 // no need as they all are abstract before us
402 m_disposed = true;
405 public delegate void KeyGeneratedEventHandler (object sender, EventArgs e);
407 public event KeyGeneratedEventHandler KeyGenerated;
409 public override string ToXmlString (bool includePrivateParameters)
411 StringBuilder sb = new StringBuilder ();
412 RSAParameters rsaParams = ExportParameters (includePrivateParameters);
413 try {
414 sb.Append ("<RSAKeyValue>");
416 sb.Append ("<Modulus>");
417 sb.Append (Convert.ToBase64String (rsaParams.Modulus));
418 sb.Append ("</Modulus>");
420 sb.Append ("<Exponent>");
421 sb.Append (Convert.ToBase64String (rsaParams.Exponent));
422 sb.Append ("</Exponent>");
424 if (includePrivateParameters) {
425 if (rsaParams.P != null) {
426 sb.Append ("<P>");
427 sb.Append (Convert.ToBase64String (rsaParams.P));
428 sb.Append ("</P>");
430 if (rsaParams.Q != null) {
431 sb.Append ("<Q>");
432 sb.Append (Convert.ToBase64String (rsaParams.Q));
433 sb.Append ("</Q>");
435 if (rsaParams.DP != null) {
436 sb.Append ("<DP>");
437 sb.Append (Convert.ToBase64String (rsaParams.DP));
438 sb.Append ("</DP>");
440 if (rsaParams.DQ != null) {
441 sb.Append ("<DQ>");
442 sb.Append (Convert.ToBase64String (rsaParams.DQ));
443 sb.Append ("</DQ>");
445 if (rsaParams.InverseQ != null) {
446 sb.Append ("<InverseQ>");
447 sb.Append (Convert.ToBase64String (rsaParams.InverseQ));
448 sb.Append ("</InverseQ>");
450 sb.Append ("<D>");
451 sb.Append (Convert.ToBase64String (rsaParams.D));
452 sb.Append ("</D>");
455 sb.Append ("</RSAKeyValue>");
457 catch {
458 if (rsaParams.P != null)
459 Array.Clear (rsaParams.P, 0, rsaParams.P.Length);
460 if (rsaParams.Q != null)
461 Array.Clear (rsaParams.Q, 0, rsaParams.Q.Length);
462 if (rsaParams.DP != null)
463 Array.Clear (rsaParams.DP, 0, rsaParams.DP.Length);
464 if (rsaParams.DQ != null)
465 Array.Clear (rsaParams.DQ, 0, rsaParams.DQ.Length);
466 if (rsaParams.InverseQ != null)
467 Array.Clear (rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
468 if (rsaParams.D != null)
469 Array.Clear (rsaParams.D, 0, rsaParams.D.Length);
470 throw;
473 return sb.ToString ();
476 // internal for Mono 1.0.x in order to preserve public contract
477 // they are public for Mono 1.1.x (for 1.2) as the API isn't froze ATM
479 public bool UseKeyBlinding {
480 get { return keyBlinding; }
481 // you REALLY shoudn't touch this (true is fine ;-)
482 set { keyBlinding = value; }
485 public bool IsCrtPossible {
486 // either the key pair isn't generated (and will be
487 // generated with CRT parameters) or CRT is (or isn't)
488 // possible (in case the key was imported)
489 get { return (!keypairGenerated || isCRTpossible); }
492 private byte[] GetPaddedValue (BigInteger value, int length)
494 byte[] result = value.GetBytes ();
495 if (result.Length >= length)
496 return result;
498 // left-pad 0x00 value on the result (same integer, correct length)
499 byte[] padded = new byte[length];
500 Buffer.BlockCopy (result, 0, padded, (length - result.Length), result.Length);
501 // temporary result may contain decrypted (plaintext) data, clear it
502 Array.Clear (result, 0, result.Length);
503 return padded;