16

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);
Jonathan Wright
  • 14,880
  • 4
  • 24
  • 19
  • What is wrong with using SHA1 in the OAEP padding scheme? – President James K. Polk Feb 26 '11 at 01:37
  • 3
    I'd rather use SHA256 from the start than be forced to urgently upgrade to it in a year or two when some customer is required to comply with tighter rules. SHA1 is looking weaker and weaker, with the recommendation being to use SHA256. – Jonathan Wright Feb 26 '11 at 23:12
  • @JamesKPolk, I'm from future! and guess what, both MD4, MD5 and SHA1 are all obsolete and aren't considered secure anymore. I'm sure same will happen to SHA256 in future, JK. – AaA Aug 17 '17 at 06:50

6 Answers6

13

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"));
Vladik Branevich
  • 1,180
  • 8
  • 11
  • 3
    This looks like it would be for signing it, not encrypting it. – vapcguy Feb 24 '17 at 17:59
  • SHA is not used for encryption, as it is a one-way hashing algorithms family. It is used as the signing complement for the encryption (in this case, SHA256 goes together with RSA2048). – Vladik Branevich May 17 '17 at 07:39
  • Actually signing is NOT the only use for SHA and you can encrypt manually.Take a look at my answer on this page. `RSAProcessEncodeOAEP()` in RSAx.cs does `byte[] pHash = rsaParams.ComputeHash(P);`, where `rsaParams` is a container for the `CryptoServiceProvider`. The hashed bytes are returned & sent to another function, returned to `dbMask`, then sent to another function and returned as `maskedDB` and combined with a seed (from a RNG). There are other processes at work, but the code DOES work to perform a manual form of encryption that can then be reversed using a private key. – vapcguy May 18 '17 at 18:38
  • 1
    Nobody but me on this page seems to be thinking outside the box, and I got a -1 for my trouble because no one wants to take the time to learn/understand it. Yes, normally you can only do `.ComputeHash()`, and a hash is non-reversible. Do all the seeding and masking yourself, however, and you have a way to perform encryption MANUALLY (doing the same thing that encryption methods would do). What is illustrated above is signing ONLY, but in my answer, I have a way to perform manual encryption using a key generated from an `RSACryptoServiceProvider`. – vapcguy May 18 '17 at 18:40
  • 1
    This is not a correct answer, OP is asking how to change the padding protocol from SHA1 to SHA256. I'm not sure how it get 11 upvotes for an wrong answer! – AaA Aug 17 '17 at 06:47
  • Why would you "go out of the box" and implement your own manual encryption? Clarification: text-book symmetric encryption (faster than asymmetric one) is performed thus: you use asymmetric crypto to derive and securely exchange the symmetric key and then you do symmetric encryption. – Vladik Branevich Aug 28 '17 at 21:08
  • As I mentioned earlier, SHA is a hashing algorithm, it can be used for computing a hash, an HMAC (hash with a secret component) and for creating a signature. When used to create a signature, a digest of the signed text is computed (using a hashing alg., e.g., SHA256) and then that digest is encrypted with the owner's private key, thus creating a verifiable digital signature. – Vladik Branevich Aug 28 '17 at 21:12
  • AaA, changing padding from Sha1 to Sha256, when you do it right, actually means one needs to upgrade the entire crypto environment. Typically this goes from RSA1024 + SHA1 to RSA2048 + SHA256. – Vladik Branevich Aug 28 '17 at 21:18
  • @VladikBranevich Why would you "go out of the box" and implement your own manual encryption? Because it takes that to be compliant with FIPS. Digital signing doesn't cut it. Part of the encryption process requires just what you said, deriving the key. Except you need a 256 key and it does NOT do this. **Signing as 256 is not the same thing as encrypting with 256.** Changing the padding is NOT all it takes, but even if it were, that is what I meant by having to implement it manually-you have to calculate your own bytes together to add it. RSACryptoServiceProvider does not encrypt to 256 bit. – vapcguy Aug 12 '20 at 14:26
  • This answer was provided 9 years ago and reflected the framework capabilities of that time. – Vladik Branevich Aug 20 '20 at 12:30
  • This answer was provided 9 years ago and reflected the framework capabilities of that time. Back then verts created with default settings were skewed - even though SHA1 was long ago considered unsafe, the default certs were created with it as the signing cypher. When using asymmetric crypto, there are two parts - encryption and signing, so the encryption cypher was RSA the encryption part was working with 2K long keys, but the signing part was stuck on SHA1. My answer explained how to "upgrade" the signing to SHA256. – Vladik Branevich Aug 20 '20 at 12:35
  • This solution worked great for a .NET 4.7.2 environment which had a cert created by a 3rd party suffering the same exact problem. – tresf Oct 19 '20 at 18:08
4

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.

Jon Adams
  • 24,464
  • 18
  • 82
  • 120
Mubashir Khan
  • 1,464
  • 1
  • 12
  • 17
  • 4
    That is using the RSA.SignData() and RSA.VerifyData() data methods, rather than using the RSA.Encrypt() and RSA.Decrypt() methods. The encrypt and decrypt use sha-1 internally. Encrypting and signing are two separate uses for RSA, but both use a secure hash. – Jonathan Wright Feb 25 '11 at 05:01
  • 2
    Last I checked the "H" in SHA = **Hash** as in Secure Hash Algorithm. By definition, that means it does not support encryption. – Thomas Feb 25 '11 at 07:11
  • 3
    @Thomas: the hash is used in the encryption process to produce the padding. – President James K. Polk Feb 26 '11 at 13:27
  • 3
    RSA encryption requires padding, typically OAEP. OAEP uses a hash function to generate the padding. – CodesInChaos Jan 18 '13 at 09:39
  • @JamesKPolk and CodesInChaos You guys are correct that you can perform a hash to produce padding, but there is no encryption being performed above, so can't say any padding is produced in this answer. I upvoted your comments because they add value to the discussion, but Jonathan Wright is correct as it relates to the answer stated, above. – vapcguy May 18 '17 at 18:50
2

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
    }
}
vapcguy
  • 7,097
  • 1
  • 56
  • 52
  • 1
    "Encryption simply requires a public key (for encrypting) and a private key (for decryption)." is true for asymmetric encryption such as RSA or EC but there is a single key for symmetric encryption such as AES. AES is generally used for encryption data, is fast and has no data size limitation. Asymmetric encryption is generally not used for encryption data, it is slow and the data length is limited to less than the key size. – zaph Apr 07 '17 at 21:35
  • @zaph Good points... never delved into AES so couldn't speak to it. Only ever used RSA, which is good enough for simple strings (and as you said, text that is less than the key size, which can be any number with a 2^n permutation, but higher you go, slower it is - 2048 was always good enough for me, though). – vapcguy Apr 07 '17 at 23:50
  • Perhaps, but doesn't make this method wrong, and doesn't make it deserve a downvote. Edit the answer & add to it, or add your own. But to me, it's the diff between going to a BMW or GM dealer. Everyone is expecting to get a car that has 4 wheels, a windshield, etc. & has a basic idea you are going to get a luxury car from BMW & you'll get a basic car from GM. If someone wants the individual specs, they'll do their own research at the dealership/online. We don't need to give all the specs on every encryption method out there for one method/implementation to be right at what it is portraying. – vapcguy Apr 10 '17 at 20:49
  • @zaph The idea here is to show how to encrypt/decrypt using SHA256/512, not give them an education in all encryption methods. They can do that research on their own, if you ask me. Everyone's use case will be different - I can't cover them all, here. The OP's question was can you use the RSACryptoServiceProvider to encrypt using SHA256 instead of SHA1. This answer shows how to do that. – vapcguy Apr 10 '17 at 20:56
  • @zaph The 1st sentence is not an error. RSA uses a public and private key-this is not incorrect & the question was about the RSACryptoServiceProvider, not AES or other similar type of encryption. You also voted to remove my other answer where I posted this on http://stackoverflow.com/questions/13650216/sha512-encryption-salted/43285332#43285332 for the same false reason. I never said SHA512 was encryption. I said "[others] neglect to say that you can, and how you can, actually encrypt WITH it." (emphasis added) - which generating hashed keys with it allows. – vapcguy Apr 10 '17 at 21:29
  • There is no such thing as "ENCRYPTING, with SHA256". – zaph Apr 11 '17 at 00:13
  • @zaph Yes, it isn't "encypting" with SHA256, agreed, but it is hashing a key that you then use to ENCRYPT WITH. Dude, I'm on your side and I get it. But your lack of reasoning and understanding that you CAN use a hashed key for encryption is destroying any chance ANYONE has in using it for this purpose. – vapcguy Apr 11 '17 at 00:32
  • @zaph At my job, we were specifically tasked with being FIPS-compliant. As it relates to key transport (which affects us when passing passwords around), an RSA-based key is required because the key length must be > 2048 (http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf page 11). Since RSA is required, by default, RSA uses SHA-1 (https://www.w3.org/PICS/DSig/RSA-SHA1_1_0.html). While NIST has SHA-1 is Acceptable (page 14) for non-digital signature apps, our office has prohibited it & said we must go to SHA-2+. Because of these 2 factors, this was the solution. – vapcguy Apr 11 '17 at 00:44
  • @zaph So unless you know some other way to encrypt with RSA and use a key generated with SHA2+, I would love to see your answer, instead of incessantly criticizing mine. You should re-read my 2nd paragraph. Yes, it's a "hack" (or you'd say wrong) to say we're encrypting with SHA-512, but then tell that to NIST to revise their publications for key transport, in the first place, then, because it's more than just a digital signature at communication time, but a need to encrypt at rest, too. Because it requires both, we have to take the most stringent. – vapcguy Apr 11 '17 at 01:08
  • "Yes, it isn't "encypting" with SHA256" and etc, why not just clean up the terminology in the answer?. Consider being more precise with terminology. "an RSA-based key is required because the key length must be > 2048" makes no sense because it implies RSA was chosen because a key length must be > 2048. No, that is not why RSA would be chosen, RSA would be chosen because a asymmetric key pair is required and then the key size is chosen for a required security level. – zaph Apr 11 '17 at 04:24
  • @zaph To me, I think you're being overly specific. Since technically there's no way to ACTUALLY encrypt with SHA-2+, but we have to meet a requirement that says "Thou shalt encrypt using SHA-2+", we have an impossible task. This was the closest thing to meeting that. And read that NIST link, page 11 (http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf ) . The requirement is that the key length is >= 2048. So you tell me how we're supposed to meet that if RSA is not chosen. None of the other approved mechanisms (AES, SKIPJACK, TDEA) have that and are approved. – vapcguy Apr 11 '17 at 14:27
  • @zaph And I'm sure plenty of others following NIST guidance will be in a similar quandry. If we simply downvote and remove any answer that provides a mechanism to encrypt against a key generated using acceptable hash algorithms because of a technicality, we are doing the industry and developer community at large a huge disservice. We simply explain it (as I have in my answer) and let the fur fly as it may. All the same, I updated my answer to include this caveat. – vapcguy Apr 11 '17 at 14:39
  • What you explained here is technically correct, but still doesn't answer the question, unless you somehow managed to change the **padding** from SHA1 to SHA256 as OP is asking. I don't think it has anything to do with how you generate the keys. Please note that most of the times keys are generated in a physical machine `HSM` and we only use them, however padding is something that we can select during encrypt/decrypt – AaA Aug 17 '17 at 06:37
  • @AaA Actually it has everything to do with the hash that is generated, because of the CryptoServiceProvider that is providing it. Padding amount, it's true, varies by SHA level, but it is set and defined by a constant that sets the string length. SHA1 = 20, SHA256 = 32, SHA512 = 64. These are in the RSAxParameters.cs class I didn't post from https://www.codeproject.com/Articles/421656/RSA-Library-with-Private-Key-Encryption-in-Csharp . – vapcguy Aug 17 '17 at 23:30
2

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:'.

yzorg
  • 4,224
  • 3
  • 39
  • 57
  • Yup - I tested it... ChillKat and BounceyCastle libraries both support OAEP padding with SHA256 hashing, and I was able to verify the (asymmetrically) encrypted payloads with Google's KMS API. – Mark Jan 22 '20 at 06:13
1

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).

Thomas Pornin
  • 72,986
  • 14
  • 147
  • 189
  • 1
    The PKCS#1 link is no longer valid. – Mani5556 Mar 18 '16 at 20:13
  • 3
    -1. Saying "Google it" isn't really an answer - that's how people would get here, in the first place. Neither is linking off somewhere and not giving the jist of it, knowing the link could be moved, later. There's no real code sample or jumping off point here to get one started. Wasted my time reading this. "You will need a SHA-256 implementation..." - yeah, that's why I came here - to find out how to do that. – vapcguy Feb 24 '17 at 17:56
-1

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.

huha
  • 4,053
  • 2
  • 29
  • 47