2003-07-05 Sebastien Pouliot <spouliot@videotron.ca>
[mono.git] / mcs / class / corlib / System.Security.Cryptography / RSACryptoServiceProvider.cs
1 //
2 // RSACryptoServiceProvider.cs: Handles an RSA implementation.
3 //
4 // Authors:
5 //      Sebastien Pouliot (spouliot@motus.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 //
11
12 using System;
13 using System.IO;
14
15 using Mono.Math;
16 using Mono.Security.Cryptography;
17
18 namespace System.Security.Cryptography {
19
20         public sealed class RSACryptoServiceProvider : RSA {
21         
22                 private CspParameters cspParams;
23         
24                 private bool privateKeyExportable = true; 
25                 private bool persistKey = false;
26                 private bool m_disposed = false;
27
28                 private RSAManaged rsa;
29         
30                 public RSACryptoServiceProvider ()
31                 {
32                         // Here it's not clear if we need to generate a keypair
33                         // (note: MS implementation generates a keypair in this case).
34                         // However we:
35                         // (a) often use this constructor to import an existing keypair.
36                         // (b) take a LOT of time to generate the RSA keypair
37                         // So we'll generate the keypair only when (and if) it's being
38                         // used (or exported). This should save us a lot of time (at 
39                         // least in the unit tests).
40                         Common (1024, null);
41                 }
42         
43                 [MonoTODO("Missing keypair persistance using CspParameters")]
44                 public RSACryptoServiceProvider (CspParameters parameters) 
45                 {
46                         Common (1024, parameters);
47                         // no keypair generation done at this stage
48                 }
49         
50                 public RSACryptoServiceProvider (int dwKeySize) 
51                 {
52                         // Here it's clear that we need to generate a new keypair
53                         Common (dwKeySize, null);
54                         // no keypair generation done at this stage
55                 }
56         
57                 // FIXME: We currently dont link with MS CAPI. Anyway this makes
58                 // only sense in Windows - what do we do elsewhere ?
59                 [MonoTODO("Missing keypair persistance using CspParameters")]
60                 public RSACryptoServiceProvider (int dwKeySize, CspParameters parameters) 
61                 {
62                         Common (dwKeySize, parameters);
63                         // no keypair generation done at this stage
64                 }
65         
66                 // FIXME: We currently dont link with MS CAPI. Anyway this makes
67                 // only sense in Windows - what do we do elsewhere ?
68                 private void Common (int dwKeySize, CspParameters p) 
69                 {
70                         if (p == null) {
71                                 cspParams = new CspParameters ();
72 #if ! NET_1_0
73                                 if (useMachineKeyStore)
74                                         cspParams.Flags |= CspProviderFlags.UseMachineKeyStore;
75 #endif
76                                 // TODO: set default values (for keypair persistance)
77                         }
78                         else
79                                 cspParams = p;
80                                 // FIXME: We'll need this to support some kind of persistance
81
82                         // Microsoft RSA CSP can do between 384 and 16384 bits keypair
83                         LegalKeySizesValue = new KeySizes [1];
84                         LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
85                         base.KeySize = dwKeySize;
86
87                         rsa = new RSAManaged (KeySize);
88                 }
89
90 #if ! NET_1_0
91                 private static bool useMachineKeyStore = false;
92
93                 [MonoTODO("Related to keypair persistance")]
94                 public static bool UseMachineKeyStore {
95                         get { return useMachineKeyStore; }
96                         set { useMachineKeyStore = value; }
97                 }
98 #endif
99         
100                 ~RSACryptoServiceProvider () 
101                 {
102                         // Zeroize private key
103                         Dispose (false);
104                 }
105         
106                 public override string KeyExchangeAlgorithm {
107                         get { return "RSA-PKCS1-KeyEx"; }
108                 }
109         
110                 public override int KeySize {
111                         get { 
112                                 if (rsa == null)
113                                       return KeySizeValue; 
114                                 else
115                                       return rsa.KeySize;
116                         }
117                 }
118         
119                 [MonoTODO("Related to keypair persistance")]
120                 public bool PersistKeyInCsp {
121                         get { return persistKey;  }
122                         set { persistKey = value; }
123                 }
124         
125                 public override string SignatureAlgorithm {
126                         get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
127                 }
128         
129                 public byte[] Decrypt (byte[] rgb, bool fOAEP) 
130                 {
131                         // choose between OAEP or PKCS#1 v.1.5 padding
132                         AsymmetricKeyExchangeDeformatter def = null;
133                         if (fOAEP)
134                                 def = new RSAOAEPKeyExchangeDeformatter (rsa);
135                         else
136                                 def = new RSAPKCS1KeyExchangeDeformatter (rsa);
137
138                         return def.DecryptKeyExchange (rgb);
139                 }
140         
141                 // NOTE: Unlike MS we need this method
142                 // LAMESPEC: Not available from MS .NET framework but MS don't tell
143                 // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
144                 // only encrypt/decrypt session (secret) key using asymmetric keys. 
145                 // Using this method to decrypt data IS dangerous (and very slow).
146                 public override byte[] DecryptValue (byte[] rgb) 
147                 {
148                         return rsa.DecryptValue (rgb);
149                 }
150         
151                 public byte[] Encrypt (byte[] rgb, bool fOAEP) 
152                 {
153                         // choose between OAEP or PKCS#1 v.1.5 padding
154                         AsymmetricKeyExchangeFormatter fmt = null;
155                         if (fOAEP)
156                                 fmt = new RSAOAEPKeyExchangeFormatter (rsa);
157                         else
158                                 fmt = new RSAPKCS1KeyExchangeFormatter (rsa);
159
160                         return fmt.CreateKeyExchange (rgb);
161                 }
162         
163                 // NOTE: Unlike MS we need this method
164                 // LAMESPEC: Not available from MS .NET framework but MS don't tell
165                 // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
166                 // only encrypt/decrypt session (secret) key using asymmetric keys. 
167                 // Using this method to encrypt data IS dangerous (and very slow).
168                 public override byte[] EncryptValue (byte[] rgb) 
169                 {
170                         return rsa.EncryptValue (rgb);
171                 }
172         
173                 public override RSAParameters ExportParameters (bool includePrivateParameters) 
174                 {
175                         if ((includePrivateParameters) && (!privateKeyExportable))
176                                 throw new CryptographicException ("cannot export private key");
177
178                         return rsa.ExportParameters (includePrivateParameters);
179                 }
180         
181                 public override void ImportParameters (RSAParameters parameters) 
182                 {
183                         rsa.ImportParameters (parameters);
184                 }
185         
186                 private HashAlgorithm GetHash (object halg) 
187                 {
188                         if (halg == null)
189                                 throw new ArgumentNullException ("halg");
190
191                         HashAlgorithm hash = null;
192                         if (halg is String)
193                                 hash = HashAlgorithm.Create ((String)halg);
194                         else if (halg is HashAlgorithm)
195                                 hash = (HashAlgorithm) halg;
196                         else if (halg is Type)
197                                 hash = (HashAlgorithm) Activator.CreateInstance ((Type)halg);
198                         else
199                                 throw new ArgumentException ("halg");
200
201                         return hash;
202                 }
203         
204                 // NOTE: this method can work with ANY configured (OID in machine.config) 
205                 // HashAlgorithm descendant
206                 public byte[] SignData (byte[] buffer, object halg) 
207                 {
208                         return SignData (buffer, 0, buffer.Length, halg);
209                 }
210         
211                 // NOTE: this method can work with ANY configured (OID in machine.config) 
212                 // HashAlgorithm descendant
213                 public byte[] SignData (Stream inputStream, object halg) 
214                 {
215                         HashAlgorithm hash = GetHash (halg);
216                         byte[] toBeSigned = hash.ComputeHash (inputStream);
217                         return PKCS1.Sign_v15 (this, hash, toBeSigned);
218                 }
219         
220                 // NOTE: this method can work with ANY configured (OID in machine.config) 
221                 // HashAlgorithm descendant
222                 public byte[] SignData (byte[] buffer, int offset, int count, object halg) 
223                 {
224                         HashAlgorithm hash = GetHash (halg);
225                         byte[] toBeSigned = hash.ComputeHash (buffer, offset, count);
226                         return PKCS1.Sign_v15 (this, hash, toBeSigned);
227                 }
228         
229                 private string GetHashNameFromOID (string oid) 
230                 {
231                         switch (oid) {
232                                 case "1.3.14.3.2.26":
233                                         return "SHA1";
234                                 case "1.2.840.113549.2.5":
235                                         return "MD5";
236                                 default:
237                                         throw new NotSupportedException (oid + " is an unsupported hash algorithm for RSA signing");
238                         }
239                 }
240
241                 // LAMESPEC: str is not the hash name but an OID
242                 // NOTE: this method is LIMITED to SHA1 and MD5 like the MS framework 1.0 
243                 // and 1.1 because there's no mathod to get a hash algorithm from an OID. 
244                 // However there's no such limit when using the [De]Formatter class.
245                 public byte[] SignHash (byte[] rgbHash, string str) 
246                 {
247                         if (rgbHash == null)
248                                 throw new ArgumentNullException ("rgbHash");
249         
250                         HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
251                         return PKCS1.Sign_v15 (this, hash, rgbHash);
252                 }
253
254                 // NOTE: this method can work with ANY configured (OID in machine.config) 
255                 // HashAlgorithm descendant
256                 public bool VerifyData (byte[] buffer, object halg, byte[] signature) 
257                 {
258                         if (signature == null)
259                                 throw new ArgumentNullException ("signature");
260
261                         HashAlgorithm hash = GetHash (halg);
262                         byte[] toBeVerified = hash.ComputeHash (buffer);
263                         return PKCS1.Verify_v15 (this, hash, toBeVerified, signature);
264                 }
265         
266                 // LAMESPEC: str is not the hash name but an OID
267                 // NOTE: this method is LIMITED to SHA1 and MD5 like the MS framework 1.0 
268                 // and 1.1 because there's no mathod to get a hash algorithm from an OID. 
269                 // However there's no such limit when using the [De]Formatter class.
270                 public bool VerifyHash (byte[] rgbHash, string str, byte[] rgbSignature) 
271                 {
272                         if (rgbHash == null) 
273                                 throw new ArgumentNullException ("rgbHash");
274                         if (rgbSignature == null)
275                                 throw new ArgumentNullException ("rgbSignature");
276         
277                         HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
278                         return PKCS1.Verify_v15 (this, hash, rgbHash, rgbSignature);
279                 }
280         
281                 protected override void Dispose (bool disposing) 
282                 {
283                         if (rsa != null)
284                                 rsa.Clear ();
285                         // call base class 
286                         // no need as they all are abstract before us
287                         m_disposed = true;
288                 }
289         }
290 }