* FileSystemInfo.cs: corrected COM visibility of UTC properties
[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                 public RSAManaged (int dwKeySize) 
56                 {
57                         KeySizeValue = dwKeySize;
58                         LegalKeySizesValue = new KeySizes [1];
59                         LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
60                 }
61
62                 ~RSAManaged () 
63                 {
64                         // Zeroize private key
65                         Dispose (false);
66                 }
67
68                 private void GenerateKeyPair () 
69                 {
70                         // p and q values should have a length of half the strength in bits
71                         int pbitlength = ((KeySize + 1) >> 1);
72                         int qbitlength = (KeySize - pbitlength);
73                         const uint uint_e = 17;
74                         e = uint_e; // fixed
75         
76                         // generate p, prime and (p-1) relatively prime to e
77                         for (;;) {
78                                 p = BigInteger.genPseudoPrime (pbitlength);
79                                 if (p % uint_e != 1)
80                                         break;
81                         }
82                         // generate a modulus of the required length
83                         for (;;) {
84                                 // generate q, prime and (q-1) relatively prime to e,
85                                 // and not equal to p
86                                 for (;;) {
87                                         q = BigInteger.genPseudoPrime (qbitlength);
88                                         if ((q % uint_e != 1) && (p != q))
89                                                 break;
90                                 }
91         
92                                 // calculate the modulus
93                                 n = p * q;
94                                 if (n.bitCount () == KeySize)
95                                         break;
96         
97                                 // if we get here our primes aren't big enough, make the largest
98                                 // of the two p and try again
99                                 if (p < q)
100                                         p = q;
101                         }
102         
103                         BigInteger pSub1 = (p - 1);
104                         BigInteger qSub1 = (q - 1);
105                         BigInteger phi = pSub1 * qSub1;
106         
107                         // calculate the private exponent
108                         d = e.modInverse (phi);
109         
110                         // calculate the CRT factors
111                         dp = d % pSub1;
112                         dq = d % qSub1;
113                         qInv = q.modInverse (p);
114         
115                         keypairGenerated = true;
116                         isCRTpossible = true;
117
118                         if (KeyGenerated != null)
119                                 KeyGenerated (this);
120                 }
121                 
122                 // overrides from RSA class
123
124                 public override int KeySize {
125                         get { 
126                                 // in case keypair hasn't been (yet) generated
127                                 if (keypairGenerated)
128                                         return n.bitCount (); 
129                                 else
130                                         return base.KeySize;
131                         }
132                 }
133                 public override string KeyExchangeAlgorithm {
134                         get { return "RSA-PKCS1-KeyEx"; }
135                 }
136
137                 // note: this property will exist in RSACryptoServiceProvider in
138                 // version 1.2 of the framework
139                 public bool PublicOnly {
140                         get { return ((d == null) || (n == null)); }
141                 }
142
143                 public override string SignatureAlgorithm {
144                         get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
145                 }
146
147                 public override byte[] DecryptValue (byte[] rgb) 
148                 {
149                         if (m_disposed)
150                                 throw new ObjectDisposedException ("private key");
151
152                         // decrypt operation is used for signature
153                         if (!keypairGenerated)
154                                 GenerateKeyPair ();
155
156                         BigInteger input = new BigInteger (rgb);
157                         BigInteger output;
158                         // decrypt (which uses the private key) can be 
159                         // optimized by using CRT (Chinese Remainder Theorem)
160                         if (isCRTpossible) {
161                                 // m1 = c^dp mod p
162                                 BigInteger m1 = input.modPow (dp, p);
163                                 // m2 = c^dq mod q
164                                 BigInteger m2 = input.modPow (dq, q);
165                                 BigInteger h;
166                                 if (m2 > m1) {
167                                         // thanks to benm!
168                                         h = p - ((m2 - m1) * qInv % p);
169                                         output = m2 + q * h;
170                                 }
171                                 else {
172                                         // h = (m1 - m2) * qInv mod p
173                                         h = (m1 - m2) * qInv % p;
174                                         // m = m2 + q * h;
175                                         output = m2 + q * h;
176                                 }
177                         }
178                         else {
179                                 // m = c^d mod n
180                                 output = input.modPow (d, n);
181                         }
182                         byte[] result = output.getBytes ();
183                         // zeroize value
184                         input.Clear (); 
185                         output.Clear ();
186                         return result;
187                 }
188
189                 public override byte[] EncryptValue (byte[] rgb) 
190                 {
191                         if (m_disposed)
192                                 throw new ObjectDisposedException ("public key");
193
194                         if (!keypairGenerated)
195                                 GenerateKeyPair ();
196
197                         BigInteger input = new BigInteger (rgb);
198                         BigInteger output = input.modPow (e, n);
199                         byte[] result = output.getBytes ();
200                         // zeroize value
201                         input.Clear (); 
202                         output.Clear ();
203                         return result;
204                 }
205
206                 public override RSAParameters ExportParameters (bool includePrivateParameters) 
207                 {
208                         if (m_disposed)
209                                 throw new ObjectDisposedException ("");
210
211                         if (!keypairGenerated)
212                                 GenerateKeyPair ();
213         
214                         RSAParameters param = new RSAParameters ();
215                         param.Exponent = e.getBytes ();
216                         param.Modulus = n.getBytes ();
217                         if (includePrivateParameters) {
218                                 // some parameters are required for exporting the private key
219                                 if ((d == null) || (p == null) || (q == null))
220                                         throw new CryptographicException ("Missing private key");
221                                 param.D = d.getBytes ();
222                                 param.P = p.getBytes ();
223                                 param.Q = q.getBytes ();
224                                 // but CRT parameters are optionals
225                                 if ((dp != null) && (dq != null) && (qInv != null)) {
226                                         // and we include them only if we have them all
227                                         param.DP = dp.getBytes ();
228                                         param.DQ = dq.getBytes ();
229                                         param.InverseQ = qInv.getBytes ();
230                                 }
231                         }
232                         return param;
233                 }
234
235                 public override void ImportParameters (RSAParameters parameters) 
236                 {
237                         if (m_disposed)
238                                 throw new ObjectDisposedException ("");
239
240                         // if missing "mandatory" parameters
241                         if (parameters.Exponent == null) 
242                                 throw new CryptographicException ("Missing Exponent");
243                         if (parameters.Modulus == null)
244                                 throw new CryptographicException ("Missing Modulus");
245         
246                         e = new BigInteger (parameters.Exponent);
247                         n = new BigInteger (parameters.Modulus);
248                         // only if the private key is present
249                         if (parameters.D != null)
250                                 d = new BigInteger (parameters.D);
251                         if (parameters.DP != null)
252                                 dp = new BigInteger (parameters.DP);
253                         if (parameters.DQ != null)
254                                 dq = new BigInteger (parameters.DQ);
255                         if (parameters.InverseQ != null)
256                                 qInv = new BigInteger (parameters.InverseQ);
257                         if (parameters.P != null)
258                                 p = new BigInteger (parameters.P);
259                         if (parameters.Q != null)
260                                 q = new BigInteger (parameters.Q);
261                         
262                         // we now have a keypair
263                         keypairGenerated = true;
264                         isCRTpossible = ((p != null) && (q != null) && (dp != null) && (dq != null) && (qInv != null));
265                 }
266
267                 protected override void Dispose (bool disposing) 
268                 {
269                         if (!m_disposed) {
270                                 // Always zeroize private key
271                                 if (d != null) {
272                                         d.Clear (); 
273                                         d = null;
274                                 }
275                                 if (p != null) {
276                                         p.Clear (); 
277                                         p = null;
278                                 }
279                                 if (q != null) {
280                                         q.Clear (); 
281                                         q = null;
282                                 }
283                                 if (dp != null) {
284                                         dp.Clear (); 
285                                         dp = null;
286                                 }
287                                 if (dq != null) {
288                                         dq.Clear (); 
289                                         dq = null;
290                                 }
291                                 if (qInv != null) {
292                                         qInv.Clear (); 
293                                         qInv = null;
294                                 }
295
296                                 if (disposing) {
297                                         // clear public key
298                                         if (e != null) {
299                                                 e.Clear (); 
300                                                 e = null;
301                                         }
302                                         if (n != null) {
303                                                 n.Clear (); 
304                                                 n = null;
305                                         }
306                                 }
307                         }
308                         // call base class 
309                         // no need as they all are abstract before us
310                         m_disposed = true;
311                 }
312
313                 public delegate void KeyGeneratedEventHandler (object sender);
314
315                 public event KeyGeneratedEventHandler KeyGenerated;
316         }
317 }