// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 

//
// 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);
        }

        /// <summary>
        ///     Encrypt raw data, generally used for encrypting symmetric key material.
        /// </summary>
        /// <remarks>
        ///     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.
        /// </remarks>
        /// <param name="rgb">raw data to encryt</param>
        /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
        /// <returns>Encrypted key</returns>
        [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;
        }

        /// <summary>
        ///     Decrypt raw data, generally used for decrypting symmetric key material
        /// </summary>
        /// <param name="rgb">encrypted data</param>
        /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
        /// <returns>decrypted data</returns>
        [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
            var hash = HashAlgorithm.Create (hashAlgorithm.Name);
            return hash.ComputeHash (data, offset, count);
#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
            var hash = HashAlgorithm.Create (hashAlgorithm.Name);
            return hash.ComputeHash (data);
#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"));
        }
    }
}
