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,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:
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
;
213 } else if (!PublicOnly
) {
215 output
= input
.ModPow (d
, n
);
217 throw new CryptographicException (Locale
.GetText ("Missing private key to decrypt value."));
223 output
= output
* r
.ModInverse (n
) % n
;
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));
236 public override byte[] EncryptValue (byte[] rgb
)
239 throw new ObjectDisposedException ("public key");
241 if (!keypairGenerated
)
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));
257 public override RSAParameters
ExportParameters (bool includePrivateParameters
)
260 throw new ObjectDisposedException (Locale
.GetText ("Keypair was disposed"));
262 if (!keypairGenerated
)
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
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
);
293 public override void ImportParameters (RSAParameters parameters
)
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)
330 // always check n == p * q
331 bool ok
= (n
== (p
* q
));
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).
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
));
356 throw new CryptographicException (Locale
.GetText ("Private/public key mismatch"));
359 protected override void Dispose (bool disposing
)
362 // Always zeroize private key
401 // no need as they all are abstract before us
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
);
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) {
427 sb
.Append (Convert
.ToBase64String (rsaParams
.P
));
430 if (rsaParams
.Q
!= null) {
432 sb
.Append (Convert
.ToBase64String (rsaParams
.Q
));
435 if (rsaParams
.DP
!= null) {
437 sb
.Append (Convert
.ToBase64String (rsaParams
.DP
));
440 if (rsaParams
.DQ
!= null) {
442 sb
.Append (Convert
.ToBase64String (rsaParams
.DQ
));
445 if (rsaParams
.InverseQ
!= null) {
446 sb
.Append ("<InverseQ>");
447 sb
.Append (Convert
.ToBase64String (rsaParams
.InverseQ
));
448 sb
.Append ("</InverseQ>");
451 sb
.Append (Convert
.ToBase64String (rsaParams
.D
));
455 sb
.Append ("</RSAKeyValue>");
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
);
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
)
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
);