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 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Runtime
.InteropServices
;
35 using Mono
.Security
.Cryptography
;
37 namespace System
.Security
.Cryptography
{
39 public partial class RSACryptoServiceProvider
{
40 private const int PROV_RSA_FULL
= 1; // from WinCrypt.h
41 private const int AT_KEYEXCHANGE
= 1;
42 private const int AT_SIGNATURE
= 2;
44 private KeyPairPersistence store
;
45 private bool persistKey
;
46 private bool persisted
;
48 private bool privateKeyExportable
= true;
49 private bool m_disposed
;
51 private RSAManaged rsa
;
53 public RSACryptoServiceProvider ()
56 // Here it's not clear if we need to generate a keypair
57 // (note: MS implementation generates a keypair in this case).
59 // (a) often use this constructor to import an existing keypair.
60 // (b) take a LOT of time to generate the RSA keypair
61 // So we'll generate the keypair only when (and if) it's being
62 // used (or exported). This should save us a lot of time (at
63 // least in the unit tests).
66 public RSACryptoServiceProvider (CspParameters parameters
)
67 : this (1024, parameters
)
69 // no keypair generation done at this stage
72 public RSACryptoServiceProvider (int dwKeySize
)
74 // Here it's clear that we need to generate a new keypair
75 Common (dwKeySize
, false);
76 // no keypair generation done at this stage
79 public RSACryptoServiceProvider (int dwKeySize
, CspParameters parameters
)
81 bool has_parameters
= parameters
!= null;
82 Common (dwKeySize
, has_parameters
);
85 // no keypair generation done at this stage
88 void Common (int dwKeySize
, bool parameters
)
90 // Microsoft RSA CSP can do between 384 and 16384 bits keypair
91 LegalKeySizesValue
= new KeySizes
[1];
92 LegalKeySizesValue
[0] = new KeySizes (384, 16384, 8);
93 base.KeySize
= dwKeySize
;
95 rsa
= new RSAManaged (KeySize
);
96 rsa
.KeyGenerated
+= new RSAManaged
.KeyGeneratedEventHandler (OnKeyGenerated
);
98 persistKey
= parameters
;
102 // no need to load - it cannot exists
103 var p
= new CspParameters (PROV_RSA_FULL
);
104 if (UseMachineKeyStore
)
105 p
.Flags
|= CspProviderFlags
.UseMachineKeyStore
;
106 store
= new KeyPairPersistence (p
);
109 void Common (CspParameters p
)
111 store
= new KeyPairPersistence (p
);
112 bool exists
= store
.Load ();
113 bool required
= (p
.Flags
& CspProviderFlags
.UseExistingKey
) != 0;
114 privateKeyExportable
= (p
.Flags
& CspProviderFlags
.UseNonExportableKey
) == 0;
116 if (required
&& !exists
)
117 throw new CryptographicException ("Keyset does not exist");
119 if (store
.KeyValue
!= null) {
121 FromXmlString (store
.KeyValue
);
125 ~
RSACryptoServiceProvider ()
127 // Zeroize private key
131 public override string KeyExchangeAlgorithm
{
132 get { return "RSA-PKCS1-KeyEx"; }
135 public override int KeySize
{
144 public bool PersistKeyInCsp
{
145 get { return persistKey; }
149 OnKeyGenerated (rsa
, null);
154 public bool PublicOnly
{
155 get { return rsa.PublicOnly; }
158 public byte[] Decrypt (byte[] rgb
, bool fOAEP
)
161 throw new ArgumentNullException("rgb");
163 // size check -- must be at most the modulus size
164 if (rgb
.Length
> (KeySize
/ 8))
165 throw new CryptographicException(Environment
.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize
/ 8));
168 throw new ObjectDisposedException ("rsa");
169 // choose between OAEP or PKCS#1 v.1.5 padding
170 AsymmetricKeyExchangeDeformatter def
= null;
172 def
= new RSAOAEPKeyExchangeDeformatter (rsa
);
174 def
= new RSAPKCS1KeyExchangeDeformatter (rsa
);
176 return def
.DecryptKeyExchange (rgb
);
179 // NOTE: Unlike MS we need this method
180 // LAMESPEC: Not available from MS .NET framework but MS don't tell
181 // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
182 // only encrypt/decrypt session (secret) key using asymmetric keys.
183 // Using this method to decrypt data IS dangerous (and very slow).
184 public override byte[] DecryptValue (byte[] rgb
)
186 if (!rsa
.IsCrtPossible
)
187 throw new CryptographicException ("Incomplete private key - missing CRT.");
189 return rsa
.DecryptValue (rgb
);
192 public byte[] Encrypt (byte[] rgb
, bool fOAEP
)
194 // choose between OAEP or PKCS#1 v.1.5 padding
195 AsymmetricKeyExchangeFormatter fmt
= null;
197 fmt
= new RSAOAEPKeyExchangeFormatter (rsa
);
199 fmt
= new RSAPKCS1KeyExchangeFormatter (rsa
);
201 return fmt
.CreateKeyExchange (rgb
);
204 // NOTE: Unlike MS we need this method
205 // LAMESPEC: Not available from MS .NET framework but MS don't tell
206 // why! DON'T USE IT UNLESS YOU KNOW WHAT YOU ARE DOING!!! You should
207 // only encrypt/decrypt session (secret) key using asymmetric keys.
208 // Using this method to encrypt data IS dangerous (and very slow).
209 public override byte[] EncryptValue (byte[] rgb
)
211 return rsa
.EncryptValue (rgb
);
214 public override RSAParameters
ExportParameters (bool includePrivateParameters
)
216 if ((includePrivateParameters
) && (!privateKeyExportable
))
217 throw new CryptographicException ("cannot export private key");
219 var rsaParams
= rsa
.ExportParameters (includePrivateParameters
);
220 if (includePrivateParameters
) {
221 // we want an ArgumentNullException is only the D is missing, but a
222 // CryptographicException if other parameters (CRT) are missings
223 if (rsaParams
.D
== null) {
224 throw new ArgumentNullException ("Missing D parameter for the private key.");
225 } else if ((rsaParams
.P
== null) || (rsaParams
.Q
== null) || (rsaParams
.DP
== null) ||
226 (rsaParams
.DQ
== null) || (rsaParams
.InverseQ
== null)) {
227 // note: we can import a private key, using FromXmlString,
228 // without the CRT parameters but we export it using ToXmlString!
229 throw new CryptographicException ("Missing some CRT parameters for the private key.");
236 public override void ImportParameters (RSAParameters parameters
)
238 rsa
.ImportParameters (parameters
);
241 private HashAlgorithm
GetHash (object halg
)
244 throw new ArgumentNullException ("halg");
246 HashAlgorithm hash
= null;
248 hash
= GetHashFromString ((string) halg
);
249 else if (halg
is HashAlgorithm
)
250 hash
= (HashAlgorithm
) halg
;
251 else if (halg
is Type
)
252 hash
= (HashAlgorithm
) Activator
.CreateInstance ((Type
)halg
);
254 throw new ArgumentException ("halg");
257 throw new ArgumentException (
258 "Could not find provider for halg='" + halg
+ "'.",
264 private HashAlgorithm
GetHashFromString (string name
)
266 HashAlgorithm hash
= HashAlgorithm
.Create (name
);
270 return HashAlgorithm
.Create (GetHashNameFromOID (name
));
271 } catch (CryptographicException e
) {
272 throw new ArgumentException (e
.Message
, "halg", e
);
276 // NOTE: this method can work with ANY configured (OID in machine.config)
277 // HashAlgorithm descendant
278 public byte[] SignData (byte[] buffer
, object halg
)
281 throw new ArgumentNullException ("buffer");
282 return SignData (buffer
, 0, buffer
.Length
, halg
);
285 // NOTE: this method can work with ANY configured (OID in machine.config)
286 // HashAlgorithm descendant
287 public byte[] SignData (Stream inputStream
, object halg
)
289 HashAlgorithm hash
= GetHash (halg
);
290 byte[] toBeSigned
= hash
.ComputeHash (inputStream
);
291 return PKCS1
.Sign_v15 (this, hash
, toBeSigned
);
294 // NOTE: this method can work with ANY configured (OID in machine.config)
295 // HashAlgorithm descendant
296 public byte[] SignData (byte[] buffer
, int offset
, int count
, object halg
)
298 HashAlgorithm hash
= GetHash (halg
);
299 byte[] toBeSigned
= hash
.ComputeHash (buffer
, offset
, count
);
300 return PKCS1
.Sign_v15 (this, hash
, toBeSigned
);
303 private string GetHashNameFromOID (string oid
)
306 case "1.3.14.3.2.26":
308 case "1.2.840.113549.2.5":
310 case "2.16.840.1.101.3.4.2.1":
312 case "2.16.840.1.101.3.4.2.2":
314 case "2.16.840.1.101.3.4.2.3":
317 throw new CryptographicException (oid
+ " is an unsupported hash algorithm for RSA signing");
321 public byte[] SignHash (byte[] rgbHash
, string str
)
324 throw new ArgumentNullException ("rgbHash");
325 // Fx 2.0 defaults to the SHA-1
326 string hashName
= (str
== null) ? "SHA1" : GetHashNameFromOID (str
);
327 HashAlgorithm hash
= HashAlgorithm
.Create (hashName
);
328 return PKCS1
.Sign_v15 (this, hash
, rgbHash
);
331 byte[] SignHash(byte[] rgbHash
, int calgHash
)
333 return PKCS1
.Sign_v15 (this, InternalHashToHashAlgorithm (calgHash
), rgbHash
);
336 static HashAlgorithm
InternalHashToHashAlgorithm (int calgHash
)
339 case Constants
.CALG_MD5
:
340 return MD5
.Create ();
341 case Constants
.CALG_SHA1
:
342 return SHA1
.Create ();
343 case Constants
.CALG_SHA_256
:
344 return SHA256
.Create ();
345 case Constants
.CALG_SHA_384
:
346 return SHA384
.Create ();
347 case Constants
.CALG_SHA_512
:
348 return SHA512
.Create ();
351 throw new NotImplementedException (calgHash
.ToString ());
354 // NOTE: this method can work with ANY configured (OID in machine.config)
355 // HashAlgorithm descendant
356 public bool VerifyData (byte[] buffer
, object halg
, byte[] signature
)
359 throw new ArgumentNullException ("buffer");
360 if (signature
== null)
361 throw new ArgumentNullException ("signature");
363 HashAlgorithm hash
= GetHash (halg
);
364 byte[] toBeVerified
= hash
.ComputeHash (buffer
);
365 return PKCS1
.Verify_v15 (this, hash
, toBeVerified
, signature
);
368 public bool VerifyHash (byte[] rgbHash
, string str
, byte[] rgbSignature
)
371 throw new ArgumentNullException ("rgbHash");
372 if (rgbSignature
== null)
373 throw new ArgumentNullException ("rgbSignature");
374 // Fx 2.0 defaults to the SHA-1
375 string hashName
= (str
== null) ? "SHA1" : GetHashNameFromOID (str
);
376 HashAlgorithm hash
= HashAlgorithm
.Create (hashName
);
377 return PKCS1
.Verify_v15 (this, hash
, rgbHash
, rgbSignature
);
380 bool VerifyHash(byte[] rgbHash
, int calgHash
, byte[] rgbSignature
)
382 return PKCS1
.Verify_v15 (this, InternalHashToHashAlgorithm (calgHash
), rgbHash
, rgbSignature
);
385 protected override void Dispose (bool disposing
)
388 // the key is persisted and we do not want it persisted
389 if ((persisted
) && (!persistKey
)) {
390 store
.Remove (); // delete the container
395 // no need as they all are abstract before us
402 private void OnKeyGenerated (object sender
, EventArgs e
)
404 // the key isn't persisted and we want it persisted
405 if ((persistKey
) && (!persisted
)) {
406 // save the current keypair
407 store
.KeyValue
= this.ToXmlString (!rsa
.PublicOnly
);
412 // ICspAsymmetricAlgorithm
415 public CspKeyContainerInfo CspKeyContainerInfo
{
417 return new CspKeyContainerInfo(store
.Parameters
);
422 public byte[] ExportCspBlob (bool includePrivateParameters
)
425 if (includePrivateParameters
)
426 blob
= CryptoConvert
.ToCapiPrivateKeyBlob (this);
428 blob
= CryptoConvert
.ToCapiPublicKeyBlob (this);
430 // ALGID (bytes 4-7) - default is KEYX
431 // 00 24 00 00 (for CALG_RSA_SIGN)
432 // 00 A4 00 00 (for CALG_RSA_KEYX)
433 blob
[5] = (byte) (((store
!= null) && (store
.Parameters
.KeyNumber
== AT_SIGNATURE
)) ? 0x24 : 0xA4);
438 public void ImportCspBlob (byte[] keyBlob
)
441 throw new ArgumentNullException ("keyBlob");
443 RSA rsa
= CryptoConvert
.FromCapiKeyBlob (keyBlob
);
444 if (rsa
is RSACryptoServiceProvider
) {
445 // default (if no change are present in machine.config)
446 RSAParameters rsap
= rsa
.ExportParameters (!(rsa
as RSACryptoServiceProvider
).PublicOnly
);
447 ImportParameters (rsap
);
449 // we can't know from RSA if the private key is available
452 RSAParameters rsap
= rsa
.ExportParameters (true);
453 ImportParameters (rsap
);
457 RSAParameters rsap
= rsa
.ExportParameters (false);
458 ImportParameters (rsap
);
462 var p
= new CspParameters (PROV_RSA_FULL
);
463 p
.KeyNumber
= keyBlob
[5] == 0x24 ? AT_SIGNATURE
: AT_KEYEXCHANGE
;
464 if (UseMachineKeyStore
)
465 p
.Flags
|= CspProviderFlags
.UseMachineKeyStore
;
466 store
= new KeyPairPersistence (p
);