Can RSACryptoServiceProvider (.NET's RSA) use

2019-01-23 06:39发布

问题:

When encrypting, can RSACryptoServiceProvider (or any other RSA encryptor available from .NET) use SHA256 instead of SHA1?

SHA1 appears to be hard coded with no way to change it. For example, RSACryptoServiceProvider.SignatureAlgorithm is hard coded to return "http://www.w3.org/2000/09/xmldsig#rsa-sha1".

If there is no way to make RSACryptoServiceProvider use SHA256, what are the alternatives?


Update

The following code works perfectly, but I'd like to change the OAEPWithSHA1AndMGF1Padding to OAEPWithSHA256AndMGF1Padding. What is required on the C# side to be able to encrypt using SHA256 rather than SHA1?

The encryption is done in C# using:

var parameters = new RSAParameters();
parameters.Exponent = new byte[] {0x01, 0x00, 0x01};
parameters.Modulus = new byte[] {0x9d, 0xc1, 0xcc, ...};
rsa.ImportParameters(parameters);

var cipherText = rsa.Encrypt(new byte[] { 0, 1, 2, 3 }, true);

The decryption is done in Java using:

Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPWithSHA1AndMGF1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] cipherText = ...;
byte[] plainText = cipher.doFinal(cipherText);

回答1:

RSACryptoServiceProvider does work with SHA2-based signatures, but you have to invest some effort into it.

When you use a certificate to get your RSACryptoServiceProvider it really matters what's the underlying CryptoAPI provider. By default, when you create a certificate with 'makecert', it's "RSA-FULL" which only supports SHA1 hashes for signature. You need the new "RSA-AES" one that supports SHA2.

So, you can create your certificate with an additional option: -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" (or an equivalent -sy 24) and then your code would look like (in .NET 4.0):

var rsa = signerCertificate.PrivateKey as RSACryptoServiceProvider;
//
byte[] signature = rsa.SignData(data, CryptoConfig.CreateFromName("SHA256"));

If you are unable to change the way your certificate is issued, there is a semi-ligitimate workaround that is based on the fact that by default RSACryptoServiceProvider is created with support for SHA2. So, the following code would also work, but it is a bit uglier: (what this code does is it creates a new RSACryptoServiceProvider and imports the keys from the one we got from the certificate)

var rsa = signerCertificate.PrivateKey as RSACryptoServiceProvider;
// Create a new RSACryptoServiceProvider
RSACryptoServiceProvider rsaClear = new RSACryptoServiceProvider();
// Export RSA parameters from 'rsa' and import them into 'rsaClear'
rsaClear.ImportParameters(rsa.ExportParameters(true));
byte[] signature = rsaClear.SignData(data, CryptoConfig.CreateFromName("SHA256"));


回答2:

As of .NET 3.5 SP1 on any Windows Server 2003 and higher OS, yes, the RSACryptoServiceProvider does support RSA-SHA256 for signing, but not encrypting.

From the blog post Using RSACryptoServiceProvider for RSA-SHA256 signatures:

byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
    byte[] signature = rsa.SignData(data, "SHA256");

    if (rsa.VerifyData(data, "SHA256", signature))
    {
        Console.WriteLine("RSA-SHA256 signature verified");
    }
    else
    {
        Console.WriteLine("RSA-SHA256 signature failed to verify");
    }
}

You should read the original post though, as there are some gotcha's to be aware of.



回答3:

If there is no way to have RSACryptoServiceProvider handle OAEP-with-SHA-256 (answers from other seem to tell so), then you can still implement the operation yourself. We are talking about the encryption part, which uses the public key only. The public key is, well, public, which means that you can export it (actually, in your code, you already have the modulus and exponent as array of bytes), and there are no gotchas about secret data leakage through a careless implementation, since there is no secret key here.

Implementing OAEP entails the following:

  • Follow PKCS#1, section 7.1. This transforms the data to encrypt into a sequence of bytes of the same length than the RSA modulus. You will need a SHA-256 implementation (System.Security.Cryptography.SHA256Managed will be fine) and a source of cryptographic-quality alea (System.Security.Cryptography.RandomNumberGenerator).
  • Decode the resulting sequence into a big integer, perform a modular exponentiation (modulo n, the RSA modulus), and encode the result into another sequence of bytes of the same length than the modulus. Encoding rules are big-endian, no sign bit, and a fixed size (if this is a 1024-bit RSA key, meaning that 21023 <= n < 21024, then the encrypted message will always have length exactly 128 bytes, even if the numerical value would have fitted in less, e.g. 127 or 126 bytes).

.NET 4.0 and onwards provides System.Numerics.BigInteger which has the code you need (method ModPow()). For previous versions, you would have to use a custom implementation; there are several lying around, Google being, as always, your friend. You do not need absolute performance here: RSA encryption is fast, because the public exponent is short (17 bits in your sample code).



回答4:

Bounty Castle C# was updated to 1.7 after this question was asked and answered. For future reference you might consider it, it adds supports a lot of crypto algorithms, hashes, signatures that Bouncy Castle makes available for Java. Hit the link, look for 'Release Notes for 1.7' and 'Current feature list:'.



回答5:

According to a Microsoft MVP (Rob Teixeira), no. You could use a third-part library such as Security.Cryptography.dll



回答6:

Just for reference: How to change the CSP within a .p12 or .pfx (certificate with private key). You need the password for the private key within the .pfx in order to do the following steps.

Step 1: Convert the file into open format temp.pem

openssl pkcs12 -in myCert.p12 -out temp.pem -passin pass:myPassword -passout pass:temppwd

or openssl pkcs12 -in myCert.pfx -out temp.pem -passin pass:myPassword -passout pass:temppwd

Step 2: Create file myCert2.pfx containing the CSP reference needed for Windows

openssl pkcs12 -export -in temp.pem -out myCert2.pfx -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -passin pass:temppwd -passout pass:myPassword

Step 3: Delete temp.pem. It's no longer needed.

del temp.pem

Step 4: Verify it is done correctly

openssl pkcs12 -info -nodes -in myCert2.pfx -passin pass:myPassword

This must show Microsoft CSP Name: Microsoft Enhanced RSA and AES Cryptographic Provider

With such a modified certificate you can use the 1st code in Kastorskijs answer.



回答7:

All of the other answers here are about SIGNING, not ENCRYPTING, with SHA256. I'm going to actually answer the question asked.

Any SHA-2 or higher algorithms are for hashing, not necessarily encrypting/decrypting, but that doesn't mean you can't generate keys using those algorithms, then encrypt/decrypt against them. Technically, I will caveat this for those that would dissent against this answer, this is not "encrypting with SHA256", but it does allow RSA to use a hashed key generated using that algorithm. Let your particular organization decide if that is enough to be NIST/FIPS-compliant, should that be your cause, as it was mine when researching this.

Encryption (using RSA, or another asymmetric encryption algorithm) simply requires a public key (for encrypting) and a private key (for decryption). Once you create keys using that hash, you can encrypt/decrypt against them.

I'm going to piece together some research I did to show a couple of routes you can go for how to get this going using a key created using a SHA-256 hash, that you then encrypt/decrypt against. You can generate a SHA-256 key either by creating a certificate or letting the RSACryptoServiceContainer give you one.

Certificate method

Create your certificate with these lines on a command line:

makecert -r -pe -n "CN=MyCertificate" -a sha256 -b 09/01/2016 -sky exchange C:\Temp\MyCertificate.cer -sv C:\Temp\MyCertificate.pvk
pvk2pfx.exe -pvk C:\Temp\MyCertificate.pvk -pi "MyP@ssw0rd" -spc C:\Temp\MyCertificate.cer -pfx C:\Temp\MyCertificate.pfx -po "MyP@ssw0rd"

Then import the certificate to the local root authority store and use this code:

string input = "test";
string output = string.Empty;

X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);

X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "MyCertificate", false);

X509Certificate2 certificate = collection[0];

using (RSACryptoServiceProvider cps = (RSACryptoServiceProvider)certificate.PublicKey.Key)
{
    byte[] bytesData = Encoding.UTF8.GetBytes(input);
    byte[] bytesEncrypted = cps.Encrypt(bytesData, false);
    output = Convert.ToBase64String(bytesEncrypted);
}

store.Close();

If you wanted to use SHA512, you would just change that sha256 parameter to sha512 when making the certificate.

Reference: https://social.msdn.microsoft.com/Forums/en-US/69e39ad0-13c2-4b5e-bb1b-972a614813fd/encrypt-with-certificate-sha512?forum=csharpgeneral

Using RSACryptoServiceProvider to generate the keys

private static string privateKey = String.Empty;

private static void generateKeys()
{
    int dwLen = 2048;
    RSACryptoServiceProvider csp = new RSACryptoServiceProvider(dwLen);
    privateKey = csp.ToXmlString(true).Replace("><",">\r\n");
}

public static string Encrypt(string data2Encrypt)
{
    try
    {
        generateKeys();
        RSAx rsax = new RSAx(privateKey, 2048);
        rsax.RSAxHashAlgorithm = RSAxParameters.RSAxHashAlgorithm.SHA256;
        byte[] CT = rsax.Encrypt(Encoding.UTF8.GetBytes(data2Encrypt), false, true); // first bool is for using private key (false forces to use public), 2nd is for using OAEP
        return Convert.ToBase64String(CT);
    }
    catch (Exception ex) 
    { 
        // handle exception
        MessageBox.Show("Error during encryption: " + ex.Message);
        return String.Empty;
    }
}

public static string Decrypt(string data2Decrypt)
{
    try
    {
        RSAx rsax = new RSAx(privateKey, 2048);
        rsax.RSAxHashAlgorithm = RSAxParameters.RSAxHashAlgorithm.SHA256;
        byte[] PT = rsax.Decrypt(Convert.FromBase64String(data2Decrypt), true, true); // first bool is for using private key, 2nd is for using OAEP
        return Encoding.UTF8.GetString(PT);
    }
    catch (Exception ex) 
    { 
        // handle exception
        MessageBox.Show("Error during encryption: " + ex.Message);
        return String.Empty;
    }
}

If you wanted to use SHA512, you could change RSAxHashAlgorithm.SHA256 to RSAxHashAlgorithm.SHA512.

These methods use a DLL called RSAx.DLL, built using the source code at https://www.codeproject.com/Articles/421656/RSA-Library-with-Private-Key-Encryption-in-Csharp , which is not mine (author: Arpan Jati), but I've used it and it is available to the developer community under CodeProject's Open Source License. You could also just bring in 3 classes from that project, instead: RSAx.cs, RSAxParameters.cs, RSAxUtils.cs

The code would take this post over the 30000 char limit, so I'll just post RSAx so you can see what's going on, but all 3 classes are required. You have to change the namespace and reference the System.Numerics assembly.

RSAx.cs

// @Date : 15th July 2012
// @Author : Arpan Jati (arpan4017@yahoo.com; arpan4017@gmail.com)
// @Library : ArpanTECH.RSAx
// @CodeProject: http://www.codeproject.com/Articles/421656/RSA-Library-with-Private-Key-Encryption-in-Csharp  

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Numerics;
using System.Linq;
using System.Text;
using System.IO;

namespace ArpanTECH
{
    /// <summary>
    /// The main RSAx Class
    /// </summary>
    public class RSAx : IDisposable
    {
        private RSAxParameters rsaParams;
        private RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

        /// <summary>
        /// Initialize the RSA class.
        /// </summary>
        /// <param name="rsaParams">Preallocated RSAxParameters containing the required keys.</param>
        public RSAx(RSAxParameters rsaParams)
        {
            this.rsaParams = rsaParams;
            UseCRTForPublicDecryption = true;
        }

        /// <summary>
        /// Initialize the RSA class from a XML KeyInfo string.
        /// </summary>
        /// <param name="keyInfo">XML Containing Key Information</param>
       /// <param name="ModulusSize">Length of RSA Modulus in bits.</param>
       public RSAx(String keyInfo, int ModulusSize)
       {
            this.rsaParams = RSAxUtils.GetRSAxParameters(keyInfo, ModulusSize);
            UseCRTForPublicDecryption = true;
        }

        /// <summary>
        /// Hash Algorithm to be used for OAEP encoding.
        /// </summary>
        public RSAxParameters.RSAxHashAlgorithm RSAxHashAlgorithm
        {
            set
            {
                rsaParams.HashAlgorithm = value;
            }
        }

        /// <summary>
        /// If True, and if the parameters are available, uses CRT for private key decryption. (Much Faster)
        /// </summary>
        public bool UseCRTForPublicDecryption
        {
            get;  set;
        }

        /// <summary>
        /// Releases all the resources.
        /// </summary>
        public void Dispose()
        {
            rsaParams.Dispose();
        }

        #region PRIVATE FUNCTIONS

        /// <summary>
        /// Low level RSA Process function for use with private key.
        /// Should never be used; Because without padding RSA is vulnerable to attacks.  Use with caution.
        /// </summary>
        /// <param name="PlainText">Data to encrypt. Length must be less than Modulus size in octets.</param>
        /// <param name="usePrivate">True to use Private key, else Public.</param>
        /// <returns>Encrypted Data</returns>
        public byte[] RSAProcess(byte[] PlainText, bool usePrivate)
        {

            if (usePrivate && (!rsaParams.Has_PRIVATE_Info))
            {
                throw new CryptographicException("RSA Process: Incomplete Private Key Info");
            }

            if ((usePrivate == false) && (!rsaParams.Has_PUBLIC_Info))
            {
                throw new CryptographicException("RSA Process: Incomplete Public Key Info");
            }            

            BigInteger _E;
            if (usePrivate)
                _E = rsaParams.D; 
            else
                _E = rsaParams.E;

            BigInteger PT = RSAxUtils.OS2IP(PlainText, false);
            BigInteger M = BigInteger.ModPow(PT, _E, rsaParams.N);

            if (M.Sign == -1)
                return RSAxUtils.I2OSP(M + rsaParams.N, rsaParams.OctetsInModulus, false);            
            else
                return RSAxUtils.I2OSP(M, rsaParams.OctetsInModulus, false);                   
        }

        /// <summary>
        /// Low level RSA Decryption function for use with private key. Uses CRT and is Much faster.
        /// Should never be used; Because without padding RSA is vulnerable to attacks. Use with caution.
        /// </summary>
        /// <param name="Data">Data to encrypt. Length must be less than Modulus size in octets.</param>
        /// <returns>Encrypted Data</returns>
        public byte[] RSADecryptPrivateCRT(byte[] Data)
        {
            if (rsaParams.Has_PRIVATE_Info && rsaParams.HasCRTInfo)
            {
                BigInteger C = RSAxUtils.OS2IP(Data, false);

                BigInteger M1 = BigInteger.ModPow(C, rsaParams.DP, rsaParams.P);
                BigInteger M2 = BigInteger.ModPow(C, rsaParams.DQ, rsaParams.Q);
                BigInteger H = ((M1 - M2) * rsaParams.InverseQ) % rsaParams.P;
                BigInteger M = (M2 + (rsaParams.Q * H));

                if (M.Sign == -1)
                    return RSAxUtils.I2OSP(M + rsaParams.N, rsaParams.OctetsInModulus, false);
                else
                    return RSAxUtils.I2OSP(M, rsaParams.OctetsInModulus, false); 
            }
            else
            {
                throw new CryptographicException("RSA Decrypt CRT: Incomplete Key Info");
            }                             
        }        

        private byte[] RSAProcessEncodePKCS(byte[] Message, bool usePrivate)
        {
            if (Message.Length > rsaParams.OctetsInModulus - 11)
            {
                throw new ArgumentException("Message too long.");
            }
            else
            {
                // RFC3447 : Page 24. [RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)]
                // EM = 0x00 || 0x02 || PS || 0x00 || Msg 

                List<byte> PCKSv15_Msg = new List<byte>();

                PCKSv15_Msg.Add(0x00);
                PCKSv15_Msg.Add(0x02);

                int PaddingLength = rsaParams.OctetsInModulus - Message.Length - 3;

                byte[] PS = new byte[PaddingLength];
                rng.GetNonZeroBytes(PS);

                PCKSv15_Msg.AddRange(PS);
                PCKSv15_Msg.Add(0x00);

                PCKSv15_Msg.AddRange(Message);

                return RSAProcess(PCKSv15_Msg.ToArray() ,  usePrivate);
            }
        }

        /// <summary>
        /// Mask Generation Function
        /// </summary>
        /// <param name="Z">Initial pseudorandom Seed.</param>
        /// <param name="l">Length of output required.</param>
        /// <returns></returns>
        private byte[] MGF(byte[] Z, int l)
        {
            if (l > (Math.Pow(2, 32)))
            {
                throw new ArgumentException("Mask too long.");
            }
            else
            {
                List<byte> result = new List<byte>();
                for (int i = 0; i <= l / rsaParams.hLen; i++)
                {
                    List<byte> data = new List<byte>();
                    data.AddRange(Z);
                    data.AddRange(RSAxUtils.I2OSP(i, 4, false));
                    result.AddRange(rsaParams.ComputeHash(data.ToArray()));
                }

                if (l <= result.Count)
                {
                    return result.GetRange(0, l).ToArray();
                }
                else
                {
                    throw new ArgumentException("Invalid Mask Length.");
                }
            }
        }


        private byte[] RSAProcessEncodeOAEP(byte[] M, byte[] P, bool usePrivate)
        {
            //                           +----------+---------+-------+
            //                      DB = |  lHash   |    PS   |   M   |
            //                           +----------+---------+-------+
            //                                          |
            //                +----------+              V
            //                |   seed   |--> MGF ---> XOR
            //                +----------+              |
            //                      |                   |
            //             +--+     V                   |
            //             |00|    XOR <----- MGF <-----|
            //             +--+     |                   |
            //               |      |                   |
            //               V      V                   V
            //             +--+----------+----------------------------+
            //       EM =  |00|maskedSeed|          maskedDB          |
            //             +--+----------+----------------------------+

            int mLen = M.Length;
            if (mLen > rsaParams.OctetsInModulus - 2 * rsaParams.hLen - 2)
            {
                throw new ArgumentException("Message too long.");
            }
            else
            {
                byte[] PS = new byte[rsaParams.OctetsInModulus - mLen - 2 * rsaParams.hLen - 2];
                //4. pHash = Hash(P),
                byte[] pHash = rsaParams.ComputeHash(P);

                //5. DB = pHash||PS||01||M.
                List<byte> _DB = new List<byte>();
                _DB.AddRange(pHash);
                _DB.AddRange(PS);
                _DB.Add(0x01);
                _DB.AddRange(M);
                byte[] DB = _DB.ToArray();

                //6. Generate a random octet string seed of length hLen.                
                byte[] seed = new byte[rsaParams.hLen];
                rng.GetBytes(seed);

                //7. dbMask = MGF(seed, k - hLen -1).
                byte[] dbMask = MGF(seed, rsaParams.OctetsInModulus - rsaParams.hLen - 1);

                //8. maskedDB = DB XOR dbMask
                byte[] maskedDB = RSAxUtils.XOR(DB, dbMask);

                //9. seedMask = MGF(maskedDB, hLen)
                byte[] seedMask = MGF(maskedDB, rsaParams.hLen);

                //10. maskedSeed = seed XOR seedMask.
                byte[] maskedSeed = RSAxUtils.XOR(seed, seedMask);

                //11. EM = 0x00 || maskedSeed || maskedDB.
                List<byte> result = new List<byte>();
                result.Add(0x00);
                result.AddRange(maskedSeed);
                result.AddRange(maskedDB);

                return RSAProcess(result.ToArray(), usePrivate);
            }
        }


        private byte[] Decrypt(byte[] Message, byte [] Parameters, bool usePrivate, bool fOAEP)
        {
            byte[] EM = new byte[0];
            try
            {
                if ((usePrivate == true) && (UseCRTForPublicDecryption) && (rsaParams.HasCRTInfo))
                {
                    EM = RSADecryptPrivateCRT(Message);
                }
                else
                {
                    EM = RSAProcess(Message, usePrivate);
                }
            }
            catch (CryptographicException ex)
            {
                throw new CryptographicException("Exception while Decryption: " + ex.Message);
            }
            catch
            {
                throw new Exception("Exception while Decryption: ");
            }

            try
            {
                if (fOAEP) //DECODE OAEP
                {
                    if ((EM.Length == rsaParams.OctetsInModulus) && (EM.Length > (2 * rsaParams.hLen + 1)))
                    {
                        byte[] maskedSeed;
                        byte[] maskedDB;
                        byte[] pHash = rsaParams.ComputeHash(Parameters);
                        if (EM[0] == 0) // RFC3447 Format : http://tools.ietf.org/html/rfc3447
                        {
                            maskedSeed = EM.ToList().GetRange(1, rsaParams.hLen).ToArray();
                            maskedDB = EM.ToList().GetRange(1 + rsaParams.hLen, EM.Length - rsaParams.hLen - 1).ToArray();
                            byte[] seedMask = MGF(maskedDB, rsaParams.hLen);
                            byte[] seed = RSAxUtils.XOR(maskedSeed, seedMask);
                            byte[] dbMask = MGF(seed, rsaParams.OctetsInModulus - rsaParams.hLen - 1);
                            byte[] DB = RSAxUtils.XOR(maskedDB, dbMask);

                            if (DB.Length >= (rsaParams.hLen + 1))
                            {
                                byte[] _pHash = DB.ToList().GetRange(0, rsaParams.hLen).ToArray();
                                List<byte> PS_M = DB.ToList().GetRange(rsaParams.hLen, DB.Length - rsaParams.hLen);
                                int pos = PS_M.IndexOf(0x01);
                                if (pos >= 0 && (pos < PS_M.Count))
                                {
                                    List<byte> _01_M = PS_M.GetRange(pos, PS_M.Count - pos);
                                    byte[] M;
                                    if (_01_M.Count > 1)
                                    {
                                        M = _01_M.GetRange(1, _01_M.Count - 1).ToArray();
                                    }
                                    else
                                    {
                                        M = new byte[0];
                                    }
                                    bool success = true;
                                    for (int i = 0; i < rsaParams.hLen; i++)
                                    {
                                         if (_pHash[i] != pHash[i])
                                        {
                                            success = false;
                                            break;
                                        }
                                    }

                                    if (success)
                                    {
                                        return M;
                                    }
                                    else
                                    {
                                        M = new byte[rsaParams.OctetsInModulus]; //Hash Match Failure.
                                        throw new CryptographicException("OAEP Decode Error");
                                    }
                                }
                                else
                                {// #3: Invalid Encoded Message Length.
                                    throw new CryptographicException("OAEP Decode Error");
                                }
                            }
                            else
                            {// #2: Invalid Encoded Message Length.
                                throw new CryptographicException("OAEP Decode Error");
                            }
                        }
                        else // Standard : ftp://ftp.rsasecurity.com/pub/rsalabs/rsa_algorithm/rsa-oaep_spec.pdf
                        {//OAEP : THIS STADNARD IS NOT IMPLEMENTED
                            throw new CryptographicException("OAEP Decode Error");
                        }
                    }
                    else
                    {// #1: Invalid Encoded Message Length.
                        throw new CryptographicException("OAEP Decode Error");
                    }
                }
                else // DECODE PKCS v1.5
                {
                    if (EM.Length >= 11)
                    {
                        if ((EM[0] == 0x00) && (EM[1] == 0x02))
                        {
                            int startIndex = 2;
                            List<byte> PS = new List<byte>();
                            for (int i = startIndex; i < EM.Length; i++)
                            {
                                if (EM[i] != 0)
                                {
                                    PS.Add(EM[i]);
                                }
                                else
                                {
                                    break;
                                }
                            }

                            if (PS.Count >= 8)
                            {
                                int DecodedDataIndex = startIndex + PS.Count + 1;
                                if (DecodedDataIndex < (EM.Length - 1))
                                {
                                    List<byte> DATA = new List<byte>();
                                    for (int i = DecodedDataIndex; i < EM.Length; i++)
                                    {
                                        DATA.Add(EM[i]);
                                    }
                                    return DATA.ToArray();
                                }
                                else
                                {
                                    return new byte[0];
                                    //throw new CryptographicException("PKCS v1.5 Decode Error #4: No Data");
                                }
                            }
                            else
                            {// #3: Invalid Key / Invalid Random Data Length
                                throw new CryptographicException("PKCS v1.5 Decode Error");
                            }
                        }
                        else
                        {// #2: Invalid Key / Invalid Identifiers
                            throw new CryptographicException("PKCS v1.5 Decode Error");
                        }
                    }
                    else
                    {// #1: Invalid Key / PKCS Encoding
                        throw new CryptographicException("PKCS v1.5 Decode Error");
                    }

                }
            }
            catch (CryptographicException ex)
            {
                throw new CryptographicException("Exception while decoding: " + ex.Message);
            }
            catch
            {
                throw new CryptographicException("Exception while decoding");
            }


        }

        #endregion

        #region PUBLIC FUNCTIONS

        /// <summary>
        /// Encrypts the given message with RSA, performs OAEP Encoding.
        /// </summary>
        /// <param name="Message">Message to Encrypt. Maximum message length is (ModulusLengthInOctets - 2 * HashLengthInOctets - 2)</param>
        /// <param name="OAEP_Params">Optional OAEP parameters. Normally Empty. But, must match the parameters while decryption.</param>
        /// <param name="usePrivate">True to use Private key for encryption. False to use Public key.</param>
        /// <returns>Encrypted message.</returns>
        public byte[] Encrypt(byte[] Message, byte[] OAEP_Params, bool usePrivate)
        {
            return RSAProcessEncodeOAEP(Message, OAEP_Params, usePrivate);
        }

        /// <summary>
        /// Encrypts the given message with RSA.
        /// </summary>
        /// <param name="Message">Message to Encrypt. Maximum message length is For OAEP [ModulusLengthInOctets - (2 * HashLengthInOctets) - 2] and for PKCS [ModulusLengthInOctets - 11]</param>
        /// <param name="usePrivate">True to use Private key for encryption. False to use Public key.</param>
        /// <param name="fOAEP">True to use OAEP encoding (Recommended), False to use PKCS v1.5 Padding.</param>
        /// <returns>Encrypted message.</returns>
        public byte[] Encrypt(byte[] Message, bool usePrivate, bool fOAEP)
        {
            if (fOAEP)
            {
                return RSAProcessEncodeOAEP(Message, new byte[0], usePrivate);
            }
            else
            {
                return RSAProcessEncodePKCS(Message, usePrivate);
            }
        }

        /// <summary>
        /// Encrypts the given message using RSA Public Key.
        /// </summary>
        /// <param name="Message">Message to Encrypt. Maximum message length is For OAEP [ModulusLengthInOctets - (2 * HashLengthInOctets) - 2] and for PKCS [ModulusLengthInOctets - 11]</param>
        /// <param name="fOAEP">True to use OAEP encoding (Recommended), False to use PKCS v1.5 Padding.</param>
        /// <returns>Encrypted message.</returns>
        public byte[] Encrypt(byte[] Message,  bool fOAEP)
        {
            if (fOAEP)
            {
                return RSAProcessEncodeOAEP(Message, new byte[0], false);
            }
            else
            {
                return RSAProcessEncodePKCS(Message, false);
            }
        }

        /// <summary>
        /// Decrypts the given RSA encrypted message.
        /// </summary>
        /// <param name="Message">The encrypted message.</param>
        /// <param name="usePrivate">True to use Private key for decryption. False to use Public key.</param>
        /// <param name="fOAEP">True to use OAEP.</param>
        /// <returns>Encrypted byte array.</returns>
        public byte[] Decrypt(byte[] Message, bool usePrivate, bool fOAEP)
        {
            return Decrypt(Message, new byte[0], usePrivate, fOAEP);
        }

        /// <summary>
        /// Decrypts the given RSA encrypted message.
        /// </summary>
        /// <param name="Message">The encrypted message.</param>
        /// <param name="OAEP_Params">Parameters to the OAEP algorithm (Must match the parameter while Encryption).</param>
        /// <param name="usePrivate">True to use Private key for decryption. False to use Public key.</param>
        /// <returns>Decrypted byte array.</returns>
        public byte[] Decrypt(byte[] Message, byte[] OAEP_Params, bool usePrivate)
        {
            return Decrypt(Message, OAEP_Params, usePrivate, true);
        }

        /// <summary>
        /// Decrypts the given RSA encrypted message using Private key.
        /// </summary>
        /// <param name="Message">The encrypted message.</param>
        /// <param name="fOAEP">True to use OAEP.</param>
        /// <returns>Decrypted byte array.</returns>
        public byte[] Decrypt(byte[] Message,  bool fOAEP)
        {
            return Decrypt(Message, new byte[0], true, fOAEP);
        }
        #endregion
    }
}