简体   繁体   English

.NET中的RSA加密/解密问题

[英]RSA Encrypt / Decrypt Problem in .NET

I'm having a problem with C# encrypting and decrypting using RSA. 我在使用RSA进行C#加密和解密时遇到问题。 I have developed a web service that will be sent sensitive financial information and transactions. 我已经开发了一个网络服务,可以发送敏感的财务信息和交易。 What I would like to be able to do is on the client side, Encrypt the certain fields using the clients RSA Private key, once it has reached my service it will decrypt with the clients public key. 我想做的是在客户端,使用客户端RSA私钥对某些字段进行加密,一旦到达我的服务,它将使用客户端的公钥进行解密。

At the moment I keep getting a "The data to be decrypted exceeds the maximum for this modulus of 128 bytes." 目前,我不断收到“要解密的数据超出了128字节模数的最大值”。 exception. 例外。 I have not dealt much with C# RSA cryptography so any help would be greatly appreciated. 我对C#RSA密码学的处理不多,因此将不胜感激。

This is the method i am using to generate the keys 这是我用来生成密钥的方法

private void buttonGenerate_Click(object sender, EventArgs e)
{
    string secretKey = RandomString(12, true);

    CspParameters param = new CspParameters();
    param.Flags = CspProviderFlags.UseMachineKeyStore;

    SecureString secureString = new SecureString();
    byte[] stringBytes = Encoding.ASCII.GetBytes(secretKey);
    for (int i = 0; i < stringBytes.Length; i++)
    {
        secureString.AppendChar((char)stringBytes[i]);
    }
    secureString.MakeReadOnly();
    param.KeyPassword = secureString;

    RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);
    rsaProvider = (RSACryptoServiceProvider)RSACryptoServiceProvider.Create();
    rsaProvider.KeySize = 1024;


    string publicKey = rsaProvider.ToXmlString(false);
    string privateKey = rsaProvider.ToXmlString(true);

    Repository.RSA_XML_PRIVATE_KEY = privateKey;
    Repository.RSA_XML_PUBLIC_KEY = publicKey;

    textBoxRsaPrivate.Text = Repository.RSA_XML_PRIVATE_KEY;
    textBoxRsaPublic.Text = Repository.RSA_XML_PUBLIC_KEY;

    MessageBox.Show("Please note, when generating keys you must sign on to the gateway\n" +
        " to exhange keys otherwise transactions will fail", "Key Exchange", MessageBoxButtons.OK, MessageBoxIcon.Information);

}

Once i have generated the keys, i send the public key to the web service which stores it as an XML file. 一旦生成密钥,我便将公共密钥发送到Web服务,该服务将其存储为XML文件。

Now i decided to test this so here is my method to encrypt a string 现在我决定测试一下,所以这是我加密字符串的方法

public static string RsaEncrypt(string dataToEncrypt)
{
    string rsaPrivate = RSA_XML_PRIVATE_KEY;
    CspParameters csp = new CspParameters();
    csp.Flags = CspProviderFlags.UseMachineKeyStore;

    RSACryptoServiceProvider provider = new RSACryptoServiceProvider(csp);

    provider.FromXmlString(rsaPrivate);

    ASCIIEncoding enc = new ASCIIEncoding();
    int numOfChars = enc.GetByteCount(dataToEncrypt);
    byte[] tempArray = enc.GetBytes(dataToEncrypt);
    byte[] result = provider.Encrypt(tempArray, true);
    string resultString = Convert.ToBase64String(result);
    Console.WriteLine("Encrypted : " + resultString);
    return resultString;
}

I do get what seems to be an encrypted value. 我确实得到了似乎是加密值的东西。 In the test crypto web method that i created, i then take this encrypted data, try and decrypt the data using the clients public key and send this back in the clear. 在我创建的测试密码网络方法中,我然后获取此加密数据,尝试使用客户端公钥对数据进行解密,然后将其以明文形式发送回去。 But this is where the exception is thrown. 但这是引发异常的地方。 Here is my method responsible for this. 这是我负责的方法。

public string DecryptRSA(string data, string merchantId)
{
    string clearData = null;
    try
    {
        CspParameters param = new CspParameters();
        param.Flags = CspProviderFlags.UseMachineKeyStore;
        RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(param);

        string merchantRsaPublic = GetXmlRsaKey(merchantId);
        rsaProvider.FromXmlString(merchantRsaPublic);
        byte[] asciiString = Encoding.ASCII.GetBytes(data);

        byte[] decryptedData = rsaProvider.Decrypt(asciiString, false);

        clearData = Convert.ToString(decryptedData);
    }
    catch (CryptographicException ex)
    {
        Log.Error("A cryptographic error occured trying to decrypt a value for " + merchantId, ex);

    }
    return clearData;
}

If anyone could help me that would be awesome, as i have said i have not done much with C# RSA encryption/decryption. 如果有人可以帮助我,那将是非常棒的,正如我所说的,我在C#RSA加密/解密方面做得还不够。

Allow me a bit of terminology. 请允许我使用一些术语。 There is asymmetric encryption and there is digital signature . 非对称加密数字签名

  • Asymmetric encryption is about keeping confidentiality. 非对称加密是关于保持机密性。 Some sensitive data is transformed into something unreadable, save for the entity who knows the decryption key. 除了知道解密密钥的实体之外,一些敏感数据会转换为不可读的内容。 The decryption key is necessarily the private key: if the decryption key is the public key, then everybody can decrypt the data (the public key is, well, public) and there is no confidentiality anymore. 解密密钥一定是私有密钥:如果解密密钥是公共密钥,那么每个人都可以解密数据(公共密钥是公共的),并且不再具有机密性。 In asymmetric encryption, one encrypts with the public key and decrypts with the corresponding private key. 在非对称加密中,使用公钥加密,然后使用相应的私钥解密。

  • Digital signatures are meant to prove integrity. 数字签名旨在证明完整性。 Someone computes a kind of keyed checksum over the data, in such a way that the link between the checksum and the data can be verified later. 有人计算出一种对数据加密钥的校验和,以使校验和与数据之间的链接可以在以后被验证。 This is a "signature" only because the power to compute that checksum requires knowledge of something which is not public -- in plain words, signing uses the private key. 这是一个“签名”,仅是因为计算该校验和的能力需要了解一些不公开的知识-简而言之,签名使用私钥 Verification, however, should be doable by anybody, and thus use the public key. 但是,验证应该对任何人都可行,并因此使用公共密钥。

A fair bit of confusion is implied by the fact that "the" RSA algorithm is actually a mathematical operation which can be declined into both an asymmetric encryption system, and a digital signature system. RSA算法实际上是一种数学运算,可以归结为非对称加密系统和数字签名系统,这一事实暗含着一些困惑。 The confusion is further enhanced by the RSA standard, aka PKCS#1 , which implicitly relies on how RSA digital signatures were first described, ie as a "reversed encryption" ("the signer encrypts the data with his private key"). RSA标准(也称为PKCS#1 )进一步增加了混乱,该标准隐含地依赖于首先描述RSA数字签名的方式,即所谓的“反向加密”(“签名者使用其私钥对数据加密”)。 Which leads to things like RSA signatures called "sha1WithRSAEncryption". 这就导致了诸如称为“ sha1WithRSAEncryption”的RSA签名之类的事情。 This is quite unfortunate. 这是很不幸的。

Therefore, you must first decide whether you want confidentiality or signatures. 因此,您必须首先决定是否要保密或签名。 For confidentiality, for data sent from clients to the server, the server shall own a private key, and the clients use the server public key to encrypt the data. 为了保密, 客户端服务器发送数据时,服务器将自己的私钥,客户端使用服务器的公钥对数据进行加密。 For signatures, each client shall have his own private key and use it to sign the data, and the server verifies the signatures. 对于签名,每个客户端应具有自己的私钥,并使用其对数据进行签名,然后服务器验证签名。 From your description I cannot tell what you are really after, thanks to the confusion I allude to above. 根据您的描述,由于上面提到的混乱,我无法告诉您真正的追求。

Also, there is something called authentication which may look like digital signatures, but is weaker. 此外,还有一种称为身份验证的东西,看起来像数字签名,但功能较弱。 The point of signatures is than anybody can verify the signature. 签名的重点是任何人都可以验证签名。 In particular, the signature can be shown to a judge and thus serve as legal weapon against the signer (the signature is legally binding -- at least if you do it right, and in the current state of regulations over electronic signatures, this is not easy). 特别是,可以将签名显示给法官,从而充当反对签名者的法律武器(签名具有法律约束力-至少如果您做对了,并且在电子签名法规的当前状态下,这不是简单)。 In most situations you only need something weaker and simpler, in which the server is convinced that it talks to the right client, but cannot afterwards convince anybody else that this client was really there. 在大多数情况下,您只需要弱点和简单点的东西即可,在这种情况下,服务器确信服务器正在与正确的客户端进行通信,但此后便无法说服其他任何人该客户端确实存在。 Any web site with user passwords is using such authentication. 任何带有用户密码的网站都在使用这种身份验证。

With that being said... 话虽这么说...

  • RSA asymmetric encryption covers only short messages. RSA非对称加密仅涵盖短消息。 For a 1024-bit RSA key (ie a key where the most important part, the "RSA modulus", is a big number with a value between 2^1023 and 2^1024, and encrypted messages will be of length 128 bytes), the maximum size of an encrypted message is 117 bytes (that's the actual source of your error message). 对于1024位RSA密钥(即,其中最重要的部分“ RSA模数”是一个很大的数字,其值在2 ^ 1023和2 ^ 1024之间,并且加密消息的长度为128个字节),加密消息的最大大小为117个字节(这是错误消息的实际来源)。 When we want to send longer messages, we use an hybrid system, in which we only encrypt a small bunch of random bits (say 128 bits) and use that bunch as a key for a symmetric encryption system (eg AES) which can process much longer messages (and much faster, too). 当我们想发送更长的消息时,我们使用一个混合系统,在该系统中,我们仅加密一小串随机位(例如128位),并使用该串作为对称加密系统(例如AES)的密钥,该对称加密系统可以处理很多更长的消息(也更快)。

  • RSA signatures, similarly, can be computed only on short messages, hence the PKCS#1 standard mandates that a signature is actually computed over a hash value. 同样,RSA签名只能在短消息上计算,因此PKCS#1标准要求在哈希值上实际计算签名。 The hash value is the output of a specific hash function , which is computed over the message to sign. 哈希值是特定哈希函数的输出,该函数是根据要签名的消息计算的。 The hash function has a fixed-sized output (eg 256 bits for SHA-256) but accepts input messages of (almost) arbitrary length. 散列函数具有固定大小的输出(例如SHA-256为256位),但接受(几乎)任意长度的输入消息。 Hash functions are public (there is no key in them) and, for proper security, must have some special properties. 哈希函数是公共的(它们中没有键),并且为了适当的安全性,必须具有一些特殊的属性。 SHA-256 is, right now, not a bad choice. 目前,SHA-256并不是一个不错的选择。 SHA-1 (a predecessor of SHA-256) has been proven to have some weaknesses and should be avoided. SHA-1(SHA-256的前身)已被证明具有某些弱点,应避免使用。 MD5 has (a kind-of uncle of SHA-1) has bigger weaknesses and shall not be used. MD5(SHA-1的一种叔叔)具有较大的弱点,因此不应使用。

  • Proper use of asymmetric encryption, especially in an hybrid scheme, and digital signatures, is trickier than what the text above may suggest. 正确使用非对称加密(尤其是在混合方案中)和数字签名,比上面的文字可能要难得多。 It is awfully easy to get it wrong at some point, invisibly , ie the code will appear to work but will leak data useful for an attacker. 很容易在某个时候将它弄错,这是无形的 ,即代码看起来可以工作,但是会泄漏对攻击者有用的数据。 The right way to use asymmetric encryption or digital signatures is to rely on existing, well-thought protocols. 使用非对称加密或数字签名的正确方法是依赖现有的,经过深思熟虑的协议。 A protocol is an assembly of cryptographic elements into a coherent system, where leaks are taken care of. 协议是将加密元素组装到一个统一的系统中,在该系统中负责泄漏的处理。 The prime example is TLS, also known as SSL. 主要示例是TLS,也称为SSL。 It is a protocol which ensures confidential data transmission, with integrity and authentication (possibly mutual authentication). 它是一个协议,可通过完整性和身份验证(可能是相互身份验证)确保机密数据传输。 The HTTPS protocol is a mixture of HTTP and SSL. HTTPS协议是HTTP和SSL的混合。 The bright side is that HTTPS has existing implementations, notably in C#. 好的方面是HTTPS具有现有的实现,尤其是在C#中。 The code which is easiest to implement and debug is the code which has already been implemented and debugged. 最容易实现和调试的代码是已经实现和调试的代码。 So use HTTPS and you will live longer and happier. 因此,使用HTTPS,您的寿命会更长,更快乐。

I understand why you are asking the question. 我了解您为什么要问这个问题。 The problem is that RSA is not used like a typical block cypher (like AES or 3DES) that encrypts 8 bytes at a time, all day long. 问题在于,RSA并非像典型的整块密码(例如AES或3DES)那样使用,整日一次加密8个字节。 RSA is a math operation that returns the remainder of a division (the modulus). RSA是一种数学运算,可返回除法的余数(模数)。 Back in grade school, when you learned long division, remember that the remainder can never be greater than the divisor:if you are dividing 20 by 7, your remainder is 6. No matter what integer you divide by 7, the remainder cannot be greater than six. 回到小学时,当您学习了长除法时,请记住余数永远不能大于除数:如果将20除以7,则余数为6。无论您将整数除以7是多少,余数都不能大于比六。

RSA math is the same way. RSA数学是相同的方法。 For example, if you are using a 1024-bit RSA public key, the remainder can never be greater than 2^1024, which is only 128 bytes. 例如,如果您使用的是1024位RSA公钥,则其余部分永远不能大于2 ^ 1024,即128个字节。 So you can only encrypt 128 bytes at a time with this key. 因此,使用此密钥一次只能加密128个字节。 (That's one reason we measure the size of RSA keys by the number of bits.) (这是我们通过位数来衡量RSA密钥大小的原因之一。)

Technically you could use this RSA key in a loop to encrypt 128 byte chunks of your data at a time. 从技术上讲,您可以在循环中使用此RSA密钥来一次加密128字节的数据块。 In reality, we almost never do this because RSA math is BIG and SLOW. 实际上,我们几乎从不执行此操作,因为RSA数学运算是大而慢的。 Instead, we use what is called "two-phase" encryption. 而是使用所谓的“两阶段”加密。 We use RSA to encrypt only a short "session key", and then use that session key in a fast symmetric-keyed block cypher (like AES) to encrypt the actual data. 我们使用RSA仅加密短的“会话密钥”,然后在快速对称密钥块密码(如AES)中使用该会话密钥对实际数据进行加密。

The whole protocol is: 整个协议是:

  1. Obtain the RSA public key of your destination. 获取目标的RSA公钥。 This is often delivered embedded in a certificate; 通常将其嵌入证书中。 if it is, be sure to validate the certificate to make sure the key is genuine. 如果是,请确保验证证书以确保密钥是真实的。 Let's say the RSA key is 2048 bits long. 假设RSA密钥的长度为2048位。
  2. Generate a cryptographically strong pseudo-random number to use as a key for the block cypher (you need 256 bits as the key for AES-256, for example.) Note that 256 < 2048, the max that RSA-2048 can encrypt at once. 生成一个加密强度高的伪随机数,用作块密码的密钥(例如,您需要256位作为AES-256的密钥。)请注意,256 <2048,即RSA-2048可以一次加密的最大值。 We call this random number the "session key". 我们将此随机数称为“会话密钥”。
  3. Encrypt the session key using the RSA 2048-bit public key. 使用RSA 2048位公共密钥对会话密钥进行加密。 It will give you 2048 bits of encrypted session key. 它将为您提供2048位加密的会话密钥。 Note that this operation is very slow. 请注意,此操作非常慢。
  4. Encrypt all the secret data using AES-256, using the session key. 使用会话密钥,使用AES-256加密所有机密数据。 Note that this is much faster than step 3. 请注意,这比第3步快得多。
  5. Bundle the public key ID from the certificate, the RSA encrypted session key, and the AES encrypted data together. 将证书中的公钥ID,RSA加密的会话密钥和AES加密的数据捆绑在一起。 I'd also tag it with a format identifier and version number, so you know what format it is in and how to decrypt it. 我还将用格式标识符和版本号对其进行标记,以便您了解其所用的格式以及如何对其进行解密。
  6. Send the bundle to the destination. 将分发包发送到目的地。

  7. At the destination you use the format identifier and version to take apart the bundle. 在目标位置,您可以使用格式标识符和版本来分解捆绑包。

  8. Retrieve the private key whose identity is in the public key ID field. 检索身份在公钥ID字段中的私钥。
  9. Use this private key in RSA to decrypt the session key. 在RSA中使用此私钥来解密会话密钥。
  10. Use the session key in AES to decrypt the data. 使用AES中的会话密钥解密数据。

If you are going to do this, you should know that it is exactly what the CMS (PKCS#7) format is for. 如果要执行此操作,则应该知道这正是CMS(PKCS#7)格式的用途。 I would encourage you to learn about the standard and adopt it, rather than trying to invent your own format. 我鼓励您学习并采用该标准,而不要尝试发明自己的格式。 Microsoft's CSP supports it, so it should be easy. Microsoft的CSP支持它,因此应该很容易。

If you don't follow a standard you will have to make your own decisions about things like "what format should the AES key bits be in in the RSA encryption process?" 如果您不遵循标准,则必须对“ RSA加密过程中的AES密钥位应采用哪种格式?”之类的问题做出自己的决定。 More likely, you would almost certainly make security mistakes, weakening your system. 您更有可能会肯定会犯安全错误,从而削弱您的系统。 Besides, you will find that tools such as the CSP will be very difficult to work with if you don't follow a standard. 此外,您会发现,如果不遵循标准,将很难使用CSP之类的工具。

In DecryptRSA, is "data" base 64 encoded? 在DecryptRSA中,“数据”基础64是否已编码? If it is, you have to undo that first. 如果是这样,则必须先撤消该操作。

Honestly I think you shouldn't implement that routine yourself to protect "sensitive financial information", unless you have a lot of experience with cryptography. 老实说,我认为您不应自己执行该例程来保护“敏感的财务信息”,除非您在密码学方面有丰富的经验。 There are too many ways to make errors. 出错的方法太多。 Better use some ready solution - maybe SSL and certificates, or just PGP or GnuPG? 更好地使用一些现成的解决方案-也许是SSL和证书,或者只是PGP或GnuPG?

RSA is primarily used to validate secure hashes of data - rather than encrypting the data itself. RSA主要用于验证数据的安全散列-而不是对数据本身进行加密。 So, given a large blob of data, you might use SHA512 to create a hash of that data, then use RSA to sign that hash. 因此,给定大量数据,您可以使用SHA512创建该数据的哈希,然后使用RSA对该哈希进行签名

You'll want to use a symmetric encryption algorithm for large blocks of data - something like AES or 3DES. 您需要对大型数据块使用对称加密算法,例如AES或3DES。

Managing secure transactions isn't easy and really ought to be left to those guys that spend all day and night thinking about it. 管理安全交易并非易事,真正的工作应该留给那些全天候花时间思考的人。 If you're exposing the service as over the web, just use SSL which already encrypts and secures your data. 如果您要通过网络公开服务,只需使用已经加密并保护数据的SSL。

First decide what you are trying to protect against. 首先确定您要保护的内容。 If you "encrypt" something using the private key, anyone can "decrypt" it with the public key, since the public key is - well - public . 如果使用私钥你“加密”的东西,任何人都可以“解密”将其与公共密钥,因为公共密钥是-好- 大众

If you actually want to sign it, you should (as Paul Alexander explains) sign a hash with the private key which can then be verified on the server. 如果您确实要对其进行签名,则应该(如Paul Alexander所解释的那样)使用私钥对哈希签名,然后可以在服务器上对其进行验证。

To encrypt data using RSA you should first generate a random symmetric key (fx AES), encrypt the key using a public key and encrypt the data using the symmetric key. 要使用RSA 加密数据,您首先应该生成一个随机对称密钥(fx AES),使用公共密钥加密密钥,然后使用对称密钥加密数据。 You can then transmit the encrypted key together with the encrypted data to the holder of the private key, who can then first decrypt the encrypted key with the private key and then decrypt the data with the symmetric key. 然后,您可以将加密的密钥与加密的数据一起传输给专用密钥的持有者,后者可以先使用专用密钥对加密的密钥进行解密,然后再使用对称密钥对数据进行解密。

You might also consider using SSL, but remember to carefully consider the authentication. 您可能还考虑使用SSL,但请记住要仔细考虑身份验证。 You will probably need client authentication and have to decide which certificates to trust (you should not just blindly accept any certificate issued by Verisign). 您可能需要客户端身份验证,并且必须决定信任哪些证书(您不应该只是盲目接受Verisign颁发的任何证书)。

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

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