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 class RSACryptoServiceProvider : RSA
41 , ICspAsymmetricAlgorithm
43 private int _dwKeySize;
44 private CspParameters _parameters;
45 private bool _randomKeyContainer;
46 [System.Security.SecurityCritical] // auto-generated
47 private SafeProvHandle _safeProvHandle;
48 [System.Security.SecurityCritical] // auto-generated
49 private SafeKeyHandle _safeKeyHandle;
51 private static volatile CspProviderFlags s_UseMachineKeyStore = 0;
57 [System.Security.SecurityCritical] // auto-generated
58 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
59 [ResourceExposure(ResourceScope.None)]
60 [SuppressUnmanagedCodeSecurity]
61 private static extern void DecryptKey(SafeKeyHandle pKeyContext,
62 [MarshalAs(UnmanagedType.LPArray)] byte[] pbEncryptedKey,
64 [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
65 ObjectHandleOnStack ohRetDecryptedKey);
67 [System.Security.SecurityCritical] // auto-generated
68 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
69 [ResourceExposure(ResourceScope.None)]
70 [SuppressUnmanagedCodeSecurity]
71 private static extern void EncryptKey(SafeKeyHandle pKeyContext,
72 [MarshalAs(UnmanagedType.LPArray)] byte[] pbKey,
74 [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
75 ObjectHandleOnStack ohRetEncryptedKey);
78 // public constructors
81 [System.Security.SecuritySafeCritical] // auto-generated
82 [ResourceExposure(ResourceScope.None)]
83 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
84 public RSACryptoServiceProvider()
85 : this(0, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), true) {
88 [System.Security.SecuritySafeCritical] // auto-generated
89 [ResourceExposure(ResourceScope.None)]
90 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
91 public RSACryptoServiceProvider(int dwKeySize)
92 : this(dwKeySize, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), false) {
95 [System.Security.SecuritySafeCritical] // auto-generated
96 public RSACryptoServiceProvider(CspParameters parameters)
97 : this(0, parameters, true) {
100 [System.Security.SecuritySafeCritical] // auto-generated
101 public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters)
102 : this(dwKeySize, parameters, false) {
109 [System.Security.SecurityCritical] // auto-generated
110 private RSACryptoServiceProvider(int dwKeySize, CspParameters parameters, bool useDefaultKeySize) {
112 throw new ArgumentOutOfRangeException("dwKeySize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
113 Contract.EndContractBlock();
115 _parameters = Utils.SaveCspParameters(CspAlgorithmType.Rsa, parameters, s_UseMachineKeyStore, ref _randomKeyContainer);
117 LegalKeySizesValue = new KeySizes[] { new KeySizes(384, 16384, 8) };
118 _dwKeySize = useDefaultKeySize ? 1024 : dwKeySize;
120 // If this is not a random container we generate, create it eagerly
121 // in the constructor so we can report any errors now.
122 if (!_randomKeyContainer
124 || Environment.GetCompatibilityFlag(CompatibilityFlag.EagerlyGenerateRandomAsymmKeys)
125 #endif //!FEATURE_CORECLR
130 [System.Security.SecurityCritical] // auto-generated
131 private void GetKeyPair () {
132 if (_safeKeyHandle == null) {
134 if (_safeKeyHandle == null) {
135 // We only attempt to generate a random key on desktop runtimes because the CoreCLR
136 // RSA surface area is limited to simply verifying signatures. Since generating a
137 // random key to verify signatures will always lead to failure (unless we happend to
138 // win the lottery and randomly generate the signing key ...), there is no need
139 // to add this functionality to CoreCLR at this point.
140 Utils.GetKeyPairHelper(CspAlgorithmType.Rsa, _parameters, _randomKeyContainer, _dwKeySize, ref _safeProvHandle, ref _safeKeyHandle);
146 [System.Security.SecuritySafeCritical] // overrides public transparent member
147 protected override void Dispose(bool disposing)
149 base.Dispose(disposing);
151 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
152 _safeKeyHandle.Dispose();
153 if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
154 _safeProvHandle.Dispose();
161 [System.Runtime.InteropServices.ComVisible(false)]
162 public bool PublicOnly {
163 [System.Security.SecuritySafeCritical] // auto-generated
166 byte[] publicKey = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_PUBLICKEYONLY);
167 return (publicKey[0] == 1);
171 [System.Runtime.InteropServices.ComVisible(false)]
172 public CspKeyContainerInfo CspKeyContainerInfo {
173 [System.Security.SecuritySafeCritical] // auto-generated
176 return new CspKeyContainerInfo(_parameters, _randomKeyContainer);
180 public override int KeySize {
181 [System.Security.SecuritySafeCritical] // auto-generated
184 byte[] keySize = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_KEYLEN);
185 _dwKeySize = (keySize[0] | (keySize[1] << 8) | (keySize[2] << 16) | (keySize[3] << 24));
190 public override string KeyExchangeAlgorithm {
192 if (_parameters.KeyNumber == Constants.AT_KEYEXCHANGE)
193 return "RSA-PKCS1-KeyEx";
198 public override string SignatureAlgorithm {
199 get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
202 public static bool UseMachineKeyStore {
203 get { return (s_UseMachineKeyStore == CspProviderFlags.UseMachineKeyStore); }
204 set { s_UseMachineKeyStore = (value ? CspProviderFlags.UseMachineKeyStore : 0); }
207 public bool PersistKeyInCsp {
208 [System.Security.SecuritySafeCritical] // auto-generated
210 if (_safeProvHandle == null) {
212 if (_safeProvHandle == null)
213 _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
216 return Utils.GetPersistKeyInCsp(_safeProvHandle);
218 [System.Security.SecuritySafeCritical] // auto-generated
220 bool oldPersistKeyInCsp = this.PersistKeyInCsp;
221 if (value == oldPersistKeyInCsp)
224 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
225 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
227 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Delete);
228 kp.AccessEntries.Add(entry);
230 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Create);
231 kp.AccessEntries.Add(entry);
236 Utils.SetPersistKeyInCsp(_safeProvHandle, value);
244 [System.Security.SecuritySafeCritical] // auto-generated
245 public override RSAParameters ExportParameters (bool includePrivateParameters) {
247 if (includePrivateParameters) {
248 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
249 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
250 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Export);
251 kp.AccessEntries.Add(entry);
255 RSACspObject rsaCspObject = new RSACspObject();
256 int blobType = includePrivateParameters ? Constants.PRIVATEKEYBLOB : Constants.PUBLICKEYBLOB;
257 // _ExportKey will check for failures and throw an exception
258 Utils._ExportKey(_safeKeyHandle, blobType, rsaCspObject);
259 return RSAObjectToStruct(rsaCspObject);
262 #if FEATURE_LEGACYNETCFCRYPTO
263 [System.Security.SecurityCritical]
265 [System.Security.SecuritySafeCritical] // auto-generated
267 [System.Runtime.InteropServices.ComVisible(false)]
268 public byte[] ExportCspBlob (bool includePrivateParameters) {
270 return Utils.ExportCspBlobHelper(includePrivateParameters, _parameters, _safeKeyHandle);
273 [System.Security.SecuritySafeCritical] // auto-generated
274 public override void ImportParameters(RSAParameters parameters) {
275 // Free the current key handle
276 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) {
277 _safeKeyHandle.Dispose();
278 _safeKeyHandle = null;
281 RSACspObject rsaCspObject = RSAStructToObject(parameters);
282 _safeKeyHandle = SafeKeyHandle.InvalidHandle;
284 if (IsPublic(parameters)) {
285 // Use our CRYPT_VERIFYCONTEXT handle, CRYPT_EXPORTABLE is not applicable to public only keys, so pass false
286 Utils._ImportKey(Utils.StaticProvHandle, Constants.CALG_RSA_KEYX, (CspProviderFlags) 0, rsaCspObject, ref _safeKeyHandle);
288 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
289 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
290 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Import);
291 kp.AccessEntries.Add(entry);
294 if (_safeProvHandle == null)
295 _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
296 // Now, import the key into the CSP; _ImportKey will check for failures.
297 Utils._ImportKey(_safeProvHandle, Constants.CALG_RSA_KEYX, _parameters.Flags, rsaCspObject, ref _safeKeyHandle);
301 #if FEATURE_LEGACYNETCFCRYPTO
302 [System.Security.SecurityCritical]
304 [System.Security.SecuritySafeCritical] // auto-generated
306 [System.Runtime.InteropServices.ComVisible(false)]
307 public void ImportCspBlob (byte[] keyBlob) {
308 Utils.ImportCspBlobHelper(CspAlgorithmType.Rsa, keyBlob, IsPublic(keyBlob), ref _parameters, _randomKeyContainer, ref _safeProvHandle, ref _safeKeyHandle);
311 public byte[] SignData(Stream inputStream, Object halg) {
312 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
313 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
314 byte[] hashVal = hash.ComputeHash(inputStream);
315 return SignHash(hashVal, calgHash);
318 public byte[] SignData(byte[] buffer, Object halg) {
319 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
320 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
321 byte[] hashVal = hash.ComputeHash(buffer);
322 return SignHash(hashVal, calgHash);
325 public byte[] SignData(byte[] buffer, int offset, int count, Object halg) {
326 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
327 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
328 byte[] hashVal = hash.ComputeHash(buffer, offset, count);
329 return SignHash(hashVal, calgHash);
332 public bool VerifyData(byte[] buffer, Object halg, byte[] signature) {
333 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
334 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
335 byte[] hashVal = hash.ComputeHash(buffer);
336 return VerifyHash(hashVal, calgHash, signature);
339 public byte[] SignHash(byte[] rgbHash, string str) {
341 throw new ArgumentNullException("rgbHash");
342 Contract.EndContractBlock();
344 throw new CryptographicException(Environment.GetResourceString("Cryptography_CSP_NoPrivateKey"));
346 int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
347 return SignHash(rgbHash, calgHash);
350 [SecuritySafeCritical]
351 internal byte[] SignHash(byte[] rgbHash, int calgHash) {
352 Contract.Requires(rgbHash != null);
355 if (!CspKeyContainerInfo.RandomlyGenerated) {
356 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
357 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
358 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Sign);
359 kp.AccessEntries.Add(entry);
363 return Utils.SignValue(_safeKeyHandle, _parameters.KeyNumber, Constants.CALG_RSA_SIGN, calgHash, rgbHash);
366 public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) {
368 throw new ArgumentNullException("rgbHash");
369 if (rgbSignature == null)
370 throw new ArgumentNullException("rgbSignature");
371 Contract.EndContractBlock();
373 int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
374 return VerifyHash(rgbHash, calgHash, rgbSignature);
377 [SecuritySafeCritical]
378 internal bool VerifyHash(byte[] rgbHash, int calgHash, byte[] rgbSignature) {
379 Contract.Requires(rgbHash != null);
380 Contract.Requires(rgbSignature != null);
384 return Utils.VerifySign(_safeKeyHandle, Constants.CALG_RSA_SIGN, calgHash, rgbHash, rgbSignature);
388 /// Encrypt raw data, generally used for encrypting symmetric key material.
391 /// This method can only encrypt (keySize - 88 bits) of data, so should not be used for encrypting
392 /// arbitrary byte arrays. Instead, encrypt a symmetric key with this method, and use the symmetric
393 /// key to encrypt the sensitive data.
395 /// <param name="rgb">raw data to encryt</param>
396 /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
397 /// <returns>Encrypted key</returns>
398 [System.Security.SecuritySafeCritical] // auto-generated
399 public byte[] Encrypt(byte[] rgb, bool fOAEP) {
401 throw new ArgumentNullException("rgb");
402 Contract.EndContractBlock();
406 byte[] encryptedKey = null;
407 EncryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref encryptedKey));
412 /// Decrypt raw data, generally used for decrypting symmetric key material
414 /// <param name="rgb">encrypted data</param>
415 /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
416 /// <returns>decrypted data</returns>
417 [System.Security.SecuritySafeCritical] // auto-generated
418 public byte [] Decrypt(byte[] rgb, bool fOAEP) {
420 throw new ArgumentNullException("rgb");
421 Contract.EndContractBlock();
425 // size check -- must be at most the modulus size
426 if (rgb.Length > (KeySize / 8))
427 throw new CryptographicException(Environment.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize / 8));
429 if (!CspKeyContainerInfo.RandomlyGenerated) {
430 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
431 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
432 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Decrypt);
433 kp.AccessEntries.Add(entry);
438 byte[] decryptedKey = null;
439 DecryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref decryptedKey));
443 public override byte[] DecryptValue(byte[] rgb) {
444 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
447 public override byte[] EncryptValue(byte[] rgb) {
448 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
452 // private static methods
455 private static RSAParameters RSAObjectToStruct (RSACspObject rsaCspObject) {
456 RSAParameters rsaParams = new RSAParameters();
457 rsaParams.Exponent = rsaCspObject.Exponent;
458 rsaParams.Modulus = rsaCspObject.Modulus;
459 rsaParams.P = rsaCspObject.P;
460 rsaParams.Q = rsaCspObject.Q;
461 rsaParams.DP = rsaCspObject.DP;
462 rsaParams.DQ = rsaCspObject.DQ;
463 rsaParams.InverseQ = rsaCspObject.InverseQ;
464 rsaParams.D = rsaCspObject.D;
468 private static RSACspObject RSAStructToObject (RSAParameters rsaParams) {
469 RSACspObject rsaCspObject = new RSACspObject();
470 rsaCspObject.Exponent = rsaParams.Exponent;
471 rsaCspObject.Modulus = rsaParams.Modulus;
472 rsaCspObject.P = rsaParams.P;
473 rsaCspObject.Q = rsaParams.Q;
474 rsaCspObject.DP = rsaParams.DP;
475 rsaCspObject.DQ = rsaParams.DQ;
476 rsaCspObject.InverseQ = rsaParams.InverseQ;
477 rsaCspObject.D = rsaParams.D;
481 // find whether an RSA key blob is public.
482 private static bool IsPublic (byte[] keyBlob) {
484 throw new ArgumentNullException("keyBlob");
485 Contract.EndContractBlock();
487 // The CAPI RSA public key representation consists of the following sequence:
491 // The first should be PUBLICKEYBLOB and magic should be RSA_PUB_MAGIC "RSA1"
492 if (keyBlob[0] != Constants.PUBLICKEYBLOB)
495 if (keyBlob[11] != 0x31 || keyBlob[10] != 0x41 || keyBlob[9] != 0x53 || keyBlob[8] != 0x52)
501 // Since P is required, we will assume its presence is synonymous to a private key.
502 private static bool IsPublic(RSAParameters rsaParams) {
503 return (rsaParams.P == null);
507 // Adapt new RSA abstraction to legacy RSACryptoServiceProvider surface area.
510 // NOTE: For the new API, we go straight to CAPI for fixed set of hash algorithms and don't use crypto config here.
513 // 1. We're moving away from crypto config and we won't have it when porting to .NET Core
515 // 2. It's slow to lookup and slow to use as the base HashAlgorithm adds considerable overhead
516 // (redundant defensive copy + double-initialization for the single-use case).
519 [SecuritySafeCritical]
520 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
521 // we're sealed and the base should have checked this already
522 Contract.Assert(data != null);
523 Contract.Assert(offset >= 0 && offset <= data.Length);
524 Contract.Assert(count >= 0 && count <= data.Length);
525 Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
527 using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
528 Utils.HashData(hashHandle, data, offset, count);
529 return Utils.EndHash(hashHandle);
533 [SecuritySafeCritical]
534 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
535 // we're sealed and the base should have checked this already
536 Contract.Assert(data != null);
537 Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
539 using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
540 // Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm
541 byte[] buffer = new byte[4096];
544 bytesRead = data.Read(buffer, 0, buffer.Length);
546 Utils.HashData(hashHandle, buffer, 0, bytesRead);
548 } while (bytesRead > 0);
550 return Utils.EndHash(hashHandle);
554 private static int GetAlgorithmId(HashAlgorithmName hashAlgorithm) {
555 switch (hashAlgorithm.Name) {
557 return Constants.CALG_MD5;
559 return Constants.CALG_SHA1;
561 return Constants.CALG_SHA_256;
563 return Constants.CALG_SHA_384;
565 return Constants.CALG_SHA_512;
567 throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name));
571 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
573 throw new ArgumentNullException("data");
575 if (padding == null) {
576 throw new ArgumentNullException("padding");
579 if (padding == RSAEncryptionPadding.Pkcs1) {
580 return Encrypt(data, fOAEP: false);
581 } else if (padding == RSAEncryptionPadding.OaepSHA1) {
582 return Encrypt(data, fOAEP: true);
584 throw PaddingModeNotSupported();
588 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
590 throw new ArgumentNullException("data");
592 if (padding == null) {
593 throw new ArgumentNullException("padding");
596 if (padding == RSAEncryptionPadding.Pkcs1) {
597 return Decrypt(data, fOAEP: false);
598 } else if (padding == RSAEncryptionPadding.OaepSHA1) {
599 return Decrypt(data, fOAEP: true);
601 throw PaddingModeNotSupported();
605 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
607 throw new ArgumentNullException("hash");
609 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
610 throw HashAlgorithmNameNullOrEmpty();
612 if (padding == null) {
613 throw new ArgumentNullException("padding");
615 if (padding != RSASignaturePadding.Pkcs1) {
616 throw PaddingModeNotSupported();
619 return SignHash(hash, GetAlgorithmId(hashAlgorithm));
622 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
624 throw new ArgumentNullException("hash");
626 if (signature == null) {
627 throw new ArgumentNullException("signature");
629 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
630 throw HashAlgorithmNameNullOrEmpty();
632 if (padding == null) {
633 throw new ArgumentNullException("padding");
635 if (padding != RSASignaturePadding.Pkcs1) {
636 throw PaddingModeNotSupported();
639 return VerifyHash(hash, GetAlgorithmId(hashAlgorithm), signature);
642 private static Exception PaddingModeNotSupported() {
643 return new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode"));