简体   繁体   English

RSACryptoServiceProvider(.NET的RSA)可以使用SHA256进行加密(不签名)而不是SHA1吗?

[英]Can RSACryptoServiceProvider (.NET's RSA) use SHA256 for encryption (not signing) instead of SHA1?

When encrypting, can RSACryptoServiceProvider (or any other RSA encryptor available from .NET) use SHA256 instead of SHA1? 加密时,RSACryptoServiceProvider(或.NET提供的任何其他RSA加密器)可以使用SHA256代替SHA1吗?

SHA1 appears to be hard coded with no way to change it. SHA1似乎是硬编码的,无法更改它。 For example, RSACryptoServiceProvider.SignatureAlgorithm is hard coded to return "http://www.w3.org/2000/09/xmldsig#rsa-sha1". 例如,RSACryptoServiceProvider.SignatureAlgorithm被硬编码为返回“http://www.w3.org/2000/09/xmldsig#rsa-sha1”。

If there is no way to make RSACryptoServiceProvider use SHA256, what are the alternatives? 如果无法使RSACryptoServiceProvider使用SHA256,有哪些替代方案?


Update 更新

The following code works perfectly, but I'd like to change the OAEPWithSHA1AndMGF1Padding to OAEPWithSHA256AndMGF1Padding. 以下代码完美地运行,但我想将OAEPWithSHA1AndMGF1Padding更改为OAEPWithSHA256AndMGF1Padding。 What is required on the C# side to be able to encrypt using SHA256 rather than SHA1? C#端需要什么才能使用SHA256而不是SHA1进行加密?

The encryption is done in C# using: 加密是在C#中完成的,使用:

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: 解密是在Java中使用:

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

RSACryptoServiceProvider does work with SHA2-based signatures, but you have to invest some effort into it. RSACryptoServiceProvider可以使用基于SHA2的签名,但您必须投入一些精力。

When you use a certificate to get your RSACryptoServiceProvider it really matters what's the underlying CryptoAPI provider. 当您使用证书获取RSACryptoServiceProvider时,底层的CryptoAPI提供程序真正重要。 By default, when you create a certificate with 'makecert', it's "RSA-FULL" which only supports SHA1 hashes for signature. 默认情况下,当您使用'makecert'创建证书时,它是“RSA-FULL”,它仅支持用于签名的SHA1哈希值。 You need the new "RSA-AES" one that supports SHA2. 您需要支持SHA2的新“RSA-AES”。

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): 因此,您可以使用其他选项创建证书:-sp“Microsoft Enhanced RSA and AES Cryptographic Provider”(或等效的-sy 24),然后您的代码看起来像(在.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. 如果您无法更改颁发证书的方式,则会有一个半连接的解决方法,该解决方案基于以下事实:默认情况下创建的RSACryptoServiceProvider支持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) 所以,下面的代码也可以工作,但它有点丑陋:(这段代码的作用是创建一个新的RSACryptoServiceProvider并从我们从证书中获取的密钥导入密钥)

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"));

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. 从任何Windows Server 2003及更高版本操作系统上的.NET 3.5 SP1开始,是的,RSACryptoServiceProvider支持RSA-SHA256进行签名 ,但不支持加密。

From the blog post Using RSACryptoServiceProvider for RSA-SHA256 signatures : 从博客文章使用RSACryptoServiceProvider获取RSA-SHA256签名

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. 你应该阅读原帖,因为有一些问题需要注意。

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. 如果没有办法让RSACryptoServiceProvider处理OAEP-with-SHA-256(其他人的答案似乎就是这样说的话),那么你仍然可以自己实现这个操作。 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: 实施OAEP需要以下内容:

  • Follow PKCS#1 , section 7.1. 遵循PKCS#1 ,第7.1节。 This transforms the data to encrypt into a sequence of bytes of the same length than the RSA modulus. 这将数据转换为加密成与RSA模数长度相同的字节序列。 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 ). 您将需要一个SHA-256实现( System.Security.Cryptography.SHA256Managed将没问题)和一个加密质量的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. 将得到的序列解码为大整数,执行模幂运算(模n ,RSA模数),并将结果编码为与模数相同长度的另一个字节序列。 Encoding rules are big-endian, no sign bit, and a fixed size (if this is a 1024-bit RSA key, meaning that 2 1023 <= n < 2 1024 , then the encrypted message will always have length exactly 128 bytes, even if the numerical value would have fitted in less, eg 127 or 126 bytes). 编码规则是big-endian,没有符号位和固定大小(如果这是1024位RSA密钥,意味着2 1023 <= n <2 1024 ,那么加密消息将始终具有正好128字节的长度,甚至如果数值适合较小,例如127或126字节)。

.NET 4.0 and onwards provides System.Numerics.BigInteger which has the code you need (method ModPow() ). .NET 4.0及更高版本提供了System.Numerics.BigInteger ,它具有您需要的代码(方法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). 这里不需要绝对性能:RSA 加密很快,因为公共指数很短(示例代码中为17位)。

Bounty Castle C# was updated to 1.7 after this question was asked and answered. 在提出问题并回答之后, Bounty Castle C#更新为1.7。 For future reference you might consider it, it adds supports a lot of crypto algorithms, hashes, signatures that Bouncy Castle makes available for Java. 为了将来你可能会考虑它,它增加了对Bouncy Castle为Java提供的许多加密算法,哈希,签名的支持。 Hit the link, look for 'Release Notes for 1.7' and 'Current feature list:'. 点击链接,查找“1.7版发行说明”和“当前功能列表:”。

Just for reference: How to change the CSP within a .p12 or .pfx (certificate with private key). 仅供参考:如何更改.p12或.pfx中的CSP(带私钥的证书)。 You need the password for the private key within the .pfx in order to do the following steps. 您需要.pfx中私钥的密码才能执行以下步骤。

Step 1: Convert the file into open format temp.pem 第1步:将文件转换为开放格式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 或者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 步骤2:创建包含Windows所需的CSP引用的文件myCert2.pfx

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. 第3步:删除temp.pem。 It's no longer needed. 它不再需要了。

del temp.pem

Step 4: Verify it is done correctly 第4步:验证它是否正确完成

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

This must show Microsoft CSP Name: Microsoft Enhanced RSA and AES Cryptographic Provider 这必须显示Microsoft CSP Name: Microsoft Enhanced RSA and AES Cryptographic Provider

With such a modified certificate you can use the 1st code in Kastorskijs answer. 使用这样的修改后的证书,您可以使用Kastorskijs答案中的第一个代码。

According to a Microsoft MVP (Rob Teixeira), no. 微软MVP (Rob Teixeira)称,没有。 You could use a third-part library such as Security.Cryptography.dll 您可以使用第三方库,例如Security.Cryptography.dll

All of the other answers here are about SIGNING, not ENCRYPTING, with SHA256. 这里的所有其他答案都是关于使用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. 任何SHA-2或更高版本的算法都用于散列,不一定是加密/解密,但这并不意味着您无法使用这些算法生成密钥,然后对它们进行加密/解密。 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. 从技术上讲,我会对那些不同意这个答案的人提出这个问题,这不是“用SHA256加密”,但它确实允许RSA使用使用该算法生成的散列密钥。 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. 让你的特定组织决定是否足以符合NIST / FIPS标准,这应该是你的原因,因为在我研究这个时,它是我的。

Encryption (using RSA, or another asymmetric encryption algorithm) simply requires a public key (for encrypting) and a private key (for decryption). 加密(使用RSA或其他非对称加密算法)只需要公钥(用于加密)和私钥(用于解密)。 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. 我将把我做过的一些研究拼凑起来,展示一些路由,你可以使用一个使用SHA-256哈希创建的密钥,然后加密/解密。 You can generate a SHA-256 key either by creating a certificate or letting the RSACryptoServiceContainer give you one. 您可以通过创建证书或让RSACryptoServiceContainer为您提供一个来生成SHA-256密钥。

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. 如果您想使用SHA512,您只需在制作证书时将该sha256参数更改为sha512

Reference : https://social.msdn.microsoft.com/Forums/en-US/69e39ad0-13c2-4b5e-bb1b-972a614813fd/encrypt-with-certificate-sha512?forum=csharpgeneral 参考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 使用RSACryptoServiceProvider生成密钥

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 . 如果要使用SHA512,可以将RSAxHashAlgorithm.SHA256更改为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 . 这些方法使用名为RSAx.DLL的DLL,使用https://www.codeproject.com/Articles/421656/RSA-Library-with-Private-Key-Encryption-in-Csharp中的源代码构建,这不是我的(作者:Arpan Jati),但我已经使用过它,并且在CodeProject的开源许可下可供开发者社区使用。 You could also just bring in 3 classes from that project, instead: RSAx.cs, RSAxParameters.cs, RSAxUtils.cs 您也可以从该项目中引入3个类,而不是: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. 该代码将在30000字符限制上发布此帖子,因此我将发布RSAx以便您可以看到正在发生的事情,但所有3个类都是必需的。 You have to change the namespace and reference the System.Numerics assembly. 您必须更改命名空间并引用System.Numerics程序集。

RSAx.cs 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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM