3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>[....]</OWNER>
10 // RSACryptoServiceProvider.cs
12 // CSP-based implementation of RSA
15 namespace System.Security.Cryptography {
17 using System.Globalization;
19 using System.Security;
20 using System.Runtime.InteropServices;
21 using System.Runtime.CompilerServices;
22 using System.Runtime.Versioning;
23 using System.Security.Cryptography.X509Certificates;
24 using System.Security.Permissions;
25 using System.Diagnostics.Contracts;
27 // Object layout of the RSAParameters structure
28 internal class RSACspObject {
29 internal byte[] Exponent;
30 internal byte[] Modulus;
35 internal byte[] InverseQ;
39 [System.Runtime.InteropServices.ComVisible(true)]
40 public sealed partial class RSACryptoServiceProvider : RSA
41 , ICspAsymmetricAlgorithm
44 private int _dwKeySize;
45 private CspParameters _parameters;
46 private bool _randomKeyContainer;
47 [System.Security.SecurityCritical] // auto-generated
48 private SafeProvHandle _safeProvHandle;
49 [System.Security.SecurityCritical] // auto-generated
50 private SafeKeyHandle _safeKeyHandle;
52 private static volatile CspProviderFlags s_UseMachineKeyStore = 0;
58 [System.Security.SecurityCritical] // auto-generated
59 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
60 [ResourceExposure(ResourceScope.None)]
61 [SuppressUnmanagedCodeSecurity]
62 private static extern void DecryptKey(SafeKeyHandle pKeyContext,
63 [MarshalAs(UnmanagedType.LPArray)] byte[] pbEncryptedKey,
65 [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
66 ObjectHandleOnStack ohRetDecryptedKey);
68 [System.Security.SecurityCritical] // auto-generated
69 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
70 [ResourceExposure(ResourceScope.None)]
71 [SuppressUnmanagedCodeSecurity]
72 private static extern void EncryptKey(SafeKeyHandle pKeyContext,
73 [MarshalAs(UnmanagedType.LPArray)] byte[] pbKey,
75 [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
76 ObjectHandleOnStack ohRetEncryptedKey);
79 // public constructors
82 [System.Security.SecuritySafeCritical] // auto-generated
83 [ResourceExposure(ResourceScope.None)]
84 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
85 public RSACryptoServiceProvider()
86 : this(0, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), true) {
89 [System.Security.SecuritySafeCritical] // auto-generated
90 [ResourceExposure(ResourceScope.None)]
91 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
92 public RSACryptoServiceProvider(int dwKeySize)
93 : this(dwKeySize, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), false) {
96 [System.Security.SecuritySafeCritical] // auto-generated
97 public RSACryptoServiceProvider(CspParameters parameters)
98 : this(0, parameters, true) {
101 [System.Security.SecuritySafeCritical] // auto-generated
102 public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters)
103 : this(dwKeySize, parameters, false) {
110 [System.Security.SecurityCritical] // auto-generated
111 private RSACryptoServiceProvider(int dwKeySize, CspParameters parameters, bool useDefaultKeySize) {
113 throw new ArgumentOutOfRangeException("dwKeySize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
114 Contract.EndContractBlock();
116 _parameters = Utils.SaveCspParameters(CspAlgorithmType.Rsa, parameters, s_UseMachineKeyStore, ref _randomKeyContainer);
118 LegalKeySizesValue = new KeySizes[] { new KeySizes(384, 16384, 8) };
119 _dwKeySize = useDefaultKeySize ? 1024 : dwKeySize;
121 // If this is not a random container we generate, create it eagerly
122 // in the constructor so we can report any errors now.
123 if (!_randomKeyContainer
125 || Environment.GetCompatibilityFlag(CompatibilityFlag.EagerlyGenerateRandomAsymmKeys)
126 #endif //!FEATURE_CORECLR
131 [System.Security.SecurityCritical] // auto-generated
132 private void GetKeyPair () {
133 if (_safeKeyHandle == null) {
135 if (_safeKeyHandle == null) {
136 // We only attempt to generate a random key on desktop runtimes because the CoreCLR
137 // RSA surface area is limited to simply verifying signatures. Since generating a
138 // random key to verify signatures will always lead to failure (unless we happend to
139 // win the lottery and randomly generate the signing key ...), there is no need
140 // to add this functionality to CoreCLR at this point.
141 Utils.GetKeyPairHelper(CspAlgorithmType.Rsa, _parameters, _randomKeyContainer, _dwKeySize, ref _safeProvHandle, ref _safeKeyHandle);
147 [System.Security.SecuritySafeCritical] // overrides public transparent member
148 protected override void Dispose(bool disposing)
150 base.Dispose(disposing);
152 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
153 _safeKeyHandle.Dispose();
154 if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
155 _safeProvHandle.Dispose();
162 [System.Runtime.InteropServices.ComVisible(false)]
163 public bool PublicOnly {
164 [System.Security.SecuritySafeCritical] // auto-generated
167 byte[] publicKey = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_PUBLICKEYONLY);
168 return (publicKey[0] == 1);
172 [System.Runtime.InteropServices.ComVisible(false)]
173 public CspKeyContainerInfo CspKeyContainerInfo {
174 [System.Security.SecuritySafeCritical] // auto-generated
177 return new CspKeyContainerInfo(_parameters, _randomKeyContainer);
181 public override int KeySize {
182 [System.Security.SecuritySafeCritical] // auto-generated
185 byte[] keySize = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_KEYLEN);
186 _dwKeySize = (keySize[0] | (keySize[1] << 8) | (keySize[2] << 16) | (keySize[3] << 24));
191 public override string KeyExchangeAlgorithm {
193 if (_parameters.KeyNumber == Constants.AT_KEYEXCHANGE)
194 return "RSA-PKCS1-KeyEx";
199 public override string SignatureAlgorithm {
200 get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
203 public static bool UseMachineKeyStore {
204 get { return (s_UseMachineKeyStore == CspProviderFlags.UseMachineKeyStore); }
205 set { s_UseMachineKeyStore = (value ? CspProviderFlags.UseMachineKeyStore : 0); }
208 public bool PersistKeyInCsp {
209 [System.Security.SecuritySafeCritical] // auto-generated
211 if (_safeProvHandle == null) {
213 if (_safeProvHandle == null)
214 _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
217 return Utils.GetPersistKeyInCsp(_safeProvHandle);
219 [System.Security.SecuritySafeCritical] // auto-generated
221 bool oldPersistKeyInCsp = this.PersistKeyInCsp;
222 if (value == oldPersistKeyInCsp)
225 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
226 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
228 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Delete);
229 kp.AccessEntries.Add(entry);
231 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Create);
232 kp.AccessEntries.Add(entry);
237 Utils.SetPersistKeyInCsp(_safeProvHandle, value);
245 [System.Security.SecuritySafeCritical] // auto-generated
246 public override RSAParameters ExportParameters (bool includePrivateParameters) {
248 if (includePrivateParameters) {
249 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
250 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
251 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Export);
252 kp.AccessEntries.Add(entry);
256 RSACspObject rsaCspObject = new RSACspObject();
257 int blobType = includePrivateParameters ? Constants.PRIVATEKEYBLOB : Constants.PUBLICKEYBLOB;
258 // _ExportKey will check for failures and throw an exception
259 Utils._ExportKey(_safeKeyHandle, blobType, rsaCspObject);
260 return RSAObjectToStruct(rsaCspObject);
263 #if FEATURE_LEGACYNETCFCRYPTO
264 [System.Security.SecurityCritical]
266 [System.Security.SecuritySafeCritical] // auto-generated
268 [System.Runtime.InteropServices.ComVisible(false)]
269 public byte[] ExportCspBlob (bool includePrivateParameters) {
271 return Utils.ExportCspBlobHelper(includePrivateParameters, _parameters, _safeKeyHandle);
274 [System.Security.SecuritySafeCritical] // auto-generated
275 public override void ImportParameters(RSAParameters parameters) {
276 // Free the current key handle
277 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) {
278 _safeKeyHandle.Dispose();
279 _safeKeyHandle = null;
282 RSACspObject rsaCspObject = RSAStructToObject(parameters);
283 _safeKeyHandle = SafeKeyHandle.InvalidHandle;
285 if (IsPublic(parameters)) {
286 // Use our CRYPT_VERIFYCONTEXT handle, CRYPT_EXPORTABLE is not applicable to public only keys, so pass false
287 Utils._ImportKey(Utils.StaticProvHandle, Constants.CALG_RSA_KEYX, (CspProviderFlags) 0, rsaCspObject, ref _safeKeyHandle);
289 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
290 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
291 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Import);
292 kp.AccessEntries.Add(entry);
295 if (_safeProvHandle == null)
296 _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
297 // Now, import the key into the CSP; _ImportKey will check for failures.
298 Utils._ImportKey(_safeProvHandle, Constants.CALG_RSA_KEYX, _parameters.Flags, rsaCspObject, ref _safeKeyHandle);
302 #if FEATURE_LEGACYNETCFCRYPTO
303 [System.Security.SecurityCritical]
305 [System.Security.SecuritySafeCritical] // auto-generated
307 [System.Runtime.InteropServices.ComVisible(false)]
308 public void ImportCspBlob (byte[] keyBlob) {
309 Utils.ImportCspBlobHelper(CspAlgorithmType.Rsa, keyBlob, IsPublic(keyBlob), ref _parameters, _randomKeyContainer, ref _safeProvHandle, ref _safeKeyHandle);
312 public byte[] SignData(Stream inputStream, Object halg) {
313 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
314 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
315 byte[] hashVal = hash.ComputeHash(inputStream);
316 return SignHash(hashVal, calgHash);
319 public byte[] SignData(byte[] buffer, Object halg) {
320 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
321 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
322 byte[] hashVal = hash.ComputeHash(buffer);
323 return SignHash(hashVal, calgHash);
326 public byte[] SignData(byte[] buffer, int offset, int count, Object halg) {
327 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
328 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
329 byte[] hashVal = hash.ComputeHash(buffer, offset, count);
330 return SignHash(hashVal, calgHash);
333 public bool VerifyData(byte[] buffer, Object halg, byte[] signature) {
334 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
335 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
336 byte[] hashVal = hash.ComputeHash(buffer);
337 return VerifyHash(hashVal, calgHash, signature);
340 public byte[] SignHash(byte[] rgbHash, string str) {
342 throw new ArgumentNullException("rgbHash");
343 Contract.EndContractBlock();
345 throw new CryptographicException(Environment.GetResourceString("Cryptography_CSP_NoPrivateKey"));
347 int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
348 return SignHash(rgbHash, calgHash);
351 [SecuritySafeCritical]
352 internal byte[] SignHash(byte[] rgbHash, int calgHash) {
353 Contract.Requires(rgbHash != null);
356 if (!CspKeyContainerInfo.RandomlyGenerated) {
357 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
358 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
359 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Sign);
360 kp.AccessEntries.Add(entry);
364 return Utils.SignValue(_safeKeyHandle, _parameters.KeyNumber, Constants.CALG_RSA_SIGN, calgHash, rgbHash);
367 public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) {
369 throw new ArgumentNullException("rgbHash");
370 if (rgbSignature == null)
371 throw new ArgumentNullException("rgbSignature");
372 Contract.EndContractBlock();
374 int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
375 return VerifyHash(rgbHash, calgHash, rgbSignature);
378 [SecuritySafeCritical]
379 internal bool VerifyHash(byte[] rgbHash, int calgHash, byte[] rgbSignature) {
380 Contract.Requires(rgbHash != null);
381 Contract.Requires(rgbSignature != null);
385 return Utils.VerifySign(_safeKeyHandle, Constants.CALG_RSA_SIGN, calgHash, rgbHash, rgbSignature);
389 /// Encrypt raw data, generally used for encrypting symmetric key material.
392 /// This method can only encrypt (keySize - 88 bits) of data, so should not be used for encrypting
393 /// arbitrary byte arrays. Instead, encrypt a symmetric key with this method, and use the symmetric
394 /// key to encrypt the sensitive data.
396 /// <param name="rgb">raw data to encryt</param>
397 /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
398 /// <returns>Encrypted key</returns>
399 [System.Security.SecuritySafeCritical] // auto-generated
400 public byte[] Encrypt(byte[] rgb, bool fOAEP) {
402 throw new ArgumentNullException("rgb");
403 Contract.EndContractBlock();
407 byte[] encryptedKey = null;
408 EncryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref encryptedKey));
413 /// Decrypt raw data, generally used for decrypting symmetric key material
415 /// <param name="rgb">encrypted data</param>
416 /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
417 /// <returns>decrypted data</returns>
418 [System.Security.SecuritySafeCritical] // auto-generated
419 public byte [] Decrypt(byte[] rgb, bool fOAEP) {
421 throw new ArgumentNullException("rgb");
422 Contract.EndContractBlock();
426 // size check -- must be at most the modulus size
427 if (rgb.Length > (KeySize / 8))
428 throw new CryptographicException(Environment.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize / 8));
430 if (!CspKeyContainerInfo.RandomlyGenerated) {
431 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
432 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
433 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Decrypt);
434 kp.AccessEntries.Add(entry);
439 byte[] decryptedKey = null;
440 DecryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref decryptedKey));
444 public override byte[] DecryptValue(byte[] rgb) {
445 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
448 public override byte[] EncryptValue(byte[] rgb) {
449 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
453 // private static methods
456 private static RSAParameters RSAObjectToStruct (RSACspObject rsaCspObject) {
457 RSAParameters rsaParams = new RSAParameters();
458 rsaParams.Exponent = rsaCspObject.Exponent;
459 rsaParams.Modulus = rsaCspObject.Modulus;
460 rsaParams.P = rsaCspObject.P;
461 rsaParams.Q = rsaCspObject.Q;
462 rsaParams.DP = rsaCspObject.DP;
463 rsaParams.DQ = rsaCspObject.DQ;
464 rsaParams.InverseQ = rsaCspObject.InverseQ;
465 rsaParams.D = rsaCspObject.D;
469 private static RSACspObject RSAStructToObject (RSAParameters rsaParams) {
470 RSACspObject rsaCspObject = new RSACspObject();
471 rsaCspObject.Exponent = rsaParams.Exponent;
472 rsaCspObject.Modulus = rsaParams.Modulus;
473 rsaCspObject.P = rsaParams.P;
474 rsaCspObject.Q = rsaParams.Q;
475 rsaCspObject.DP = rsaParams.DP;
476 rsaCspObject.DQ = rsaParams.DQ;
477 rsaCspObject.InverseQ = rsaParams.InverseQ;
478 rsaCspObject.D = rsaParams.D;
482 // find whether an RSA key blob is public.
483 private static bool IsPublic (byte[] keyBlob) {
485 throw new ArgumentNullException("keyBlob");
486 Contract.EndContractBlock();
488 // The CAPI RSA public key representation consists of the following sequence:
492 // The first should be PUBLICKEYBLOB and magic should be RSA_PUB_MAGIC "RSA1"
493 if (keyBlob[0] != Constants.PUBLICKEYBLOB)
496 if (keyBlob[11] != 0x31 || keyBlob[10] != 0x41 || keyBlob[9] != 0x53 || keyBlob[8] != 0x52)
502 // Since P is required, we will assume its presence is synonymous to a private key.
503 private static bool IsPublic(RSAParameters rsaParams) {
504 return (rsaParams.P == null);
508 // Adapt new RSA abstraction to legacy RSACryptoServiceProvider surface area.
511 // NOTE: For the new API, we go straight to CAPI for fixed set of hash algorithms and don't use crypto config here.
514 // 1. We're moving away from crypto config and we won't have it when porting to .NET Core
516 // 2. It's slow to lookup and slow to use as the base HashAlgorithm adds considerable overhead
517 // (redundant defensive copy + double-initialization for the single-use case).
520 [SecuritySafeCritical]
521 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
522 // we're sealed and the base should have checked this already
523 Contract.Assert(data != null);
524 Contract.Assert(offset >= 0 && offset <= data.Length);
525 Contract.Assert(count >= 0 && count <= data.Length);
526 Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
529 throw new NotImplementedException ();
531 using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
532 Utils.HashData(hashHandle, data, offset, count);
533 return Utils.EndHash(hashHandle);
538 [SecuritySafeCritical]
539 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
540 // we're sealed and the base should have checked this already
541 Contract.Assert(data != null);
542 Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
545 throw new NotImplementedException ();
547 using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
548 // Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm
549 byte[] buffer = new byte[4096];
552 bytesRead = data.Read(buffer, 0, buffer.Length);
554 Utils.HashData(hashHandle, buffer, 0, bytesRead);
556 } while (bytesRead > 0);
558 return Utils.EndHash(hashHandle);
563 private static int GetAlgorithmId(HashAlgorithmName hashAlgorithm) {
564 switch (hashAlgorithm.Name) {
566 return Constants.CALG_MD5;
568 return Constants.CALG_SHA1;
570 return Constants.CALG_SHA_256;
572 return Constants.CALG_SHA_384;
574 return Constants.CALG_SHA_512;
576 throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name));
580 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
582 throw new ArgumentNullException("data");
584 if (padding == null) {
585 throw new ArgumentNullException("padding");
588 if (padding == RSAEncryptionPadding.Pkcs1) {
589 return Encrypt(data, fOAEP: false);
590 } else if (padding == RSAEncryptionPadding.OaepSHA1) {
591 return Encrypt(data, fOAEP: true);
593 throw PaddingModeNotSupported();
597 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
599 throw new ArgumentNullException("data");
601 if (padding == null) {
602 throw new ArgumentNullException("padding");
605 if (padding == RSAEncryptionPadding.Pkcs1) {
606 return Decrypt(data, fOAEP: false);
607 } else if (padding == RSAEncryptionPadding.OaepSHA1) {
608 return Decrypt(data, fOAEP: true);
610 throw PaddingModeNotSupported();
614 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
616 throw new ArgumentNullException("hash");
618 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
619 throw HashAlgorithmNameNullOrEmpty();
621 if (padding == null) {
622 throw new ArgumentNullException("padding");
624 if (padding != RSASignaturePadding.Pkcs1) {
625 throw PaddingModeNotSupported();
628 return SignHash(hash, GetAlgorithmId(hashAlgorithm));
631 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
633 throw new ArgumentNullException("hash");
635 if (signature == null) {
636 throw new ArgumentNullException("signature");
638 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
639 throw HashAlgorithmNameNullOrEmpty();
641 if (padding == null) {
642 throw new ArgumentNullException("padding");
644 if (padding != RSASignaturePadding.Pkcs1) {
645 throw PaddingModeNotSupported();
648 return VerifyHash(hash, GetAlgorithmId(hashAlgorithm), signature);
651 private static Exception PaddingModeNotSupported() {
652 return new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode"));