[System.IO.Compression.FileSystem] Fixed date time when creating Zip entries from...
[mono.git] / mcs / class / Mono.Security / Mono.Security.Cryptography / RSAManaged.cs
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)
11 //
12 // Key generation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
13 // See bouncycastle.txt for license.
14 //
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.
33 //
34
35 using System;
36 using System.Security.Cryptography;
37 using System.Text;
38
39 using Mono.Math;
40
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).
48
49 namespace Mono.Security.Cryptography {
50
51 #if INSIDE_CORLIB
52         internal
53 #else
54         public
55 #endif
56         class RSAManaged : RSA {
57
58                 private const int defaultKeySize = 1024;
59
60                 private bool isCRTpossible = false;
61                 private bool keyBlinding = true;
62                 private bool keypairGenerated = false;
63                 private bool m_disposed = false;
64
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;
73
74                 public RSAManaged () : this (defaultKeySize)
75                 {
76                 }
77
78                 public RSAManaged (int keySize) 
79                 {
80                         LegalKeySizesValue = new KeySizes [1];
81                         LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
82                         base.KeySize = keySize;
83                 }
84
85                 ~RSAManaged () 
86                 {
87                         // Zeroize private key
88                         Dispose (false);
89                 }
90
91                 private void GenerateKeyPair () 
92                 {
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
98         
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;
104                         }
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;
113                                 }
114         
115                                 // calculate the modulus
116                                 n = p * q;
117                                 if (n.BitCount () == KeySize)
118                                         break;
119         
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;
124                         }
125         
126                         BigInteger pSub1 = (p - 1);
127                         BigInteger qSub1 = (q - 1);
128                         BigInteger phi = pSub1 * qSub1;
129         
130                         // calculate the private exponent
131                         d = e.ModInverse (phi);
132         
133                         // calculate the CRT factors
134                         dp = d % pSub1;
135                         dq = d % qSub1;
136                         qInv = q.ModInverse (p);
137         
138                         keypairGenerated = true;
139                         isCRTpossible = true;
140
141                         if (KeyGenerated != null)
142                                 KeyGenerated (this, null);
143                 }
144                 
145                 // overrides from RSA class
146
147                 public override int KeySize {
148                         get {
149                                 if (m_disposed)
150                                         throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
151                                 
152                                 // in case keypair hasn't been (yet) generated
153                                 if (keypairGenerated) {
154                                         int ks = n.BitCount ();
155                                         if ((ks & 7) != 0)
156                                                 ks = ks + (8 - (ks & 7));
157                                         return ks;
158                                 }
159                                 else
160                                         return base.KeySize;
161                         }
162                 }
163                 public override string KeyExchangeAlgorithm {
164                         get { return "RSA-PKCS1-KeyEx"; }
165                 }
166
167                 // note: when (if) we generate a keypair then it will have both
168                 // the public and private keys
169                 public bool PublicOnly {
170                         get { return (keypairGenerated && ((d == null) || (n == null))); }
171                 }
172
173                 public override string SignatureAlgorithm {
174                         get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
175                 }
176
177                 public override byte[] DecryptValue (byte[] rgb) 
178                 {
179                         if (m_disposed)
180                                 throw new ObjectDisposedException ("private key");
181
182                         // decrypt operation is used for signature
183                         if (!keypairGenerated)
184                                 GenerateKeyPair ();
185
186                         BigInteger input = new BigInteger (rgb);
187                         BigInteger r = null;
188
189                         // we use key blinding (by default) against timing attacks
190                         if (keyBlinding) {
191                                 // x = (r^e * g) mod n 
192                                 // *new* random number (so it's timing is also random)
193                                 r = BigInteger.GenerateRandom (n.BitCount ());
194                                 input = r.ModPow (e, n) * input % n;
195                         }
196
197                         BigInteger output;
198                         // decrypt (which uses the private key) can be 
199                         // optimized by using CRT (Chinese Remainder Theorem)
200                         if (isCRTpossible) {
201                                 // m1 = c^dp mod p
202                                 BigInteger m1 = input.ModPow (dp, p);
203                                 // m2 = c^dq mod q
204                                 BigInteger m2 = input.ModPow (dq, q);
205                                 BigInteger h;
206                                 if (m2 > m1) {
207                                         // thanks to benm!
208                                         h = p - ((m2 - m1) * qInv % p);
209                                         output = m2 + q * h;
210                                 } else {
211                                         // h = (m1 - m2) * qInv mod p
212                                         h = (m1 - m2) * qInv % p;
213                                         // m = m2 + q * h;
214                                         output = m2 + q * h;
215                                 }
216                         } else if (!PublicOnly) {
217                                 // m = c^d mod n
218                                 output = input.ModPow (d, n);
219                         } else {
220                                 throw new CryptographicException (Locale.GetText ("Missing private key to decrypt value."));
221                         }
222
223                         if (keyBlinding) {
224                                 // Complete blinding
225                                 // x^e / r mod n
226                                 output = output * r.ModInverse (n) % n;
227                                 r.Clear ();
228                         }
229
230                         // it's sometimes possible for the results to be a byte short
231                         // and this can break some software (see #79502) so we 0x00 pad the result
232                         byte[] result = GetPaddedValue (output, (KeySize >> 3));
233                         // zeroize values
234                         input.Clear (); 
235                         output.Clear ();
236                         return result;
237                 }
238
239                 public override byte[] EncryptValue (byte[] rgb) 
240                 {
241                         if (m_disposed)
242                                 throw new ObjectDisposedException ("public key");
243
244                         if (!keypairGenerated)
245                                 GenerateKeyPair ();
246
247                         BigInteger input = new BigInteger (rgb);
248                         BigInteger output = input.ModPow (e, n);
249                         // it's sometimes possible for the results to be a byte short
250                         // and this can break some software (see #79502) so we 0x00 pad the result
251                         byte[] result = GetPaddedValue (output, (KeySize >> 3));
252                         // zeroize value
253                         input.Clear (); 
254                         output.Clear ();
255                         return result;
256                 }
257
258
259
260                 public override RSAParameters ExportParameters (bool includePrivateParameters) 
261                 {
262                         if (m_disposed)
263                                 throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
264
265                         if (!keypairGenerated)
266                                 GenerateKeyPair ();
267         
268                         RSAParameters param = new RSAParameters ();
269                         param.Exponent = e.GetBytes ();
270                         param.Modulus = n.GetBytes ();
271                         if (includePrivateParameters) {
272                                 // some parameters are required for exporting the private key
273                                 if (d == null)
274                                         throw new CryptographicException ("Missing private key");
275                                 param.D = d.GetBytes ();
276                                 // hack for bugzilla #57941 where D wasn't provided
277                                 if (param.D.Length != param.Modulus.Length) {
278                                         byte[] normalizedD = new byte [param.Modulus.Length];
279                                         Buffer.BlockCopy (param.D, 0, normalizedD, (normalizedD.Length - param.D.Length), param.D.Length);
280                                         param.D = normalizedD;
281                                 }
282                                 // but CRT parameters are optionals
283                                 if ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null)) {
284                                         // and we include them only if we have them all
285                                         int length = (KeySize >> 4);
286                                         param.P = GetPaddedValue (p, length);
287                                         param.Q = GetPaddedValue (q, length);
288                                         param.DP = GetPaddedValue (dp, length);
289                                         param.DQ = GetPaddedValue (dq, length);
290                                         param.InverseQ = GetPaddedValue (qInv, length);
291                                 }
292                         }
293                         return param;
294                 }
295
296                 public override void ImportParameters (RSAParameters parameters) 
297                 {
298                         if (m_disposed)
299                                 throw new ObjectDisposedException (Locale.GetText ("Keypair was disposed"));
300
301                         // if missing "mandatory" parameters
302                         if (parameters.Exponent == null) 
303                                 throw new CryptographicException (Locale.GetText ("Missing Exponent"));
304                         if (parameters.Modulus == null)
305                                 throw new CryptographicException (Locale.GetText ("Missing Modulus"));
306         
307                         e = new BigInteger (parameters.Exponent);
308                         n = new BigInteger (parameters.Modulus);
309                         
310                         //reset all private key values to null
311                         d = dp = dq = qInv = p = q = null;
312                         
313                         // only if the private key is present
314                         if (parameters.D != null)
315                                 d = new BigInteger (parameters.D);
316                         if (parameters.DP != null)
317                                 dp = new BigInteger (parameters.DP);
318                         if (parameters.DQ != null)
319                                 dq = new BigInteger (parameters.DQ);
320                         if (parameters.InverseQ != null)
321                                 qInv = new BigInteger (parameters.InverseQ);
322                         if (parameters.P != null)
323                                 p = new BigInteger (parameters.P);
324                         if (parameters.Q != null)
325                                 q = new BigInteger (parameters.Q);
326
327                         // we now have a keypair
328                         keypairGenerated = true;
329                         bool privateKey = ((p != null) && (q != null) && (dp != null));
330                         isCRTpossible = (privateKey && (dq != null) && (qInv != null));
331
332                         // check if the public/private keys match
333                         // the way the check is made allows a bad D to work if CRT is available (like MS does, see unit tests)
334                         if (!privateKey)
335                                 return;
336
337                         // always check n == p * q
338                         bool ok = (n == (p * q));
339                         if (ok) {
340                                 // we now know that p and q are correct, so (p - 1), (q - 1) and phi will be ok too
341                                 BigInteger pSub1 = (p - 1);
342                                 BigInteger qSub1 = (q - 1);
343                                 BigInteger phi = pSub1 * qSub1;
344                                 // e is fairly static but anyway we can ensure it makes sense by recomputing d
345                                 BigInteger dcheck = e.ModInverse (phi);
346
347                                 // now if our new d(check) is different than the d we're provided then we cannot
348                                 // be sure if 'd' or 'e' is invalid... (note that, from experience, 'd' is more 
349                                 // likely to be invalid since it's twice as large as DP (or DQ) and sits at the
350                                 // end of the structure (e.g. truncation).
351                                 ok = (d == dcheck);
352
353                                 // ... unless we have the pre-computed CRT parameters
354                                 if (!ok && isCRTpossible) {
355                                         // we can override the previous decision since Mono always prefer, for 
356                                         // performance reasons, using the CRT algorithm
357                                         ok = (dp == (dcheck % pSub1)) && (dq == (dcheck % qSub1)) && 
358                                                 (qInv == q.ModInverse (p));
359                                 }
360                         }
361
362                         if (!ok)
363                                 throw new CryptographicException (Locale.GetText ("Private/public key mismatch"));
364                 }
365
366                 protected override void Dispose (bool disposing) 
367                 {
368                         if (!m_disposed) {
369                                 // Always zeroize private key
370                                 if (d != null) {
371                                         d.Clear (); 
372                                         d = null;
373                                 }
374                                 if (p != null) {
375                                         p.Clear (); 
376                                         p = null;
377                                 }
378                                 if (q != null) {
379                                         q.Clear (); 
380                                         q = null;
381                                 }
382                                 if (dp != null) {
383                                         dp.Clear (); 
384                                         dp = null;
385                                 }
386                                 if (dq != null) {
387                                         dq.Clear (); 
388                                         dq = null;
389                                 }
390                                 if (qInv != null) {
391                                         qInv.Clear (); 
392                                         qInv = null;
393                                 }
394
395                                 if (disposing) {
396                                         // clear public key
397                                         if (e != null) {
398                                                 e.Clear (); 
399                                                 e = null;
400                                         }
401                                         if (n != null) {
402                                                 n.Clear (); 
403                                                 n = null;
404                                         }
405                                 }
406                         }
407                         // call base class 
408                         // no need as they all are abstract before us
409                         m_disposed = true;
410                 }
411
412                 public delegate void KeyGeneratedEventHandler (object sender, EventArgs e);
413
414                 public event KeyGeneratedEventHandler KeyGenerated;
415
416                 public override string ToXmlString (bool includePrivateParameters) 
417                 {
418                         StringBuilder sb = new StringBuilder ();
419                         RSAParameters rsaParams = ExportParameters (includePrivateParameters);
420                         try {
421                                 sb.Append ("<RSAKeyValue>");
422                                 
423                                 sb.Append ("<Modulus>");
424                                 sb.Append (Convert.ToBase64String (rsaParams.Modulus));
425                                 sb.Append ("</Modulus>");
426
427                                 sb.Append ("<Exponent>");
428                                 sb.Append (Convert.ToBase64String (rsaParams.Exponent));
429                                 sb.Append ("</Exponent>");
430
431                                 if (includePrivateParameters) {
432                                         if (rsaParams.P != null) {
433                                                 sb.Append ("<P>");
434                                                 sb.Append (Convert.ToBase64String (rsaParams.P));
435                                                 sb.Append ("</P>");
436                                         }
437                                         if (rsaParams.Q != null) {
438                                                 sb.Append ("<Q>");
439                                                 sb.Append (Convert.ToBase64String (rsaParams.Q));
440                                                 sb.Append ("</Q>");
441                                         }
442                                         if (rsaParams.DP != null) {
443                                                 sb.Append ("<DP>");
444                                                 sb.Append (Convert.ToBase64String (rsaParams.DP));
445                                                 sb.Append ("</DP>");
446                                         }
447                                         if (rsaParams.DQ != null) {
448                                                 sb.Append ("<DQ>");
449                                                 sb.Append (Convert.ToBase64String (rsaParams.DQ));
450                                                 sb.Append ("</DQ>");
451                                         }
452                                         if (rsaParams.InverseQ != null) {
453                                                 sb.Append ("<InverseQ>");
454                                                 sb.Append (Convert.ToBase64String (rsaParams.InverseQ));
455                                                 sb.Append ("</InverseQ>");
456                                         }
457                                         sb.Append ("<D>");
458                                         sb.Append (Convert.ToBase64String (rsaParams.D));
459                                         sb.Append ("</D>");
460                                 }
461                                 
462                                 sb.Append ("</RSAKeyValue>");
463                         }
464                         catch {
465                                 if (rsaParams.P != null)
466                                         Array.Clear (rsaParams.P, 0, rsaParams.P.Length);
467                                 if (rsaParams.Q != null)
468                                         Array.Clear (rsaParams.Q, 0, rsaParams.Q.Length);
469                                 if (rsaParams.DP != null)
470                                         Array.Clear (rsaParams.DP, 0, rsaParams.DP.Length);
471                                 if (rsaParams.DQ != null)
472                                         Array.Clear (rsaParams.DQ, 0, rsaParams.DQ.Length);
473                                 if (rsaParams.InverseQ != null)
474                                         Array.Clear (rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
475                                 if (rsaParams.D != null)
476                                         Array.Clear (rsaParams.D, 0, rsaParams.D.Length);
477                                 throw;
478                         }
479                         
480                         return sb.ToString ();
481                 }
482
483                 // internal for Mono 1.0.x in order to preserve public contract
484                 // they are public for Mono 1.1.x (for 1.2) as the API isn't froze ATM
485
486                 public bool UseKeyBlinding {
487                         get { return keyBlinding; }
488                         // you REALLY shoudn't touch this (true is fine ;-)
489                         set { keyBlinding = value; }
490                 }
491
492                 public bool IsCrtPossible {
493                         // either the key pair isn't generated (and will be 
494                         // generated with CRT parameters) or CRT is (or isn't)
495                         // possible (in case the key was imported)
496                         get { return (!keypairGenerated || isCRTpossible); }
497                 }
498
499                 private byte[] GetPaddedValue (BigInteger value, int length)
500                 {
501                         byte[] result = value.GetBytes ();
502                         if (result.Length >= length)
503                                 return result;
504
505                         // left-pad 0x00 value on the result (same integer, correct length)
506                         byte[] padded = new byte[length];
507                         Buffer.BlockCopy (result, 0, padded, (length - result.Length), result.Length);
508                         // temporary result may contain decrypted (plaintext) data, clear it
509                         Array.Clear (result, 0, result.Length);
510                         return padded;
511                 }
512         }
513 }