// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // [....] // // // RSACryptoServiceProvider.cs // // CSP-based implementation of RSA // namespace System.Security.Cryptography { using System; using System.Globalization; using System.IO; using System.Security; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography.X509Certificates; using System.Security.Permissions; using System.Diagnostics.Contracts; #if !MONO // Object layout of the RSAParameters structure internal class RSACspObject { internal byte[] Exponent; internal byte[] Modulus; internal byte[] P; internal byte[] Q; internal byte[] DP; internal byte[] DQ; internal byte[] InverseQ; internal byte[] D; } #endif [System.Runtime.InteropServices.ComVisible(true)] public sealed partial class RSACryptoServiceProvider : RSA , ICspAsymmetricAlgorithm { #if !MONO private int _dwKeySize; private CspParameters _parameters; private bool _randomKeyContainer; [System.Security.SecurityCritical] // auto-generated private SafeProvHandle _safeProvHandle; [System.Security.SecurityCritical] // auto-generated private SafeKeyHandle _safeKeyHandle; #endif private static volatile CspProviderFlags s_UseMachineKeyStore = 0; // // QCalls // #if !MONO [System.Security.SecurityCritical] // auto-generated [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [ResourceExposure(ResourceScope.None)] [SuppressUnmanagedCodeSecurity] private static extern void DecryptKey(SafeKeyHandle pKeyContext, [MarshalAs(UnmanagedType.LPArray)] byte[] pbEncryptedKey, int cbEncryptedKey, [MarshalAs(UnmanagedType.Bool)] bool fOAEP, ObjectHandleOnStack ohRetDecryptedKey); [System.Security.SecurityCritical] // auto-generated [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [ResourceExposure(ResourceScope.None)] [SuppressUnmanagedCodeSecurity] private static extern void EncryptKey(SafeKeyHandle pKeyContext, [MarshalAs(UnmanagedType.LPArray)] byte[] pbKey, int cbKey, [MarshalAs(UnmanagedType.Bool)] bool fOAEP, ObjectHandleOnStack ohRetEncryptedKey); // // public constructors // [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] public RSACryptoServiceProvider() : this(0, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), true) { } [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] public RSACryptoServiceProvider(int dwKeySize) : this(dwKeySize, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), false) { } [System.Security.SecuritySafeCritical] // auto-generated public RSACryptoServiceProvider(CspParameters parameters) : this(0, parameters, true) { } [System.Security.SecuritySafeCritical] // auto-generated public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters) : this(dwKeySize, parameters, false) { } // // private methods // [System.Security.SecurityCritical] // auto-generated private RSACryptoServiceProvider(int dwKeySize, CspParameters parameters, bool useDefaultKeySize) { if (dwKeySize < 0) throw new ArgumentOutOfRangeException("dwKeySize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); Contract.EndContractBlock(); _parameters = Utils.SaveCspParameters(CspAlgorithmType.Rsa, parameters, s_UseMachineKeyStore, ref _randomKeyContainer); LegalKeySizesValue = new KeySizes[] { new KeySizes(384, 16384, 8) }; _dwKeySize = useDefaultKeySize ? 1024 : dwKeySize; // If this is not a random container we generate, create it eagerly // in the constructor so we can report any errors now. if (!_randomKeyContainer #if !FEATURE_CORECLR || Environment.GetCompatibilityFlag(CompatibilityFlag.EagerlyGenerateRandomAsymmKeys) #endif //!FEATURE_CORECLR ) GetKeyPair(); } [System.Security.SecurityCritical] // auto-generated private void GetKeyPair () { if (_safeKeyHandle == null) { lock (this) { if (_safeKeyHandle == null) { // We only attempt to generate a random key on desktop runtimes because the CoreCLR // RSA surface area is limited to simply verifying signatures. Since generating a // random key to verify signatures will always lead to failure (unless we happend to // win the lottery and randomly generate the signing key ...), there is no need // to add this functionality to CoreCLR at this point. Utils.GetKeyPairHelper(CspAlgorithmType.Rsa, _parameters, _randomKeyContainer, _dwKeySize, ref _safeProvHandle, ref _safeKeyHandle); } } } } [System.Security.SecuritySafeCritical] // overrides public transparent member protected override void Dispose(bool disposing) { base.Dispose(disposing); if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) _safeKeyHandle.Dispose(); if (_safeProvHandle != null && !_safeProvHandle.IsClosed) _safeProvHandle.Dispose(); } // // public properties // [System.Runtime.InteropServices.ComVisible(false)] public bool PublicOnly { [System.Security.SecuritySafeCritical] // auto-generated get { GetKeyPair(); byte[] publicKey = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_PUBLICKEYONLY); return (publicKey[0] == 1); } } [System.Runtime.InteropServices.ComVisible(false)] public CspKeyContainerInfo CspKeyContainerInfo { [System.Security.SecuritySafeCritical] // auto-generated get { GetKeyPair(); return new CspKeyContainerInfo(_parameters, _randomKeyContainer); } } public override int KeySize { [System.Security.SecuritySafeCritical] // auto-generated get { GetKeyPair(); byte[] keySize = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_KEYLEN); _dwKeySize = (keySize[0] | (keySize[1] << 8) | (keySize[2] << 16) | (keySize[3] << 24)); return _dwKeySize; } } public override string KeyExchangeAlgorithm { get { if (_parameters.KeyNumber == Constants.AT_KEYEXCHANGE) return "RSA-PKCS1-KeyEx"; return null; } } #endif public override string SignatureAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; } } public static bool UseMachineKeyStore { get { return (s_UseMachineKeyStore == CspProviderFlags.UseMachineKeyStore); } set { s_UseMachineKeyStore = (value ? CspProviderFlags.UseMachineKeyStore : 0); } } #if !MONO public bool PersistKeyInCsp { [System.Security.SecuritySafeCritical] // auto-generated get { if (_safeProvHandle == null) { lock (this) { if (_safeProvHandle == null) _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer); } } return Utils.GetPersistKeyInCsp(_safeProvHandle); } [System.Security.SecuritySafeCritical] // auto-generated set { bool oldPersistKeyInCsp = this.PersistKeyInCsp; if (value == oldPersistKeyInCsp) return; if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); if (!value) { KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Delete); kp.AccessEntries.Add(entry); } else { KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Create); kp.AccessEntries.Add(entry); } kp.Demand(); } Utils.SetPersistKeyInCsp(_safeProvHandle, value); } } // // public methods // [System.Security.SecuritySafeCritical] // auto-generated public override RSAParameters ExportParameters (bool includePrivateParameters) { GetKeyPair(); if (includePrivateParameters) { if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Export); kp.AccessEntries.Add(entry); kp.Demand(); } } RSACspObject rsaCspObject = new RSACspObject(); int blobType = includePrivateParameters ? Constants.PRIVATEKEYBLOB : Constants.PUBLICKEYBLOB; // _ExportKey will check for failures and throw an exception Utils._ExportKey(_safeKeyHandle, blobType, rsaCspObject); return RSAObjectToStruct(rsaCspObject); } #if FEATURE_LEGACYNETCFCRYPTO [System.Security.SecurityCritical] #else [System.Security.SecuritySafeCritical] // auto-generated #endif [System.Runtime.InteropServices.ComVisible(false)] public byte[] ExportCspBlob (bool includePrivateParameters) { GetKeyPair(); return Utils.ExportCspBlobHelper(includePrivateParameters, _parameters, _safeKeyHandle); } [System.Security.SecuritySafeCritical] // auto-generated public override void ImportParameters(RSAParameters parameters) { // Free the current key handle if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) { _safeKeyHandle.Dispose(); _safeKeyHandle = null; } RSACspObject rsaCspObject = RSAStructToObject(parameters); _safeKeyHandle = SafeKeyHandle.InvalidHandle; if (IsPublic(parameters)) { // Use our CRYPT_VERIFYCONTEXT handle, CRYPT_EXPORTABLE is not applicable to public only keys, so pass false Utils._ImportKey(Utils.StaticProvHandle, Constants.CALG_RSA_KEYX, (CspProviderFlags) 0, rsaCspObject, ref _safeKeyHandle); } else { if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Import); kp.AccessEntries.Add(entry); kp.Demand(); } if (_safeProvHandle == null) _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer); // Now, import the key into the CSP; _ImportKey will check for failures. Utils._ImportKey(_safeProvHandle, Constants.CALG_RSA_KEYX, _parameters.Flags, rsaCspObject, ref _safeKeyHandle); } } #if FEATURE_LEGACYNETCFCRYPTO [System.Security.SecurityCritical] #else [System.Security.SecuritySafeCritical] // auto-generated #endif [System.Runtime.InteropServices.ComVisible(false)] public void ImportCspBlob (byte[] keyBlob) { Utils.ImportCspBlobHelper(CspAlgorithmType.Rsa, keyBlob, IsPublic(keyBlob), ref _parameters, _randomKeyContainer, ref _safeProvHandle, ref _safeKeyHandle); } public byte[] SignData(Stream inputStream, Object halg) { int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm); HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg); byte[] hashVal = hash.ComputeHash(inputStream); return SignHash(hashVal, calgHash); } public byte[] SignData(byte[] buffer, Object halg) { int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm); HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg); byte[] hashVal = hash.ComputeHash(buffer); return SignHash(hashVal, calgHash); } public byte[] SignData(byte[] buffer, int offset, int count, Object halg) { int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm); HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg); byte[] hashVal = hash.ComputeHash(buffer, offset, count); return SignHash(hashVal, calgHash); } public bool VerifyData(byte[] buffer, Object halg, byte[] signature) { int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm); HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg); byte[] hashVal = hash.ComputeHash(buffer); return VerifyHash(hashVal, calgHash, signature); } public byte[] SignHash(byte[] rgbHash, string str) { if (rgbHash == null) throw new ArgumentNullException("rgbHash"); Contract.EndContractBlock(); if (PublicOnly) throw new CryptographicException(Environment.GetResourceString("Cryptography_CSP_NoPrivateKey")); int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm); return SignHash(rgbHash, calgHash); } [SecuritySafeCritical] internal byte[] SignHash(byte[] rgbHash, int calgHash) { Contract.Requires(rgbHash != null); GetKeyPair(); if (!CspKeyContainerInfo.RandomlyGenerated) { if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Sign); kp.AccessEntries.Add(entry); kp.Demand(); } } return Utils.SignValue(_safeKeyHandle, _parameters.KeyNumber, Constants.CALG_RSA_SIGN, calgHash, rgbHash); } public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) { if (rgbHash == null) throw new ArgumentNullException("rgbHash"); if (rgbSignature == null) throw new ArgumentNullException("rgbSignature"); Contract.EndContractBlock(); int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm); return VerifyHash(rgbHash, calgHash, rgbSignature); } [SecuritySafeCritical] internal bool VerifyHash(byte[] rgbHash, int calgHash, byte[] rgbSignature) { Contract.Requires(rgbHash != null); Contract.Requires(rgbSignature != null); GetKeyPair(); return Utils.VerifySign(_safeKeyHandle, Constants.CALG_RSA_SIGN, calgHash, rgbHash, rgbSignature); } /// /// Encrypt raw data, generally used for encrypting symmetric key material. /// /// /// This method can only encrypt (keySize - 88 bits) of data, so should not be used for encrypting /// arbitrary byte arrays. Instead, encrypt a symmetric key with this method, and use the symmetric /// key to encrypt the sensitive data. /// /// raw data to encryt /// true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding /// Encrypted key [System.Security.SecuritySafeCritical] // auto-generated public byte[] Encrypt(byte[] rgb, bool fOAEP) { if (rgb == null) throw new ArgumentNullException("rgb"); Contract.EndContractBlock(); GetKeyPair(); byte[] encryptedKey = null; EncryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref encryptedKey)); return encryptedKey; } /// /// Decrypt raw data, generally used for decrypting symmetric key material /// /// encrypted data /// true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding /// decrypted data [System.Security.SecuritySafeCritical] // auto-generated public byte [] Decrypt(byte[] rgb, bool fOAEP) { if (rgb == null) throw new ArgumentNullException("rgb"); Contract.EndContractBlock(); GetKeyPair(); // size check -- must be at most the modulus size if (rgb.Length > (KeySize / 8)) throw new CryptographicException(Environment.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize / 8)); if (!CspKeyContainerInfo.RandomlyGenerated) { if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags); KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Decrypt); kp.AccessEntries.Add(entry); kp.Demand(); } } byte[] decryptedKey = null; DecryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref decryptedKey)); return decryptedKey; } public override byte[] DecryptValue(byte[] rgb) { throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method")); } public override byte[] EncryptValue(byte[] rgb) { throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method")); } // // private static methods // private static RSAParameters RSAObjectToStruct (RSACspObject rsaCspObject) { RSAParameters rsaParams = new RSAParameters(); rsaParams.Exponent = rsaCspObject.Exponent; rsaParams.Modulus = rsaCspObject.Modulus; rsaParams.P = rsaCspObject.P; rsaParams.Q = rsaCspObject.Q; rsaParams.DP = rsaCspObject.DP; rsaParams.DQ = rsaCspObject.DQ; rsaParams.InverseQ = rsaCspObject.InverseQ; rsaParams.D = rsaCspObject.D; return rsaParams; } private static RSACspObject RSAStructToObject (RSAParameters rsaParams) { RSACspObject rsaCspObject = new RSACspObject(); rsaCspObject.Exponent = rsaParams.Exponent; rsaCspObject.Modulus = rsaParams.Modulus; rsaCspObject.P = rsaParams.P; rsaCspObject.Q = rsaParams.Q; rsaCspObject.DP = rsaParams.DP; rsaCspObject.DQ = rsaParams.DQ; rsaCspObject.InverseQ = rsaParams.InverseQ; rsaCspObject.D = rsaParams.D; return rsaCspObject; } // find whether an RSA key blob is public. private static bool IsPublic (byte[] keyBlob) { if (keyBlob == null) throw new ArgumentNullException("keyBlob"); Contract.EndContractBlock(); // The CAPI RSA public key representation consists of the following sequence: // - BLOBHEADER // - RSAPUBKEY // The first should be PUBLICKEYBLOB and magic should be RSA_PUB_MAGIC "RSA1" if (keyBlob[0] != Constants.PUBLICKEYBLOB) return false; if (keyBlob[11] != 0x31 || keyBlob[10] != 0x41 || keyBlob[9] != 0x53 || keyBlob[8] != 0x52) return false; return true; } // Since P is required, we will assume its presence is synonymous to a private key. private static bool IsPublic(RSAParameters rsaParams) { return (rsaParams.P == null); } #endif // // Adapt new RSA abstraction to legacy RSACryptoServiceProvider surface area. // // NOTE: For the new API, we go straight to CAPI for fixed set of hash algorithms and don't use crypto config here. // // Reasons: // 1. We're moving away from crypto config and we won't have it when porting to .NET Core // // 2. It's slow to lookup and slow to use as the base HashAlgorithm adds considerable overhead // (redundant defensive copy + double-initialization for the single-use case). // [SecuritySafeCritical] protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) { // we're sealed and the base should have checked this already Contract.Assert(data != null); Contract.Assert(offset >= 0 && offset <= data.Length); Contract.Assert(count >= 0 && count <= data.Length); Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name)); #if MONO throw new NotImplementedException (); #else using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) { Utils.HashData(hashHandle, data, offset, count); return Utils.EndHash(hashHandle); } #endif } [SecuritySafeCritical] protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) { // we're sealed and the base should have checked this already Contract.Assert(data != null); Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name)); #if MONO throw new NotImplementedException (); #else using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) { // Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm byte[] buffer = new byte[4096]; int bytesRead = 0; do { bytesRead = data.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { Utils.HashData(hashHandle, buffer, 0, bytesRead); } } while (bytesRead > 0); return Utils.EndHash(hashHandle); } #endif } private static int GetAlgorithmId(HashAlgorithmName hashAlgorithm) { switch (hashAlgorithm.Name) { case "MD5": return Constants.CALG_MD5; case "SHA1": return Constants.CALG_SHA1; case "SHA256": return Constants.CALG_SHA_256; case "SHA384": return Constants.CALG_SHA_384; case "SHA512": return Constants.CALG_SHA_512; default: throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name)); } } public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) { if (data == null) { throw new ArgumentNullException("data"); } if (padding == null) { throw new ArgumentNullException("padding"); } if (padding == RSAEncryptionPadding.Pkcs1) { return Encrypt(data, fOAEP: false); } else if (padding == RSAEncryptionPadding.OaepSHA1) { return Encrypt(data, fOAEP: true); } else { throw PaddingModeNotSupported(); } } public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { if (data == null) { throw new ArgumentNullException("data"); } if (padding == null) { throw new ArgumentNullException("padding"); } if (padding == RSAEncryptionPadding.Pkcs1) { return Decrypt(data, fOAEP: false); } else if (padding == RSAEncryptionPadding.OaepSHA1) { return Decrypt(data, fOAEP: true); } else { throw PaddingModeNotSupported(); } } public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) { if (hash == null) { throw new ArgumentNullException("hash"); } if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } if (padding == null) { throw new ArgumentNullException("padding"); } if (padding != RSASignaturePadding.Pkcs1) { throw PaddingModeNotSupported(); } return SignHash(hash, GetAlgorithmId(hashAlgorithm)); } public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) { if (hash == null) { throw new ArgumentNullException("hash"); } if (signature == null) { throw new ArgumentNullException("signature"); } if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } if (padding == null) { throw new ArgumentNullException("padding"); } if (padding != RSASignaturePadding.Pkcs1) { throw PaddingModeNotSupported(); } return VerifyHash(hash, GetAlgorithmId(hashAlgorithm), signature); } private static Exception PaddingModeNotSupported() { return new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode")); } } }