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;
28 // Object layout of the RSAParameters structure
29 internal class RSACspObject {
30 internal byte[] Exponent;
31 internal byte[] Modulus;
36 internal byte[] InverseQ;
41 [System.Runtime.InteropServices.ComVisible(true)]
42 public sealed partial class RSACryptoServiceProvider : RSA
43 , ICspAsymmetricAlgorithm
46 private int _dwKeySize;
47 private CspParameters _parameters;
48 private bool _randomKeyContainer;
49 [System.Security.SecurityCritical] // auto-generated
50 private SafeProvHandle _safeProvHandle;
51 [System.Security.SecurityCritical] // auto-generated
52 private SafeKeyHandle _safeKeyHandle;
54 private static volatile CspProviderFlags s_UseMachineKeyStore = 0;
60 [System.Security.SecurityCritical] // auto-generated
61 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
62 [ResourceExposure(ResourceScope.None)]
63 [SuppressUnmanagedCodeSecurity]
64 private static extern void DecryptKey(SafeKeyHandle pKeyContext,
65 [MarshalAs(UnmanagedType.LPArray)] byte[] pbEncryptedKey,
67 [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
68 ObjectHandleOnStack ohRetDecryptedKey);
70 [System.Security.SecurityCritical] // auto-generated
71 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
72 [ResourceExposure(ResourceScope.None)]
73 [SuppressUnmanagedCodeSecurity]
74 private static extern void EncryptKey(SafeKeyHandle pKeyContext,
75 [MarshalAs(UnmanagedType.LPArray)] byte[] pbKey,
77 [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
78 ObjectHandleOnStack ohRetEncryptedKey);
81 // public constructors
84 [System.Security.SecuritySafeCritical] // auto-generated
85 [ResourceExposure(ResourceScope.None)]
86 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
87 public RSACryptoServiceProvider()
88 : this(0, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), true) {
91 [System.Security.SecuritySafeCritical] // auto-generated
92 [ResourceExposure(ResourceScope.None)]
93 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
94 public RSACryptoServiceProvider(int dwKeySize)
95 : this(dwKeySize, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), false) {
98 [System.Security.SecuritySafeCritical] // auto-generated
99 public RSACryptoServiceProvider(CspParameters parameters)
100 : this(0, parameters, true) {
103 [System.Security.SecuritySafeCritical] // auto-generated
104 public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters)
105 : this(dwKeySize, parameters, false) {
112 [System.Security.SecurityCritical] // auto-generated
113 private RSACryptoServiceProvider(int dwKeySize, CspParameters parameters, bool useDefaultKeySize) {
115 throw new ArgumentOutOfRangeException("dwKeySize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
116 Contract.EndContractBlock();
118 _parameters = Utils.SaveCspParameters(CspAlgorithmType.Rsa, parameters, s_UseMachineKeyStore, ref _randomKeyContainer);
120 LegalKeySizesValue = new KeySizes[] { new KeySizes(384, 16384, 8) };
121 _dwKeySize = useDefaultKeySize ? 1024 : dwKeySize;
123 // If this is not a random container we generate, create it eagerly
124 // in the constructor so we can report any errors now.
125 if (!_randomKeyContainer
127 || Environment.GetCompatibilityFlag(CompatibilityFlag.EagerlyGenerateRandomAsymmKeys)
128 #endif //!FEATURE_CORECLR
133 [System.Security.SecurityCritical] // auto-generated
134 private void GetKeyPair () {
135 if (_safeKeyHandle == null) {
137 if (_safeKeyHandle == null) {
138 // We only attempt to generate a random key on desktop runtimes because the CoreCLR
139 // RSA surface area is limited to simply verifying signatures. Since generating a
140 // random key to verify signatures will always lead to failure (unless we happend to
141 // win the lottery and randomly generate the signing key ...), there is no need
142 // to add this functionality to CoreCLR at this point.
143 Utils.GetKeyPairHelper(CspAlgorithmType.Rsa, _parameters, _randomKeyContainer, _dwKeySize, ref _safeProvHandle, ref _safeKeyHandle);
149 [System.Security.SecuritySafeCritical] // overrides public transparent member
150 protected override void Dispose(bool disposing)
152 base.Dispose(disposing);
154 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
155 _safeKeyHandle.Dispose();
156 if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
157 _safeProvHandle.Dispose();
164 [System.Runtime.InteropServices.ComVisible(false)]
165 public bool PublicOnly {
166 [System.Security.SecuritySafeCritical] // auto-generated
169 byte[] publicKey = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_PUBLICKEYONLY);
170 return (publicKey[0] == 1);
174 [System.Runtime.InteropServices.ComVisible(false)]
175 public CspKeyContainerInfo CspKeyContainerInfo {
176 [System.Security.SecuritySafeCritical] // auto-generated
179 return new CspKeyContainerInfo(_parameters, _randomKeyContainer);
183 public override int KeySize {
184 [System.Security.SecuritySafeCritical] // auto-generated
187 byte[] keySize = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_KEYLEN);
188 _dwKeySize = (keySize[0] | (keySize[1] << 8) | (keySize[2] << 16) | (keySize[3] << 24));
193 public override string KeyExchangeAlgorithm {
195 if (_parameters.KeyNumber == Constants.AT_KEYEXCHANGE)
196 return "RSA-PKCS1-KeyEx";
201 public override string SignatureAlgorithm {
202 get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
205 public static bool UseMachineKeyStore {
206 get { return (s_UseMachineKeyStore == CspProviderFlags.UseMachineKeyStore); }
207 set { s_UseMachineKeyStore = (value ? CspProviderFlags.UseMachineKeyStore : 0); }
210 public bool PersistKeyInCsp {
211 [System.Security.SecuritySafeCritical] // auto-generated
213 if (_safeProvHandle == null) {
215 if (_safeProvHandle == null)
216 _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
219 return Utils.GetPersistKeyInCsp(_safeProvHandle);
221 [System.Security.SecuritySafeCritical] // auto-generated
223 bool oldPersistKeyInCsp = this.PersistKeyInCsp;
224 if (value == oldPersistKeyInCsp)
227 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
228 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
230 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Delete);
231 kp.AccessEntries.Add(entry);
233 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Create);
234 kp.AccessEntries.Add(entry);
239 Utils.SetPersistKeyInCsp(_safeProvHandle, value);
247 [System.Security.SecuritySafeCritical] // auto-generated
248 public override RSAParameters ExportParameters (bool includePrivateParameters) {
250 if (includePrivateParameters) {
251 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
252 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
253 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Export);
254 kp.AccessEntries.Add(entry);
258 RSACspObject rsaCspObject = new RSACspObject();
259 int blobType = includePrivateParameters ? Constants.PRIVATEKEYBLOB : Constants.PUBLICKEYBLOB;
260 // _ExportKey will check for failures and throw an exception
261 Utils._ExportKey(_safeKeyHandle, blobType, rsaCspObject);
262 return RSAObjectToStruct(rsaCspObject);
265 #if FEATURE_LEGACYNETCFCRYPTO
266 [System.Security.SecurityCritical]
268 [System.Security.SecuritySafeCritical] // auto-generated
270 [System.Runtime.InteropServices.ComVisible(false)]
271 public byte[] ExportCspBlob (bool includePrivateParameters) {
273 return Utils.ExportCspBlobHelper(includePrivateParameters, _parameters, _safeKeyHandle);
276 [System.Security.SecuritySafeCritical] // auto-generated
277 public override void ImportParameters(RSAParameters parameters) {
278 // Free the current key handle
279 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) {
280 _safeKeyHandle.Dispose();
281 _safeKeyHandle = null;
284 RSACspObject rsaCspObject = RSAStructToObject(parameters);
285 _safeKeyHandle = SafeKeyHandle.InvalidHandle;
287 if (IsPublic(parameters)) {
288 // Use our CRYPT_VERIFYCONTEXT handle, CRYPT_EXPORTABLE is not applicable to public only keys, so pass false
289 Utils._ImportKey(Utils.StaticProvHandle, Constants.CALG_RSA_KEYX, (CspProviderFlags) 0, rsaCspObject, ref _safeKeyHandle);
291 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
292 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
293 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Import);
294 kp.AccessEntries.Add(entry);
297 if (_safeProvHandle == null)
298 _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
299 // Now, import the key into the CSP; _ImportKey will check for failures.
300 Utils._ImportKey(_safeProvHandle, Constants.CALG_RSA_KEYX, _parameters.Flags, rsaCspObject, ref _safeKeyHandle);
304 #if FEATURE_LEGACYNETCFCRYPTO
305 [System.Security.SecurityCritical]
307 [System.Security.SecuritySafeCritical] // auto-generated
309 [System.Runtime.InteropServices.ComVisible(false)]
310 public void ImportCspBlob (byte[] keyBlob) {
311 Utils.ImportCspBlobHelper(CspAlgorithmType.Rsa, keyBlob, IsPublic(keyBlob), ref _parameters, _randomKeyContainer, ref _safeProvHandle, ref _safeKeyHandle);
314 public byte[] SignData(Stream inputStream, Object halg) {
315 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
316 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
317 byte[] hashVal = hash.ComputeHash(inputStream);
318 return SignHash(hashVal, calgHash);
321 public byte[] SignData(byte[] buffer, Object halg) {
322 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
323 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
324 byte[] hashVal = hash.ComputeHash(buffer);
325 return SignHash(hashVal, calgHash);
328 public byte[] SignData(byte[] buffer, int offset, int count, Object halg) {
329 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
330 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
331 byte[] hashVal = hash.ComputeHash(buffer, offset, count);
332 return SignHash(hashVal, calgHash);
335 public bool VerifyData(byte[] buffer, Object halg, byte[] signature) {
336 int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
337 HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
338 byte[] hashVal = hash.ComputeHash(buffer);
339 return VerifyHash(hashVal, calgHash, signature);
342 public byte[] SignHash(byte[] rgbHash, string str) {
344 throw new ArgumentNullException("rgbHash");
345 Contract.EndContractBlock();
347 throw new CryptographicException(Environment.GetResourceString("Cryptography_CSP_NoPrivateKey"));
349 int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
350 return SignHash(rgbHash, calgHash);
353 [SecuritySafeCritical]
354 internal byte[] SignHash(byte[] rgbHash, int calgHash) {
355 Contract.Requires(rgbHash != null);
358 if (!CspKeyContainerInfo.RandomlyGenerated) {
359 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
360 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
361 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Sign);
362 kp.AccessEntries.Add(entry);
366 return Utils.SignValue(_safeKeyHandle, _parameters.KeyNumber, Constants.CALG_RSA_SIGN, calgHash, rgbHash);
369 public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) {
371 throw new ArgumentNullException("rgbHash");
372 if (rgbSignature == null)
373 throw new ArgumentNullException("rgbSignature");
374 Contract.EndContractBlock();
376 int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
377 return VerifyHash(rgbHash, calgHash, rgbSignature);
380 [SecuritySafeCritical]
381 internal bool VerifyHash(byte[] rgbHash, int calgHash, byte[] rgbSignature) {
382 Contract.Requires(rgbHash != null);
383 Contract.Requires(rgbSignature != null);
387 return Utils.VerifySign(_safeKeyHandle, Constants.CALG_RSA_SIGN, calgHash, rgbHash, rgbSignature);
391 /// Encrypt raw data, generally used for encrypting symmetric key material.
394 /// This method can only encrypt (keySize - 88 bits) of data, so should not be used for encrypting
395 /// arbitrary byte arrays. Instead, encrypt a symmetric key with this method, and use the symmetric
396 /// key to encrypt the sensitive data.
398 /// <param name="rgb">raw data to encryt</param>
399 /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
400 /// <returns>Encrypted key</returns>
401 [System.Security.SecuritySafeCritical] // auto-generated
402 public byte[] Encrypt(byte[] rgb, bool fOAEP) {
404 throw new ArgumentNullException("rgb");
405 Contract.EndContractBlock();
409 byte[] encryptedKey = null;
410 EncryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref encryptedKey));
415 /// Decrypt raw data, generally used for decrypting symmetric key material
417 /// <param name="rgb">encrypted data</param>
418 /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
419 /// <returns>decrypted data</returns>
420 [System.Security.SecuritySafeCritical] // auto-generated
421 public byte [] Decrypt(byte[] rgb, bool fOAEP) {
423 throw new ArgumentNullException("rgb");
424 Contract.EndContractBlock();
428 // size check -- must be at most the modulus size
429 if (rgb.Length > (KeySize / 8))
430 throw new CryptographicException(Environment.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize / 8));
432 if (!CspKeyContainerInfo.RandomlyGenerated) {
433 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
434 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
435 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Decrypt);
436 kp.AccessEntries.Add(entry);
441 byte[] decryptedKey = null;
442 DecryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref decryptedKey));
446 public override byte[] DecryptValue(byte[] rgb) {
447 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
450 public override byte[] EncryptValue(byte[] rgb) {
451 throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
455 // private static methods
458 private static RSAParameters RSAObjectToStruct (RSACspObject rsaCspObject) {
459 RSAParameters rsaParams = new RSAParameters();
460 rsaParams.Exponent = rsaCspObject.Exponent;
461 rsaParams.Modulus = rsaCspObject.Modulus;
462 rsaParams.P = rsaCspObject.P;
463 rsaParams.Q = rsaCspObject.Q;
464 rsaParams.DP = rsaCspObject.DP;
465 rsaParams.DQ = rsaCspObject.DQ;
466 rsaParams.InverseQ = rsaCspObject.InverseQ;
467 rsaParams.D = rsaCspObject.D;
471 private static RSACspObject RSAStructToObject (RSAParameters rsaParams) {
472 RSACspObject rsaCspObject = new RSACspObject();
473 rsaCspObject.Exponent = rsaParams.Exponent;
474 rsaCspObject.Modulus = rsaParams.Modulus;
475 rsaCspObject.P = rsaParams.P;
476 rsaCspObject.Q = rsaParams.Q;
477 rsaCspObject.DP = rsaParams.DP;
478 rsaCspObject.DQ = rsaParams.DQ;
479 rsaCspObject.InverseQ = rsaParams.InverseQ;
480 rsaCspObject.D = rsaParams.D;
484 // find whether an RSA key blob is public.
485 private static bool IsPublic (byte[] keyBlob) {
487 throw new ArgumentNullException("keyBlob");
488 Contract.EndContractBlock();
490 // The CAPI RSA public key representation consists of the following sequence:
494 // The first should be PUBLICKEYBLOB and magic should be RSA_PUB_MAGIC "RSA1"
495 if (keyBlob[0] != Constants.PUBLICKEYBLOB)
498 if (keyBlob[11] != 0x31 || keyBlob[10] != 0x41 || keyBlob[9] != 0x53 || keyBlob[8] != 0x52)
504 // Since P is required, we will assume its presence is synonymous to a private key.
505 private static bool IsPublic(RSAParameters rsaParams) {
506 return (rsaParams.P == null);
510 // Adapt new RSA abstraction to legacy RSACryptoServiceProvider surface area.
513 // NOTE: For the new API, we go straight to CAPI for fixed set of hash algorithms and don't use crypto config here.
516 // 1. We're moving away from crypto config and we won't have it when porting to .NET Core
518 // 2. It's slow to lookup and slow to use as the base HashAlgorithm adds considerable overhead
519 // (redundant defensive copy + double-initialization for the single-use case).
522 [SecuritySafeCritical]
523 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
524 // we're sealed and the base should have checked this already
525 Contract.Assert(data != null);
526 Contract.Assert(offset >= 0 && offset <= data.Length);
527 Contract.Assert(count >= 0 && count <= data.Length);
528 Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
531 throw new NotImplementedException ();
533 using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
534 Utils.HashData(hashHandle, data, offset, count);
535 return Utils.EndHash(hashHandle);
540 [SecuritySafeCritical]
541 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
542 // we're sealed and the base should have checked this already
543 Contract.Assert(data != null);
544 Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
547 throw new NotImplementedException ();
549 using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
550 // Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm
551 byte[] buffer = new byte[4096];
554 bytesRead = data.Read(buffer, 0, buffer.Length);
556 Utils.HashData(hashHandle, buffer, 0, bytesRead);
558 } while (bytesRead > 0);
560 return Utils.EndHash(hashHandle);
565 private static int GetAlgorithmId(HashAlgorithmName hashAlgorithm) {
566 switch (hashAlgorithm.Name) {
568 return Constants.CALG_MD5;
570 return Constants.CALG_SHA1;
572 return Constants.CALG_SHA_256;
574 return Constants.CALG_SHA_384;
576 return Constants.CALG_SHA_512;
578 throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name));
582 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
584 throw new ArgumentNullException("data");
586 if (padding == null) {
587 throw new ArgumentNullException("padding");
590 if (padding == RSAEncryptionPadding.Pkcs1) {
591 return Encrypt(data, fOAEP: false);
592 } else if (padding == RSAEncryptionPadding.OaepSHA1) {
593 return Encrypt(data, fOAEP: true);
595 throw PaddingModeNotSupported();
599 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
601 throw new ArgumentNullException("data");
603 if (padding == null) {
604 throw new ArgumentNullException("padding");
607 if (padding == RSAEncryptionPadding.Pkcs1) {
608 return Decrypt(data, fOAEP: false);
609 } else if (padding == RSAEncryptionPadding.OaepSHA1) {
610 return Decrypt(data, fOAEP: true);
612 throw PaddingModeNotSupported();
616 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
618 throw new ArgumentNullException("hash");
620 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
621 throw HashAlgorithmNameNullOrEmpty();
623 if (padding == null) {
624 throw new ArgumentNullException("padding");
626 if (padding != RSASignaturePadding.Pkcs1) {
627 throw PaddingModeNotSupported();
630 return SignHash(hash, GetAlgorithmId(hashAlgorithm));
633 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
635 throw new ArgumentNullException("hash");
637 if (signature == null) {
638 throw new ArgumentNullException("signature");
640 if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
641 throw HashAlgorithmNameNullOrEmpty();
643 if (padding == null) {
644 throw new ArgumentNullException("padding");
646 if (padding != RSASignaturePadding.Pkcs1) {
647 throw PaddingModeNotSupported();
650 return VerifyHash(hash, GetAlgorithmId(hashAlgorithm), signature);
653 private static Exception PaddingModeNotSupported() {
654 return new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode"));