【问题标题】:How can one encrypt content using RSA for Chilkat in C# and decrypt it in Java?如何在 C# 中使用 RSA for Chilkat 加密内容并在 Java 中解密?
【发布时间】:2019-11-01 05:37:40
【问题描述】:

更新

我看到很多人觉得我的问题太长(因为要解释的内容很多),请阅读第一句话,然后认为我在看不到整个问题的情况下可能会走最糟糕的切线。如果问题不够清楚,请告诉我。我试图以最简单的方式浓缩它,而不是引起任何混乱。

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


背景

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

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

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

不幸的是,当我尝试使用它时,我收到“解密错误”。

这是一个示例设置。我已经生成了我自己的个人公钥和私钥对。在我的 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();

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

示例:

以下是控制台窗口中的示例值:

加密内容

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

公钥

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

网站公钥解密尝试

现在,当我访问网站(如上所述)时,我将我的加密内容粘贴到加密内容文本块中,并将我生成的公钥粘贴在它下面的文本块中,并将 RSA 密钥类型设置为 @ 987654333@。但它失败了。


第二次尝试

但是……

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


我注意到了什么

因此,我开始阅读该网站必须提供的内容,并且该页面的作者发布了有关如何完成此操作的说明 (https://www.devglan.com/java8/rsa-encryption-decryption-java),它使用了 Java RSA 库。显然,有两种 Java RSA 密码可以使用“RSA”和“RSA/ECB/PKCS1Padding”。

我对 Java 库不是很熟悉,而且我对密码学的了解足以知道如何完成工作,但是还有很多技术方面我仍然不清楚,以帮助我弄清楚下一步该去哪里。


问题

我的问题是,我需要在 Chilkat 中设置什么,以便它可以加密允许 Java 应用程序(如上面发布的网站链接)能够解密的内容? (当然 Chilkat 也需要能够解密)

【问题讨论】:

  • 您的问题不清楚。首先,您说解密返回错误,然后为此显示图像。然后您说解密有效,并为此显示图像。那么它是哪一个?另外,为什么不提供您的解密代码以及显示错误的示例私钥和输入?
  • 对任何混淆和匆忙写原件表示歉意。感谢您的建议和建议。它已被重写,包含更多信息。我希望一切都更清楚。
  • 能够使用外部工具验证数字签名(使用公钥解密签名)至关重要。 Chikat 可以成功加密和解密是可以的,但这还不够,它加密的另一个加密服务必须能够解密以证明签名的有效性。对结果感兴趣,有人可以帮忙吗?

标签: cryptography chilkat


【解决方案1】:

使用 Chilkat 库使用私钥加密的消息无法使用 Java(至少不能使用标准 SunJCE 提供程序)或基于 java 的网站使用公钥解密,因为 不同的两边都使用了填充变体。

成功解密的先决条件是加密和解密都使用相同的填充变体。这同样适用于签名和验证。

RFC8017中描述的PKCS1-v1.5-padding有两种变体:一种是RSAES-PKCS1-v1_5,用于加解密的上下文中,另一种是RSASSA-PKCS1-v1_5,用于在签名和验证的上下文中。 RSAES-PKCS1-v1_5 是非确定性的,即用相同的密钥重复加密相同的明文总是会产生不同的密文。 RSASSA-PKCS1-v1_5 是确定性的,即在上述条件下总是生成相同的密文。

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

  • Chilkat 在加密/解密上下文中提供的方法使用 RSAES-PKCS1-v1_5,无论是使用公钥还是私钥进行加密。模拟方法也存在于签名/验证的上下文中。这些使用 RSASSA-PKCS1-v1_5。 要检查这一点,可以通过将Chilkat.Rsa#NoUnpad 标志设置为true 来确定填充变量,以便在解密期间不会删除填充。测试的另一种选择是使用相同的密钥重复加密相同的明文。由于 RSAES-PKCS1-v1_5 是概率性的,因此每次生成不同的密文。

  • 在 Java 中,Cipher-class 根据模式(加密或解密)和使用的密钥类型(私有或公共)确定使用哪个填充变体。对于使用公钥加密和使用私钥解密,使用 RSAES-PKCS1-v1_5。对于使用私钥加密和使用公钥解密,使用 RSASSA-PKCS1-v1_5。对于签名/验证,Java 提供了使用 RSASSA-PKCS1-v1_5 的 Signature-class。 要检查这一点,请按上述步骤进行。在 Java 中,您可以在解密期间使用RSA/ECB/NoPadding 防止填充被删除。

由于在加密/解密上下文中,公钥用于加密,私钥用于解密,并且在签名/验证上下文中使用专用类或方法,因此没有或很少有使用案例直接用私钥加密,用公钥解密。此外或者可能是因为这些过程在库中没有统一实现,正如您在 Chilkat 库和 Java 的示例中看到的那样。

Chilkat 库和 Java 总共可以区分三种情况:

  • 相同 库/语言中,可以使用公钥或私钥执行加密,并使用相应的对应方进行解密。由于这个原因,网站上的加密和解密(使用 Java)在发布的示例 Second Attempt 中工作:使用私钥加密和使用公钥解密都使用 RSASSA-PKCS1-v1_5 .
  • 如果在 Chilkat 代码中公钥用于加密,而在 Java 中私钥用于解密,则 RSAES-PKCS1-v1_5 用于加密和解密,这就是解密工作的原因。
  • 但是,如果在 Chilkat 代码中使用私钥进行加密,而在 Java 中使用公钥进行解密,则 RSAES-PKCS1-v1_5 用于加密,而 RSASSA-PKCS1-v1_5 用于解密。因此,两种填充变体 不同 并且解密失败。这对应于问题中描述的场景。

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

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

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

【讨论】:

  • Topaco,非常感谢您的回答。尽管这不是我所希望的,但它非常全面,引导我走向正确的方向,对此我很感激。它解释了这么多。我实际上尝试使用 Chilkat Sign 操作,并且在网站上它实际上解密了签名,但是以某种不能很好显示的二进制表示形式(正如预期的那样,但它只是为了测试你提到的内容)但它证明了Java Cypher 类能够解密它。
  • 不幸的是,这意味着使用在线工具进行验证将不起作用。因此,我将做的可能是找到一种用于签名验证的离线方法(如 OpenSSL 命令)并更新我的应用程序以改为使用标准的签名/验证操作。
  • 我同意这一点,但要注意一点:使用 Chilkat 或 Java 创建的签名验证没有问题,即使是在线,例如here。为了确定散列值,解密签名在线可能是不可能的(或只有困难),因为这需要一个允许使用解密的网站。 >public 键,使用正确的填充并以合适的编码(例如 Base64 或十六进制)显示结果。 devglan-website 非常接近这一点,只是因为最后提到的条件而失败。
  • 我忘记了那个网站。我不知道他们有签名验证工具。我已经使用我用来验证 OpenSSL 的相同测试数据对其进行了测试,并且它正在工作。你是个明星!感谢大家的帮助!
【解决方案2】:

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

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

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

Chilkat 的 API 提供了以相反方式使用公钥/私钥的能力,这没有任何意义,但之所以需要,是因为“外面”有一些系统在做没有意义的事情,而 Chilkat 是需要执行相反的操作。 (加密任何人都可以解密的东西是没有意义的。)

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

或者..您可以改为使用 Chilkat 创建一个“不透明签名”,这是一个包含数据的签名,然后找到 devglan 在线工具来验证/从 PKCS7 签名中提取数据(如果存在 devglan 工具) .这样您就可以保留您的私钥并将公钥提供给收件人。

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

【讨论】:

  • I think the code behind the devglan website is not capable of doing the RSA encrypt/decrypt in the opposite way. You would need to encrypt using your public key, and then give your private key to the other person. 在我的问题的中间部分,您会看到我使用 Chilkat 生成的密钥以另一种方式实现它
  • 现在更有意义了吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-24
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 2010-11-11
  • 2015-07-25
  • 2013-03-17
相关资源
最近更新 更多