2004-06-23 Sebastien Pouliot <sebastien@ximian.com>
[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 // (C) 2004 Novell (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
16 using System;
17 using System.Security.Cryptography;
18
19 using Mono.Math;
20
21 // Big chunks of code are coming from the original RSACryptoServiceProvider class.
22 // The class was refactored to :
23 // a.   ease integration of new hash algorithm (like MD2, RIPEMD160, ...);
24 // b.   provide better support for the coming SSL implementation (requires 
25 //      EncryptValue/DecryptValue) with, or without, Mono runtime/corlib;
26 // c.   provide an alternative RSA implementation for all Windows (like using 
27 //      OAEP without Windows XP).
28
29 namespace Mono.Security.Cryptography {
30
31 #if INSIDE_CORLIB
32         internal
33 #else
34         public
35 #endif
36         class RSAManaged : RSA {
37
38                 private const int defaultKeySize = 1024;
39
40                 private bool isCRTpossible = false;
41                 private bool keypairGenerated = false;
42                 private bool m_disposed = false;
43
44                 private BigInteger d;
45                 private BigInteger p;
46                 private BigInteger q;
47                 private BigInteger dp;
48                 private BigInteger dq;
49                 private BigInteger qInv;
50                 private BigInteger n;           // modulus
51                 private BigInteger e;
52
53                 public RSAManaged () : this (defaultKeySize)
54                 {
55                 }
56
57                 public RSAManaged (int keySize) 
58                 {
59                         KeySizeValue = keySize;
60                         LegalKeySizesValue = new KeySizes [1];
61                         LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
62                 }
63
64                 ~RSAManaged () 
65                 {
66                         // Zeroize private key
67                         Dispose (false);
68                 }
69
70                 private void GenerateKeyPair () 
71                 {
72                         // p and q values should have a length of half the strength in bits
73                         int pbitlength = ((KeySize + 1) >> 1);
74                         int qbitlength = (KeySize - pbitlength);
75                         const uint uint_e = 17;
76                         e = uint_e; // fixed
77         
78                         // generate p, prime and (p-1) relatively prime to e
79                         for (;;) {
80                                 p = BigInteger.GeneratePseudoPrime (pbitlength);
81                                 if (p % uint_e != 1)
82                                         break;
83                         }
84                         // generate a modulus of the required length
85                         for (;;) {
86                                 // generate q, prime and (q-1) relatively prime to e,
87                                 // and not equal to p
88                                 for (;;) {
89                                         q = BigInteger.GeneratePseudoPrime (qbitlength);
90                                         if ((q % uint_e != 1) && (p != q))
91                                                 break;
92                                 }
93         
94                                 // calculate the modulus
95                                 n = p * q;
96                                 if (n.BitCount () == KeySize)
97                                         break;
98         
99                                 // if we get here our primes aren't big enough, make the largest
100                                 // of the two p and try again
101                                 if (p < q)
102                                         p = q;
103                         }
104         
105                         BigInteger pSub1 = (p - 1);
106                         BigInteger qSub1 = (q - 1);
107                         BigInteger phi = pSub1 * qSub1;
108         
109                         // calculate the private exponent
110                         d = e.ModInverse (phi);
111         
112                         // calculate the CRT factors
113                         dp = d % pSub1;
114                         dq = d % qSub1;
115                         qInv = q.ModInverse (p);
116         
117                         keypairGenerated = true;
118                         isCRTpossible = true;
119
120                         if (KeyGenerated != null)
121                                 KeyGenerated (this, null);
122                 }
123                 
124                 // overrides from RSA class
125
126                 public override int KeySize {
127                         get { 
128                                 // in case keypair hasn't been (yet) generated
129                                 if (keypairGenerated)
130                                         return n.BitCount (); 
131                                 else
132                                         return base.KeySize;
133                         }
134                 }
135                 public override string KeyExchangeAlgorithm {
136                         get { return "RSA-PKCS1-KeyEx"; }
137                 }
138
139                 // note: this property will exist in RSACryptoServiceProvider in
140                 // version 1.2 of the framework
141                 public bool PublicOnly {
142                         get { return ((d == null) || (n == null)); }
143                 }
144
145                 public override string SignatureAlgorithm {
146                         get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
147                 }
148
149                 public override byte[] DecryptValue (byte[] rgb) 
150                 {
151                         if (m_disposed)
152                                 throw new ObjectDisposedException ("private key");
153
154                         // decrypt operation is used for signature
155                         if (!keypairGenerated)
156                                 GenerateKeyPair ();
157
158                         BigInteger input = new BigInteger (rgb);
159                         BigInteger output;
160                         // decrypt (which uses the private key) can be 
161                         // optimized by using CRT (Chinese Remainder Theorem)
162                         if (isCRTpossible) {
163                                 // m1 = c^dp mod p
164                                 BigInteger m1 = input.ModPow (dp, p);
165                                 // m2 = c^dq mod q
166                                 BigInteger m2 = input.ModPow (dq, q);
167                                 BigInteger h;
168                                 if (m2 > m1) {
169                                         // thanks to benm!
170                                         h = p - ((m2 - m1) * qInv % p);
171                                         output = m2 + q * h;
172                                 }
173                                 else {
174                                         // h = (m1 - m2) * qInv mod p
175                                         h = (m1 - m2) * qInv % p;
176                                         // m = m2 + q * h;
177                                         output = m2 + q * h;
178                                 }
179                         }
180                         else {
181                                 // m = c^d mod n
182                                 output = input.ModPow (d, n);
183                         }
184                         byte[] result = output.GetBytes ();
185                         // zeroize value
186                         input.Clear (); 
187                         output.Clear ();
188                         return result;
189                 }
190
191                 public override byte[] EncryptValue (byte[] rgb) 
192                 {
193                         if (m_disposed)
194                                 throw new ObjectDisposedException ("public key");
195
196                         if (!keypairGenerated)
197                                 GenerateKeyPair ();
198
199                         BigInteger input = new BigInteger (rgb);
200                         BigInteger output = input.ModPow (e, n);
201                         byte[] result = output.GetBytes ();
202                         // zeroize value
203                         input.Clear (); 
204                         output.Clear ();
205                         return result;
206                 }
207
208                 public override RSAParameters ExportParameters (bool includePrivateParameters) 
209                 {
210                         if (m_disposed)
211                                 throw new ObjectDisposedException ("");
212
213                         if (!keypairGenerated)
214                                 GenerateKeyPair ();
215         
216                         RSAParameters param = new RSAParameters ();
217                         param.Exponent = e.GetBytes ();
218                         param.Modulus = n.GetBytes ();
219                         if (includePrivateParameters) {
220                                 // some parameters are required for exporting the private key
221                                 if ((d == null) || (p == null) || (q == null))
222                                         throw new CryptographicException ("Missing private key");
223                                 param.D = d.GetBytes ();
224                                 // hack for bugzilla #57941 where D wasn't provided
225                                 if (param.D.Length != param.Modulus.Length) {
226                                         byte[] normalizedD = new byte [param.Modulus.Length];
227                                         Buffer.BlockCopy (param.D, 0, normalizedD, (normalizedD.Length - param.D.Length), param.D.Length);
228                                         param.D = normalizedD;
229                                 }
230                                 param.P = p.GetBytes ();
231                                 param.Q = q.GetBytes ();
232                                 // but CRT parameters are optionals
233                                 if ((dp != null) && (dq != null) && (qInv != null)) {
234                                         // and we include them only if we have them all
235                                         param.DP = dp.GetBytes ();
236                                         param.DQ = dq.GetBytes ();
237                                         param.InverseQ = qInv.GetBytes ();
238                                 }
239                         }
240                         return param;
241                 }
242
243                 public override void ImportParameters (RSAParameters parameters) 
244                 {
245                         if (m_disposed)
246                                 throw new ObjectDisposedException ("");
247
248                         // if missing "mandatory" parameters
249                         if (parameters.Exponent == null) 
250                                 throw new CryptographicException ("Missing Exponent");
251                         if (parameters.Modulus == null)
252                                 throw new CryptographicException ("Missing Modulus");
253         
254                         e = new BigInteger (parameters.Exponent);
255                         n = new BigInteger (parameters.Modulus);
256                         // only if the private key is present
257                         if (parameters.D != null)
258                                 d = new BigInteger (parameters.D);
259                         if (parameters.DP != null)
260                                 dp = new BigInteger (parameters.DP);
261                         if (parameters.DQ != null)
262                                 dq = new BigInteger (parameters.DQ);
263                         if (parameters.InverseQ != null)
264                                 qInv = new BigInteger (parameters.InverseQ);
265                         if (parameters.P != null)
266                                 p = new BigInteger (parameters.P);
267                         if (parameters.Q != null)
268                                 q = new BigInteger (parameters.Q);
269                         
270                         // we now have a keypair
271                         keypairGenerated = true;
272                         isCRTpossible = ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null));
273                 }
274
275                 protected override void Dispose (bool disposing) 
276                 {
277                         if (!m_disposed) {
278                                 // Always zeroize private key
279                                 if (d != null) {
280                                         d.Clear (); 
281                                         d = null;
282                                 }
283                                 if (p != null) {
284                                         p.Clear (); 
285                                         p = null;
286                                 }
287                                 if (q != null) {
288                                         q.Clear (); 
289                                         q = null;
290                                 }
291                                 if (dp != null) {
292                                         dp.Clear (); 
293                                         dp = null;
294                                 }
295                                 if (dq != null) {
296                                         dq.Clear (); 
297                                         dq = null;
298                                 }
299                                 if (qInv != null) {
300                                         qInv.Clear (); 
301                                         qInv = null;
302                                 }
303
304                                 if (disposing) {
305                                         // clear public key
306                                         if (e != null) {
307                                                 e.Clear (); 
308                                                 e = null;
309                                         }
310                                         if (n != null) {
311                                                 n.Clear (); 
312                                                 n = null;
313                                         }
314                                 }
315                         }
316                         // call base class 
317                         // no need as they all are abstract before us
318                         m_disposed = true;
319                 }
320
321                 public delegate void KeyGeneratedEventHandler (object sender, EventArgs e);
322
323                 public event KeyGeneratedEventHandler KeyGenerated;
324         }
325 }