.NET 中的 RSA 加密/解密问题
我在使用 RSA 进行 C# 加密和解密时遇到问题。我开发了一个网络服务,将发送敏感的财务信息和交易。我希望能够在客户端使用客户端 RSA 私钥加密某些字段,一旦到达我的服务,它将使用客户端公钥进行解密。
目前,我不断收到“要解密的数据超出了 128 字节模数的最大值”。例外。我对 C# RSA 加密技术了解不多,因此我们将不胜感激。
这是我用来生成密钥的方法。
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);
}
生成密钥后,我将公钥发送到 Web 服务,该服务将其存储为 XML 文件。
现在我决定对此进行测试,因此这是我加密字符串的方法,
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;
}
我确实得到了似乎是加密值的值。在我创建的测试加密 Web 方法中,我获取此加密数据,尝试使用客户端公钥解密数据,并将其以明文形式发回。但这就是抛出异常的地方。这是我负责此的方法。
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;
}
如果有人能帮助我,那就太棒了,正如我所说,我在 C# RSA 加密/解密方面还没有做太多事情。
I'm having a problem with C# encrypting and decrypting using RSA. 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.
At the moment I keep getting a "The data to be decrypted exceeds the maximum for this modulus of 128 bytes." exception. I have not dealt much with C# RSA cryptography so any help would be greatly appreciated.
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.
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
请允许我使用一些术语。有非对称加密和数字签名。
非对称加密是为了保密。一些敏感数据被转换成不可读的东西,除了知道解密密钥的实体。解密密钥必然是私钥:如果解密密钥是公钥,那么每个人都可以解密数据(公钥是公开的)并且不再有机密性。在非对称加密中,使用公钥进行加密,并使用相应的私钥进行解密。
数字签名旨在证明完整性。有人对数据计算一种密钥校验和,以便稍后可以验证校验和与数据之间的链接。这是一个“签名”,只是因为计算校验和的能力需要了解一些不公开的信息——简单来说,签名使用私钥。然而,任何人都应该可以进行验证,因此可以使用公钥。
“RSA”算法实际上是一种数学运算,可以归结为非对称加密系统和数字签名系统,这一事实隐含着相当大的混乱。 RSA 标准(又名 PKCS#1)进一步加剧了这种混乱,这隐含地依赖于 RSA 数字签名的最初描述方式,即“反向加密”(“签名者用他的私钥加密数据”)。这导致了诸如称为“sha1WithRSAEncryption”的 RSA 签名之类的事情。这是相当不幸的。
因此,您必须首先决定是否需要保密或签名。为了保密,对于从客户端发送到服务器的数据,服务器应拥有私钥,客户端使用服务器公钥来加密数据。对于签名,每个客户端都应该有自己的私钥,并用它来对数据进行签名,服务器验证签名。从你的描述中,我无法判断你真正想要什么,这要归功于我上面提到的混乱。
此外,还有一种称为“身份验证”的东西,它可能看起来像数字签名,但功能较弱。签名的要点是任何人都可以验证签名。特别是,签名可以向法官展示,从而作为针对签名者的法律武器(签名具有法律约束力——至少如果你做得正确的话,并且在当前的法规状态下)通过电子签名,这并不容易)。在大多数情况下,您只需要一些更弱和更简单的东西,其中服务器确信它与正确的客户端对话,但事后无法说服其他任何人该客户端确实存在。任何具有用户密码的网站都在使用此类身份验证。
话虽这么说...
RSA 非对称加密仅涵盖短消息。对于 1024 位 RSA 密钥(即密钥中最重要的部分“RSA 模数”是一个值在 2^1023 到 2^1024 之间的大数,加密消息的长度为 128 字节),加密消息的最大大小为 117 字节(这是错误消息的实际来源)。当我们想要发送更长的消息时,我们使用混合系统,在该系统中,我们仅加密一小堆随机位(例如 128 位),并将该堆用作对称加密系统的密钥(例如 AES),它可以处理更长的消息(而且速度也更快)。
正确使用非对称加密(尤其是在混合方案中)和数字签名,比上面的文本所建议的更棘手。在某些时候很容易犯错误,不可见,即代码看起来可以工作,但会泄露对攻击者有用的数据。使用非对称加密或数字签名的正确方法是依赖现有的、经过深思熟虑的协议。协议是将密码元素组合成一个连贯的系统,并在其中处理泄漏问题。最好的例子是 TLS,也称为 SSL。它是一种确保机密数据传输、完整性和身份验证(可能是相互身份验证)的协议。 HTTPS 协议是 HTTP 和 SSL 的混合。好的一面是 HTTPS 已有实现,特别是在 C# 中。最容易实现和调试的代码是已经实现和调试的代码。因此,使用 HTTPS,您会活得更长久、更快乐。
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. The confusion is further enhanced by the RSA standard, aka PKCS#1, which implicitly relies on how RSA digital signatures were first described, i.e. as a "reversed encryption" ("the signer encrypts the data with his private key"). Which leads to things like RSA signatures called "sha1WithRSAEncryption". 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. For a 1024-bit RSA key (i.e. 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). 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 (e.g. AES) which can process much longer messages (and much faster, too).
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. 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 (e.g. 256 bits for SHA-256) but accepts input messages of (almost) arbitrary length. 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-1 (a predecessor of SHA-256) has been proven to have some weaknesses and should be avoided. MD5 has (a kind-of uncle of SHA-1) has bigger weaknesses and shall not be used.
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, i.e. 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. 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. The bright side is that HTTPS has existing implementations, notably in 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.
我明白你为什么问这个问题。问题在于 RSA 的使用方式与典型的分组密码(如 AES 或 3DES)不同,全天一次加密 8 个字节。 RSA 是一种数学运算,返回除法的余数(模数)。回到小学,当你学习长除法时,请记住余数永远不能大于除数:如果你将 20 除以 7,你的余数是 6。无论你除以什么整数,余数都不能大于除数比六个。
RSA 数学也是同样的道理。例如,如果您使用 1024 位 RSA 公钥,则余数永远不能大于 2^1024,即 128 字节。所以用这个密钥一次只能加密128个字节。 (这是我们通过位数来衡量 RSA 密钥大小的原因之一。)
从技术上讲,您可以在循环中使用此 RSA 密钥一次加密 128 字节的数据块。实际上,我们几乎从不这样做,因为 RSA 数学运算庞大且缓慢。相反,我们使用所谓的“两阶段”加密。我们使用 RSA 仅加密一个短的“会话密钥”,然后在快速对称密钥块密码(如 AES)中使用该会话密钥来加密实际数据。
整个协议是:
将捆绑包发送到目标。
在目的地,您使用格式标识符和版本来分解捆绑包。
如果您打算这样做,您应该知道这正是 CMS (PKCS#7) 格式的用途。我鼓励您了解该标准并采用它,而不是试图发明自己的格式。 Microsoft 的 CSP 支持它,因此应该很容易。
如果您不遵循标准,您将不得不自己做出决定,例如“在 RSA 加密过程中 AES 密钥位应采用什么格式?”更有可能的是,您几乎肯定会犯安全错误,从而削弱您的系统。此外,您会发现,如果不遵循标准,CSP 等工具将很难使用。
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 is a math operation that returns the remainder of a division (the modulus). 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.
RSA math is the same way. 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. So you can only encrypt 128 bytes at a time with this key. (That's one reason we measure the size of RSA keys by the number of bits.)
Technically you could use this RSA key in a loop to encrypt 128 byte chunks of your data at a time. In reality, we almost never do this because RSA math is BIG and SLOW. 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.
The whole protocol is:
Send the bundle to the destination.
At the destination you use the format identifier and version to take apart the bundle.
If you are going to do this, you should know that it is exactly what the CMS (PKCS#7) format is for. 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.
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?" 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.
在 DecryptRSA 中,“数据”是基于 64 编码的吗?如果是,您必须先撤消该操作。
老实说,我认为您不应该自己实施该例程来保护“敏感的财务信息”,除非您在密码学方面拥有丰富的经验。犯错误的方法太多了。最好使用一些现成的解决方案 - 也许是 SSL 和证书,或者只是 PGP 或 GnuPG?
In DecryptRSA, is "data" base 64 encoded? 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?
RSA 主要用于验证数据的安全哈希值,而不是加密数据本身。因此,给定大量数据,您可以使用 SHA512 创建该数据的哈希值,然后使用 RSA 对该哈希值进行签名。
您需要对大数据块使用对称加密算法 - 例如 AES 或 3DES。
管理安全交易并不容易,确实应该留给那些日夜思考的人。如果您通过网络公开服务,只需使用已加密并保护您的数据的 SSL。
RSA is primarily used to validate secure hashes of data - rather than encrypting the data itself. 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.
You'll want to use a symmetric encryption algorithm for large blocks of data - something like AES or 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.
首先决定您要防止什么。如果您使用私钥“加密”某些内容,则任何人都可以使用公钥“解密”它,因为公钥是 - 嗯 - 公共。
如果您确实想对其进行签名,则应该(正如 Paul Alexander 所解释的那样)使用私钥对哈希进行签名,然后可以在服务器上对其进行验证。
要使用 RSA 加密数据,您应该首先生成随机对称密钥 (fx AES),使用公钥加密密钥,然后使用对称密钥加密数据。然后,您可以将加密的密钥与加密的数据一起传输给私钥的持有者,然后该持有者可以首先使用私钥解密加密的密钥,然后使用对称密钥解密数据。
您也可以考虑使用 SSL,但请记住仔细考虑身份验证。您可能需要客户端身份验证,并且必须决定信任哪些证书(您不应盲目接受 Verisign 颁发的任何证书)。
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.
To encrypt data using RSA you should first generate a random symmetric key (f.x. AES), encrypt the key using a public key and encrypt the data using the symmetric key. 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. 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).