如何在 C# 中使用 RSA for Chilkat 加密内容并在 Java 中解密?

How can one encrypt content using RSA for Chilkat in C# and decrypt it in Java?

更新

我看到很多人觉得我的问题太长(因为有很多东西要解释),阅读第一句话然后就认为我在看最糟糕的切线而没有看到整个问题。如果问题不够清楚,请告诉我。我试图以最简单的方式压缩它,而不是造成任何混淆。

public 密钥解密的原因是为了实现一种数字签名形式,在这种形式中,收件人对加密内容进行解密以显示哈希值。我认为没有必要在问题中提及这一点,因为我想了解如何以其基本形式执行此操作。但是,为了避免对 RSA 是什么以及 public 密钥解密不好的任何进一步担忧和警告,我用免责声明更新了我的问题。


背景

我编写了一个 C# 应用程序,它使用 Chilkat 的 RSA 库获取内容并使用个人私钥对其进行加密。

然后我想使用 public 网站允许某人使用关联的 public 密钥解密该内容(已加密)。

现在,我找到了一个允许您使用 RSA public 密钥解密内容的第 3 方网站(其中 不是 很多,顺便说一句) (https://www.devglan.com/online-tools/rsa-encryption-decryption).

不幸的是,当我尝试使用它时,我得到了 "Decrypt error"。

这是一个示例设置。我已经生成了我自己的个人 Public 和私钥对。在我的 C# 应用程序中,我获取一个字符串并使用私钥对其进行加密并使用 Base64 对其进行编码。

const string originalContent = "This !s original c0nt3nt";

var rsa = new Chilkat.Rsa();
rsa.GenerateKey(2048);

var encryptedBytes = rsa.EncryptBytes(Encoding.UTF8.GetBytes(originalContent), true);
var encryptedEncodedString = Convert.ToBase64String(encryptedBytes);
Console.WriteLine($"Encrypted:{Environment.NewLine}{encryptedEncodedString}");
Console.WriteLine();

var privateKeyBytes = rsa.ExportPrivateKeyObj().GetPkcs8();
var privateKeyEncodedString = Convert.ToBase64String(privateKeyBytes);
Console.WriteLine($"Private Key:{Environment.NewLine}{privateKeyEncodedString}");
Console.WriteLine();

var publicKeyBytes = rsa.ExportPublicKeyObj().GetDer(false);
var publicKeyEncodedString = Convert.ToBase64String(publicKeyBytes);
Console.WriteLine($"Public Key:{Environment.NewLine}{publicKeyEncodedString}");
Console.WriteLine();

var decyptedContentBytes = rsa.DecryptBytes(encryptedBytes, false);
var decryptedContentString = Encoding.UTF8.GetString(decyptedContentBytes);
Console.WriteLine($"Decrypted:{Environment.NewLine}{decryptedContentString}");
Console.WriteLine();

Console.WriteLine("Press ENTER to quit");
Console.ReadLine();

此示例控制台应用程序将写出流程的下一部分所需的所有必要信息,并证明其原则上按预期工作。

示例:

这是来自控制台的示例值 window:

加密内容

H5JTsGhune1n3WWSPjwVJuUwp70Hsh1Ojaa0NFCVyq0qMjVPMxnknexOG/+HZDrIYsZM7EnPulpmihJk4QyLM8T2KNQIhbWuMHvzgHYlcPJdXpGZhAxwfklL4HP0iRUUXJBsJcS/2XoUDZ6elUoMIFY9cDB4O+WFxKS/5vzLEukTLbQ3aEBNg3xaf9fg12F8LcMxZ3GDsk0W9b6oJci09NTxXd6KKes0RM1hnOhw6bu0U33ZLF3sa0nH9Kdf8w23PoKc/tl12Jsa8N1A4OjaT5910UF8FRH6OkAbNKnxqXcL7+V4HVuHchi3ghuFivAW57boLeHr7OG7wOEC/gfPOw==

私钥

MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8xYcNXckXf1X4Kd6qE5c7pddfWdKo71mcwZWskuaq+wq3FTcCTAedo/Vcx8Vxn+RMn5XE7QCDzcAAN0K/BzQsoU81myRzZ+bKP+TJ5HH0jClCUMj+ideEm0fay873jnbG0hKEOJPVxPWwKq3jvDLLmWrdgvd/UiDStDm286SFKfMlLWkSw8YIc5nXsthAgP0hv8Nj7UDKvTEG5o3boTuhG1JQARCEXP0fTdIiv0cEFlSN3KkgF4KDf32Vt2x57N/+PJXpQvcECkLwPpBAq/aM0qbtgeiILxavfBJRwQ5zXDUmZHepvSjK6KIYQsTavQQLDXnFKuXa2fxOJHIlys6pAgMBAAECggEBAI0ZMBtDkL2phj7aPP7vaclB6rvwzc9MKLVM1W2K2DPRNW8nwlhLMB4aoZnaELEfjGvhlPb/F7VtIyiGJbPX1J3PbP9qmVJRxWZDX+WwhaT+5xAUhkgMDDWoQ4s9b9QGfq2Z9BE0oPvWHraxEAz7bRRV9lTgQdK/Np2H7OPdNYn6SW8qVgAukgTBqVno4VDbC34bJwal0e63oBFFfensWlhPtDUQB/uQX7UiRfEkxL+CNuqVLDoAeXWmSVWOPlDTKVu1y1bzfA+WMOKHm1ndq21I07TUPf9FcgYdKf4yKpWvMfVeDev6Oo/2mlac+vrJO571S+h4a5m79jUhCeJwX4kCgYEA7q5hrNtMbErA6dgEOG+KpFTaeqbknwtcykVApEvHt4LKULedAvwkORu65acKFYkxbMt19Fx7ligGxg0yOQRWX1BXK1XOCo9eYOjvOVlbRqBywLIbegehoZQ0LoSsdRcOvFq7EbMV3BaxCmxgpnrCZ75VaCYUMzylIduPWKeT9xcCgYEAyngNIIgsXfpCI+HHILNpprFfS2JBBGPx8N/d9cXahKCJhxrMe8K64CSMyxTwum5DXjJnbE4QBsoowRZTCEF6JUBagRM/pQrVX/CK//oyUUaa5+1S/0OxlUevXR7TD6gcpGNEdPjruc+gZzhfKFuWh+V9mJQUviqm3RjAcEdHAD8CgYBL0kOfGM8vO5QK9R9qGiztxTLecbQAvihM7TD6wEQCjN7eQ2Xyc8zCA4gcujKe4sU7rWqcJODxs2drdPe2WyVhA/GdB5X7js3JdVXBXxx61C9//VRzMIds/9qPyH/MdnWs6hmxJrXUA7Vb/U+6sxacxD73ZdlW6XX/ynLAFAQSIwKBgQCk4i12j87p3ZMdW5HprJJeoNYFMwfVxnrSec1tiGoTVhWJxCZAp22+eaV7ARumB4OvY4bcKZpdnSahUEfgUkphqc3Kjd1nz7HCxsa7/YoarFAcjiXoIb2t30oNoLurZXGl4f1u8QQvNsnfJYZA/I1TMG4e4oEd+OgY6D5XcYR9ywKBgGdaZmoBieiw0NkbijjgQZ0WILDmrIYdsSp4HMp6XDeVvdMb/qYg2jTnvVyqMSb8NdfCOB0GT19r1isQX9RnUgxPikJbVLj8WjAQjHT28mtmRn+Ju/3KT75RJ/LHY3SySNMOgTW75X2u8v0ELdEiiOmc/vTkCYoS/oqp92ELjT1Y

PUBLIC 键

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvMWHDV3JF39V+CneqhOXO6XXX1nSqO9ZnMGVrJLmqvsKtxU3AkwHnaP1XMfFcZ/kTJ+VxO0Ag83AADdCvwc0LKFPNZskc2fmyj/kyeRx9IwpQlDI/onXhJtH2svO9452xtIShDiT1cT1sCqt47wyy5lq3YL3f1Ig0rQ5tvOkhSnzJS1pEsPGCHOZ17LYQID9Ib/DY+1Ayr0xBuaN26E7oRtSUAEQhFz9H03SIr9HBBZUjdypIBeCg399lbdseezf/jyV6UL3BApC8D6QQKv2jNKm7YHoiC8Wr3wSUcEOc1w1JmR3qb0oyuiiGELE2r0ECw15xSrl2tn8TiRyJcrOqQIDAQAB

网站PUBLIC密钥解密尝试

现在,当我访问网站(如上所述)时,我将加密内容粘贴到加密内容文本块中,然后粘贴我在其下方的文本块中生成的 public 密钥并设置将 RSA 密钥类型设置为 Public Key。但是它失败了。


第二次尝试

但是...

我已经使用我生成的个人私钥和 Public 密钥进行了一些故障排除,我使用该网站使用我的密钥执行加密和解密,我能够加密我的字符串并解密加密内容成功让我相信我的 Chilkat 加密设置与网站使用的设置不完全一致。


我注意到了什么

所以我开始阅读该网站必须提供的内容,该页面的作者发布了关于如何完成此操作的解释 (https://www.devglan.com/java8/rsa-encryption-decryption-java),其中使用了 Java RSA 库。显然,有两个 Java RSA 密码可以使用 "RSA" 和 "RSA/ECB/PKCS1Padding".

我不太熟悉 Java 库,我对密码学了解足够多,知道如何完成工作,但有很多技术方面我仍然不清楚,无法帮助我弄清楚下一步要去哪里。


问题

我的问题是,Chilkat 中是否有任何我需要设置的东西,以便它可以加密允许 Java 应用程序(如上面发布的网站 link)能够加密的内容解密? (当然奇尔卡特也需要能够解密)

我打算(希望)在阅读第一部分后回答这个问题。我到了你写的地步“......我正在使用一个字符串并用私钥......加密它”,这引发了危险信号。

Public 密钥加密应该是您使用收件人的 public 密钥加密的地方。私钥用于解密。加密的要点是只有预期的收件人才能解密和查看邮件。使用 public/private 密钥对,您可以将您的 public 密钥提供给任何人,但您独自拥有您的私钥。因此,任何人都可以使用您的 public 密钥来加密发给您的消息,但您是唯一可以解密的人。这是有道理的。

签名则相反:您使用您的私钥进行签名,任何人都可以使用您的 public 密钥进行验证。签名可以选择包含签名数据,以便验证签名的行为也提取原始数据。因此,您验证 (1) 数据只能由私钥持有者签名,(2) 数据未被修改,以及 (3) 您恢复了原始数据。

Chilkat 的 API 提供了以相反方式使用 public/private 键的能力,这没有任何意义,但是需要,因为有系统 "out there"没有意义的事情,需要 Chilkat 来执行相反的操作。 (加密任何人都可以解密的东西是没有意义的。)

我认为 devglan 网站背后的代码无法以相反的方式执行 RSA encrypt/decrypt。您需要使用 public 密钥加密,然后将您的私钥提供给其他人。

或者..您可以改为使用 Chilkat 创建一个 "opaque signature",这是一个包含数据的签名,然后找到 devglan 在线工具 verify/extract 来自 PKCS7 签名的数据(如果devglan 工具存在)。这样您就可以保留您的私钥并将 public 密钥提供给收件人。

最后.. 在我看来,您确实将 public/private 密钥视为共享秘密——即仅在发送方和接收方之间共享的秘密。在那种情况下,为什么还要费心使用 RSA? (请记住,RSA 仅适用于 encrypting/decrypting 少量数据。您可以加密的最大字节数等于密钥大小减去一些开销。因此,如果您有一个 2048 位密钥,那么您可以最大程度地加密2048/8 字节减去填充中使用的开销,大约为 20 字节左右。)如果在语义上你只有一个共享秘密,那么你可以简化并使用对称加密 (AES),其中秘密密钥只是一个随机一堆字节,你没有数据大小限制。

无法使用 public 密钥使用 Java(至少不能使用标准的 SunJCE 提供程序)或基于 java 的网站,因为两侧使用了 不同的 填充变体。

解密成功的前提是加密和解密使用相同的填充变体。这同样适用于签名和验证。

RFC8017: One is RSAES-PKCS1-v1_5, which is used in the context of encryption and decryption, and the other is RSASSA-PKCS1-v1_5 中描述的 PKCS1-v1.5-padding 有两种变体,用于签名和验证。 RSAES-PKCS1-v1_5 是非确定性的,即用相同的密钥对相同的明文重复加密总是会产生不同的密文。 RSASSA-PKCS1-v1_5是确定性的,即在上述条件下总是生成相同的密文。

由于填充变体取决于各自的 platform/library,因此无法进行一般性陈述。但是,对于 Chilkat-library 和 Java(标准 SunJCE 提供程序),以下内容适用(假定 PKCS1-v1.5-padding):

  • Chilkat 在encryption/decryption 上下文中提供的方法使用RSAES-PKCS1-v1_5,而不管是否使用public 或私钥进行加密。 signing/verifying 的上下文中也存在模拟方法。这些使用 RSASSA-PKCS1-v1_5。 要检查这一点,可以通过将 Chilkat.Rsa#NoUnpad 标志设置为 true 来确定填充变体,以便在解密期间不会删除填充。测试的另一种选择是使用相同的密钥重复加密相同的明文。由于RSAES-PKCS1-v1_5是概率性的,所以每次生成的密文都不一样

  • 在Java中,Cipher-class根据模式(加密或解密)和使用的密钥类型确定使用哪种填充变体(私有或 public)。对于使用 public 密钥加密和使用私钥解密,使用 RSAES-PKCS1-v1_5。对于使用私钥加密和使用 public 密钥解密,使用 RSASSA-PKCS1-v1_5。对于signing/verifying,Java提供了Signature-class,其中使用了RSASSA-PKCS1-v1_5。 要检查这一点,请按照上述说明进行操作。在 Java 中,您可以防止在解密过程中使用 RSA/ECB/NoPadding 删除填充。

由于在encryption/decryption的上下文中,public密钥用于加密,私钥用于解密,并且上下文中使用了专用的classes或方法在 signing/verifying 中,直接 使用私钥加密和使用 public 密钥解密的用例没有或很少。此外,或者可能正因为如此,正如您在 Chilkat 库和 Java.

示例中所见,这些过程并未在库中 统一 实现

Chilkat-library和Java:

一共可以区分三种情况
  • 相同 library/language中,可以使用public或私钥进行加密,并使用相应的对应项进行解密。出于这个原因,网站上的加密和解密(使用 Java)在发布的示例中有效 第二次尝试:使用私钥加密和使用public 键使用 RSASSA-PKCS1-v1_5.
  • 如果在 Chilkat 代码中 public 密钥用于加密,而在 Java 中私钥用于解密,则 RSAES-PKCS1-v1_5 用于加密和解密,这就是解密有效的原因。
  • 但是,如果在 Chilkat 代码中使用私钥进行加密,而在 Java 中使用 public 密钥进行解密,则 RSAES-PKCS1-v1_5 用于加密并且 RSASSA-PKCS1-v1_5用于解密。因此,两种填充变体 不同 并且解密失败。这与问题中描述的场景相对应。

在对你的问题进行解释之后:我的问题是,Chilkat 中是否有任何我需要设置的内容,以便它可以加密允许 Java 应用程序的内容(例如上面发布的网站 link)能够解密吗? 由于 Java-代码使用 RSASSA-PKCS1-v1_5 通过 public 密钥进行解密,这将是兼容性需要在 encryption/decryption 的上下文中将 Chilkat 代码中的填充变体从 RSAES-PKCS1-v1_5 更改为 RSASSA-PKCS1-v1_5。如果您查看 Chilkat's RSA-methods,这似乎不是故意的,但是确定填充变体的逻辑是硬编码的(可能与大多数库一样)。您只能在 PKCS1-v1.5-padding 和 OAEP 之间选择填充。这意味着无法使用 Java 中或网站上的 public 密钥解密使用 Chilkat 代码的私钥加密的消息。

有哪些选择?根据问题,目标是:public 密钥解密的原因是为了实现一种数字签名形式,接收者解密加密内容以显示哈希值。

  • 这里最好在 Chilkat 端创建一个标准签名,例如signBytes。数据的散列是自动创建的,RSASSA-PKCS1-v1_5 用作填充变体(如果数据已经散列,可以使用方法 signHash)。在Java端,这个签名可以验证。或者,可以使用 public 密钥 解密 签名,这允许确定哈希值,因为 Java 使用填充变体 RSASSA-PKCS1-v1_5在两个案例中。也可以在网站上解密,但解密后的数据无法正确显示,因为它们仅作为字符串给出(由于散列中的任意字节序列,它不会产生任何有意义的输出)并且无法更改编码到十六进制或 Base64。
  • 另一种可能性是在 Java 端也使用 Chilkat。可能 Chilkat 跨平台使用统一的逻辑(不过我没有验证)。