2 // RSACryptoServiceProvider.cs: Handles an RSA implementation.
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 // (C) 2004 Novell (http://www.novell.com)
17 using Mono.Security.Cryptography;
19 namespace System.Security.Cryptography {
21 public sealed class RSACryptoServiceProvider : RSA {
23 private const int PROV_RSA_FULL = 1; // from WinCrypt.h
25 private KeyPairPersistence store;
26 private bool persistKey;
27 private bool persisted;
29 private bool privateKeyExportable = true;
30 private bool m_disposed;
32 private RSAManaged rsa;
34 public RSACryptoServiceProvider ()
36 // Here it's not clear if we need to generate a keypair
37 // (note: MS implementation generates a keypair in this case).
39 // (a) often use this constructor to import an existing keypair.
40 // (b) take a LOT of time to generate the RSA keypair
41 // So we'll generate the keypair only when (and if) it's being
42 // used (or exported). This should save us a lot of time (at
43 // least in the unit tests).
47 public RSACryptoServiceProvider (CspParameters parameters)
49 Common (1024, parameters);
50 // no keypair generation done at this stage
53 public RSACryptoServiceProvider (int dwKeySize)
55 // Here it's clear that we need to generate a new keypair
56 Common (dwKeySize, null);
57 // no keypair generation done at this stage
60 public RSACryptoServiceProvider (int dwKeySize, CspParameters parameters)
62 Common (dwKeySize, parameters);
63 // no keypair generation done at this stage
66 private void Common (int dwKeySize, CspParameters p)
68 // Microsoft RSA CSP can do between 384 and 16384 bits keypair
69 LegalKeySizesValue = new KeySizes [1];
70 LegalKeySizesValue [0] = new KeySizes (384, 16384, 8);
71 base.KeySize = dwKeySize;
73 rsa = new RSAManaged (KeySize);
74 rsa.KeyGenerated += new RSAManaged.KeyGeneratedEventHandler (OnKeyGenerated);
76 persistKey = (p != null);
78 p = new CspParameters (PROV_RSA_FULL);
80 if (useMachineKeyStore)
81 p.Flags |= CspProviderFlags.UseMachineKeyStore;
83 store = new KeyPairPersistence (p);
84 // no need to load - it cannot exists
87 store = new KeyPairPersistence (p);
89 if (store.KeyValue != null) {
91 this.FromXmlString (store.KeyValue);
97 private static bool useMachineKeyStore = false;
99 public static bool UseMachineKeyStore {
100 get { return useMachineKeyStore; }
101 set { useMachineKeyStore = value; }
105 ~RSACryptoServiceProvider ()
107 // Zeroize private key
111 public override string KeyExchangeAlgorithm {
112 get { return "RSA-PKCS1-KeyEx"; }
115 public override int KeySize {
124 public bool PersistKeyInCsp {
125 get { return persistKey; }
129 OnKeyGenerated (rsa, null);
133 #if (NET_1_0 || NET_1_1)
139 get { return rsa.PublicOnly; }
142 public override string SignatureAlgorithm {
143 get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
146 public byte[] Decrypt (byte[] rgb, bool fOAEP)
150 throw new ObjectDisposedException ("rsa");
152 // choose between OAEP or PKCS#1 v.1.5 padding
153 AsymmetricKeyExchangeDeformatter def = null;
155 def = new RSAOAEPKeyExchangeDeformatter (rsa);
157 def = new RSAPKCS1KeyExchangeDeformatter (rsa);
159 return def.DecryptKeyExchange (rgb);
162 // NOTE: Unlike MS we need this method
163 // LAMESPEC: Not available from MS .NET framework but MS don't tell
164 // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
165 // only encrypt/decrypt session (secret) key using asymmetric keys.
166 // Using this method to decrypt data IS dangerous (and very slow).
167 public override byte[] DecryptValue (byte[] rgb)
169 return rsa.DecryptValue (rgb);
172 public byte[] Encrypt (byte[] rgb, bool fOAEP)
174 // choose between OAEP or PKCS#1 v.1.5 padding
175 AsymmetricKeyExchangeFormatter fmt = null;
177 fmt = new RSAOAEPKeyExchangeFormatter (rsa);
179 fmt = new RSAPKCS1KeyExchangeFormatter (rsa);
181 return fmt.CreateKeyExchange (rgb);
184 // NOTE: Unlike MS we need this method
185 // LAMESPEC: Not available from MS .NET framework but MS don't tell
186 // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
187 // only encrypt/decrypt session (secret) key using asymmetric keys.
188 // Using this method to encrypt data IS dangerous (and very slow).
189 public override byte[] EncryptValue (byte[] rgb)
191 return rsa.EncryptValue (rgb);
194 public override RSAParameters ExportParameters (bool includePrivateParameters)
196 if ((includePrivateParameters) && (!privateKeyExportable))
197 throw new CryptographicException ("cannot export private key");
199 return rsa.ExportParameters (includePrivateParameters);
202 public override void ImportParameters (RSAParameters parameters)
204 rsa.ImportParameters (parameters);
207 private HashAlgorithm GetHash (object halg)
210 throw new ArgumentNullException ("halg");
212 HashAlgorithm hash = null;
214 hash = HashAlgorithm.Create ((String)halg);
215 else if (halg is HashAlgorithm)
216 hash = (HashAlgorithm) halg;
217 else if (halg is Type)
218 hash = (HashAlgorithm) Activator.CreateInstance ((Type)halg);
220 throw new ArgumentException ("halg");
225 // NOTE: this method can work with ANY configured (OID in machine.config)
226 // HashAlgorithm descendant
227 public byte[] SignData (byte[] buffer, object halg)
231 throw new ArgumentNullException ("buffer");
233 return SignData (buffer, 0, buffer.Length, halg);
236 // NOTE: this method can work with ANY configured (OID in machine.config)
237 // HashAlgorithm descendant
238 public byte[] SignData (Stream inputStream, object halg)
240 HashAlgorithm hash = GetHash (halg);
241 byte[] toBeSigned = hash.ComputeHash (inputStream);
242 return PKCS1.Sign_v15 (this, hash, toBeSigned);
245 // NOTE: this method can work with ANY configured (OID in machine.config)
246 // HashAlgorithm descendant
247 public byte[] SignData (byte[] buffer, int offset, int count, object halg)
249 HashAlgorithm hash = GetHash (halg);
250 byte[] toBeSigned = hash.ComputeHash (buffer, offset, count);
251 return PKCS1.Sign_v15 (this, hash, toBeSigned);
254 private string GetHashNameFromOID (string oid)
257 case "1.3.14.3.2.26":
259 case "1.2.840.113549.2.5":
262 throw new NotSupportedException (oid + " is an unsupported hash algorithm for RSA signing");
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 method to get a hash algorithm from an OID.
269 // However there's no such limit when using the [De]Formatter class.
270 public byte[] SignHash (byte[] rgbHash, string str)
273 throw new ArgumentNullException ("rgbHash");
275 throw new CryptographicException (Locale.GetText ("No OID specified"));
277 HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
278 return PKCS1.Sign_v15 (this, hash, rgbHash);
281 // NOTE: this method can work with ANY configured (OID in machine.config)
282 // HashAlgorithm descendant
283 public bool VerifyData (byte[] buffer, object halg, byte[] signature)
287 throw new ArgumentNullException ("buffer");
289 if (signature == null)
290 throw new ArgumentNullException ("signature");
292 HashAlgorithm hash = GetHash (halg);
293 byte[] toBeVerified = hash.ComputeHash (buffer);
294 return PKCS1.Verify_v15 (this, hash, toBeVerified, signature);
297 // LAMESPEC: str is not the hash name but an OID
298 // NOTE: this method is LIMITED to SHA1 and MD5 like the MS framework 1.0
299 // and 1.1 because there's no method to get a hash algorithm from an OID.
300 // However there's no such limit when using the [De]Formatter class.
301 public bool VerifyHash (byte[] rgbHash, string str, byte[] rgbSignature)
304 throw new ArgumentNullException ("rgbHash");
305 if (rgbSignature == null)
306 throw new ArgumentNullException ("rgbSignature");
308 HashAlgorithm hash = HashAlgorithm.Create (GetHashNameFromOID (str));
309 return PKCS1.Verify_v15 (this, hash, rgbHash, rgbSignature);
312 protected override void Dispose (bool disposing)
315 // the key is persisted and we do not want it persisted
316 if ((persisted) && (!persistKey)) {
317 store.Remove (); // delete the container
322 // no need as they all are abstract before us
329 private void OnKeyGenerated (object sender, EventArgs e)
331 // the key isn't persisted and we want it persisted
332 if ((persistKey) && (!persisted)) {
333 // save the current keypair
334 store.KeyValue = this.ToXmlString (!rsa.PublicOnly);