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

Posted

技术标签:

【中文标题】.NET 中的 RSA 加密/解密问题【英文标题】:RSA Encrypt / Decrypt Problem in .NET 【发布时间】:2011-01-29 08:54:59 【问题描述】:

我在使用 RSA 进行 C# 加密和解密时遇到问题。我开发了一个 Web 服务,它将发送敏感的财务信息和交易。我想做的是在客户端,使用客户端 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);


生成密钥后,我将公钥发送到将其存储为 XML 文件的 Web 服务。

现在我决定测试一下,所以这是我加密字符串的方法

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 加密/解密方面做得不多。

【问题讨论】:

最近 RSA 加密被破解的事情对你有影响吗? engadget.com/2010/03/09/… RSA 未破解。在实际系统中,只有它的实现会受到侧信道攻击:该实现可能会通过功耗、时序和其他不属于抽象的物理措施泄露私钥信息电脑的型号。这已经有很长一段时间了(我认为在 1997 年描述了定时攻击)。这样的新闻不是新闻,也不是真正的“裂缝”。 【参考方案1】:

请允许我使用一些术语。有非对称加密,还有数字签名

非对称加密是为了保持机密性。一些敏感数据被转换成不可读的东西,除了知道解密密钥的实体。解密密钥必然是私钥:如果解密密钥是公钥,那么每个人都可以解密数据(公钥是公开的)并且不再有保密性。在非对称加密中,用公钥加密,用对应的私钥解密。

数字签名旨在证明完整性。有人在数据上计算一种键控校验和,以便以后可以验证校验和与数据之间的链接。这是一个“签名”,只是因为计算校验和的能力需要知道一些不公开的东西——简单来说,签名使用 private 密钥。但是,任何人都应该可以进行验证,因此使用公钥。

“RSA”算法实际上是一种数学运算,可以分解为非对称加密系统和数字签名系统,这一事实暗示了相当多的混淆。 RSA 标准又名PKCS#1 进一步加剧了这种混淆,它隐含地依赖于 RSA 数字签名的首次描述方式,即作为“反向加密”(“签名者使用他的私钥加密数据”)。这导致诸如称为“sha1WithRSAEncryption”的RSA签名之类的东西。这很不幸。

因此,您必须首先决定是否需要保密或签名。为保密起见,对于客户端发送到服务器的数据,服务器应拥有私钥,客户端使用服务器公钥来加密数据。对于签名,每个客户端都有自己的私钥,并用它来对数据进行签名,服务器验证签名。由于我在上面提到的混乱,从你的描述中我无法判断你真正追求的是什么。

此外,还有一种称为身份验证的东西,它可能看起来像数字签名,但更弱。签名的意义在于任何人都可以验证签名。特别是,签名可以显示给法官,因此可以作为反对签名者的法律武器(签名具有法律约束力——至少如果你做得对,并且在当前的法规状态下通过电子签名,这并不容易)。在大多数情况下,您只需要更弱和更简单的东西,其中服务器确信它与正确的客户端对话,但事后无法说服其他任何人该客户端确实存在。任何有用户密码的网站都在使用这种身份验证。

话虽这么说......

RSA 非对称加密仅涵盖短消息。对于 1024 位 RSA 密钥(即其中最重要的部分“RSA 模数”是一个数值在 2^1023 和 2^1024 之间的大数字,加密消息的长度为 128 字节的密钥),加密消息的最大大小为 117 个字节(这是错误消息的实际来源)。当我们想要发送更长的消息时,我们会使用混合系统,其中我们只加密一小部分随机位(比如 128 位),并将该束用作对称加密系统的密钥(例如 AES),它可以处理更长的消息(也更快)。

类似地,RSA 签名只能在短消息上计算,因此 PKCS#1 标准要求签名实际上是在哈希值上计算的。哈希值是特定 哈希函数 的输出,它是根据要签名的消息计算得出的。散列函数具有固定大小的输出(例如 SHA-256 的 256 位),但接受(几乎)任意长度的输入消息。散列函数是公共的(其中没有密钥),并且为了适当的安全性,必须具有一些特殊属性。 SHA-256 现在是一个不错的选择。 SHA-1(SHA-256 的前身)已被证明有一些弱点,应该避免使用。 MD5(SHA-1的一种叔叔)有较大的弱点,不能用。

正确使用非对称加密,尤其是在混合方案和数字签名中,比上面的文字可能暗示的要棘手。在某些时候很容易出错,不可见,即代码看起来可以工作,但会泄露对攻击者有用的数据。使用非对称加密或数字签名的正确方法是依赖现有的、经过深思熟虑的协议。协议是将加密元素组合成一个连贯的系统,其中泄漏得到处理。最好的例子是 TLS,也称为 SSL。它是一种确保机密数据传输的协议,具有完整性和身份验证(可能是相互身份验证)。 HTTPS 协议是 HTTP 和 SSL 的混合。好的一面是 HTTPS 有现有的实现,尤其是在 C# 中。最容易实现和调试的代码是已经实现和调试过的代码。所以使用 HTTPS,你会活得更久,更快乐。

【讨论】:

【参考方案2】:

我明白你为什么要问这个问题。问题是 RSA 不像典型的块密码(如 AES 或 3DES)那样使用,它一次加密 8 个字节,一整天。 RSA 是一种数学运算,它返回除法的余数(模数)。回到小学,当你学习长除法时,记住余数永远不会大于除数:如果你将 20 除以 7,则余数是 6。无论你除以什么整数,余数都不能大于 7超过六个。

RSA 数学也是如此。例如,如果您使用的是 1024 位 RSA 公钥,则余数永远不会大于 2^1024,即只有 128 个字节。因此,您一次只能使用此密钥加密 128 个字节。 (这就是我们通过位数来衡量 RSA 密钥大小的原因之一。)

从技术上讲,您可以在循环中使用此 RSA 密钥一次加密 128 字节的数据块。实际上,我们几乎从不这样做,因为 RSA 数学又大又慢。相反,我们使用所谓的“两阶段”加密。我们使用 RSA 仅加密一个简短的“会话密钥”,然后在快速对称密钥块密码(如 AES)中使用该会话密钥来加密实际数据。

整个协议是:

    获取目标的 RSA 公钥。这通常嵌入在证书中;如果是,请务必验证证书以确保密钥是真实的。假设 RSA 密钥的长度为 2048 位。 生成一个加密性强的伪随机数,用作块密码的密钥(例如,您需要 256 位作为 AES-256 的密钥。)请注意,256 使用 RSA 2048 位公钥加密会话密钥。它将为您提供 2048 位的加密会话密钥。请注意,此操作非常缓慢。 使用 AES-256 加密所有秘密数据,使用会话密钥。请注意,这比第 3 步要快得多。 将证书中的公钥 ID、RSA 加密会话密钥和 AES 加密数据捆绑在一起。我还会用格式标识符和版本号标记它,这样你就知道它是什么格式以及如何解密它。

    将捆绑包发送到目的地。

    在目的地,您使用格式标识符和版本来拆分包。

    检索身份在公钥 ID 字段中的私钥。 在 RSA 中使用此私钥解密会话密钥。 使用 AES 中的会话密钥解密数据。

如果您打算这样做,您应该知道这正是 CMS (PKCS#7) 格式的用途。我鼓励您了解并采用该标准,而不是尝试发明自己的格式。微软的 CSP 支持它,所以应该很容易。

如果您不遵循标准,您将不得不自行决定“在 RSA 加密过程中 AES 密钥位应采用什么格式?”更有可能的是,您几乎肯定会犯安全错误,从而削弱您的系统。此外,如果您不遵循标准,您会发现 CSP 等工具将很难使用。

【讨论】:

对于标准做法,您是否需要在上述步骤之一中使用私钥对其进行“签名”? @Dennis,没有。您可能提到的任何“标准实践”都是需求驱动的,而不是自动绑定到每个通信中。签名用于验证消息来自真实来源,而不是为了保密。如果真实性是一个有效的要求,那么上面的问题中没有说明。如果真实性和数据完整性很重要,那么您将在创建消息时添加额外的签名步骤,并在收到时验证签名。到那时,您已经超越了普通旧 RSA 的范围,并坚定地进入了 HMAC 领域。【参考方案3】:

在 DecryptRSA 中,“数据”是 64 位编码的吗?如果是,您必须先撤消它。

老实说,我认为您不应该自己实施该例程来保护“敏感的财务信息”,除非您有丰富的密码学经验。犯错的方法太多了。最好使用一些现成的解决方案 - 也许是 SSL 和证书,或者只是 PGP 或 GnuPG?

【讨论】:

是的,它是 base64 编码的,可能会像你们建议我的那样使用,像 SSL 这样的镭射解决方案【参考方案4】:

RSA 主要用于验证数据的安全散列 - 而不是加密数据本身。因此,给定大量数据,您可以使用 SHA512 创建该数据的哈希,然后使用 RSA 对该哈希签名

您需要对大型数据块使用对称加密算法,例如 AES 或 3DES。

管理安全交易并不容易,真的应该留给那些日日夜夜思考它的人。如果您通过网络公开服务,只需使用已经加密和保护您数据的 SSL。

【讨论】:

感谢 Paul,可能只使用 SSL,但只是想尝试使用加密。【参考方案5】:

首先确定您要保护的内容。如果您使用私钥“加密”某些内容,那么任何人都可以使用公钥“解密”它,因为公钥是 - 嗯 - public

如果你真的想签名,你应该(正如 Paul Alexander 解释的那样)用私钥对哈希进行签名,然后可以在服务器上进行验证。

要使用 RSA 加密数据,您应该首先生成一个随机对称密钥 (fx AES),使用 public 密钥加密密钥,然后使用对称密钥加密数据.然后,您可以将加密的密钥与加密的数据一起传输给 private 密钥的持有者,后者可以先用私钥解密加密的密钥,然后再用对称密钥解密数据。

您也可以考虑使用 SSL,但请记住仔细考虑身份验证。您可能需要客户端身份验证,并且必须决定信任哪些证书(您不应盲目接受 Verisign 颁发的任何证书)。

【讨论】:

我已经意识到我最终会使用 AES 或 3DES 或某种类似的加密。我理解你所说的关于 SSL 的内容。我想知道加密敏感数据并使用 SSL 会不会过大?

以上是关于.NET 中的 RSA 加密/解密问题的主要内容,如果未能解决你的问题,请参考以下文章

如何解密 .NET 中的 RSA 内容?

使用 RSA/ECB/OAEPWithSHA-256AndMGF1Padding 的 Java 加密无法在 .NET 中解密

Python中的RSA加密和解密

.NET Core加解密实战系列之——RSA非对称加密算法

python中的RSA加密与解密

Android中的RSA加密解密