【问题标题】:Too much data for RSA block fail. What is PKCS#7?RSA 块的数据过多失败。什么是 PKCS#7?
【发布时间】:2011-02-04 11:10:33
【问题描述】:

javax.crypto.Cipher

我尝试使用 Cipher.getInstance("RSA/None/NoPadding", "BC") 加密数据,但出现异常:

ArrayIndexOutOfBoundsException: RSA 块的数据过多

看起来与“NoPadding”有关,因此,阅读有关填充的内容,看起来 CBC 是在这里使用的最佳方法。

我在谷歌上找到了一些关于“RSA/CBC/PKCS#7”的东西,这个“PKCS#7”是什么?为什么它没有在sun's standard algorithm names 上列出?

更新:

我想知道,如果是填充问题,为什么这个例子运行得很好?

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

/**
 * Basic RSA example.
 */
public class BaseRSAExample
{
    public static void main(
        String[]    args)
        throws Exception
    {
        byte[]           input = new byte[] { (byte)0xbe, (byte)0xef };
        Cipher          cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
        KeyFactory       keyFactory = KeyFactory.getInstance("RSA", "BC");

        // create the keys

        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
                new BigInteger("d46f473a2d746537de2056ae3092c451", 16),
                new BigInteger("11", 16));
        RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(
                new BigInteger("d46f473a2d746537de2056ae3092c451", 16),  
                new BigInteger("57791d5430d593164082036ad8b29fb1", 16));

        RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec);
        RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(privKeySpec);

        // encryption step

        cipher.init(Cipher.ENCRYPT_MODE, pubKey);

        byte[] cipherText = cipher.doFinal(input);

        // decryption step

        cipher.init(Cipher.DECRYPT_MODE, privKey);

        byte[] plainText = cipher.doFinal(cipherText);

    }
}

更新 2:

我意识到,即使我只使用Cipher.getInstance("RSA", "BC"),它也会引发相同的异常。

【问题讨论】:

  • brito 我没有仔细阅读这个问题。我会添加作为答案。现在看下面。
  • 我更新了我的答案,对您的更新做出了合理的解释。

标签: java security cryptography


【解决方案1】:

RSA 只能用于加密,当用于加密的位数大于您要加密的内容的大小 + 11 个字节时

公钥加密标准 - PKCS

【讨论】:

    【解决方案2】:

    PKCS#7 已列出(参考您的链接)。它的编码是PKCS7

    说明

    一个 PKCS#7 SignedData 对象,带有 唯一重要的领域是 证书。

    使用PKCS7时使用java.security.cert.CertificateFactoryCertPath


    RSA 是一种分组密码。它加密相同密钥大小的块。 因此,如果您尝试加密一个块,BouncyCastle RSA 会给出异常 比密钥长。

    到目前为止,我只能告诉你这些。

    【讨论】:

      【解决方案3】:

      如果您使用分组密码,您的输入必须是分组位长度的精确倍数。

      为了加密任意长度的数据,您首先需要将数据填充到块长度的倍数。这可以通过任何方法完成,但有许多标准。 PKCS7 是比较常见的一种,可以看概述on the wikipedia article on padding

      由于块密码器对块进行操作,因此您还需要想出一种连接加密块的方法。这非常重要,因为幼稚的技术大大降低了加密的强度。还有一个wikipedia article on this

      您所做的是尝试加密(或解密)长度与密码的块长度不匹配的数据,并且您还明确要求不进行填充和链接操作模式。

      因此,分组密码无法应用于您的数据,并且您收到了报告的异常。

      更新:

      作为对您的更新和 GregS 评论的回应,我想承认 GregS 是对的(我对 RSA 不了解),并详细说明一下:

      RSA 不对位进行操作,而是对整数进行操作。因此,为了使用 RSA,您需要将字符串消息转换为整数 m:0 < m < n,其中n 是生成过程中选择的两个不同素数的模数。 RSA 算法中密钥的大小通常是指n。有关这方面的更多详细信息,请访问wikipedia article on RSA

      将字符串消息无损失地转换为整数的过程(例如截断初始零),通常遵循PKCS#1 标准。此过程还为消息完整性(哈希摘要)、语义安全(IV)等添加了一些其他信息。有了这些额外的数据,可以提供给 RSA/None/PKCS1Padding 的最大字节数是 (keylength - 11)。我不知道 PKCS#1 如何将输入数据映射到输出整数范围,但是 我的印象是它可以输入小于或等于 keylength - 11 的任何长度,并为 RSA 加密生成一个有效的整数。

      如果您不使用填充,您的输入将被简单地解释为一个数字。您的示例输入 {0xbe, 0xef} 很可能会被解释为 {10111110 +o 11101111} = 1011111011101111_2 = 48879_10 = beef_16(原文如此!)。由于 0

      bouncycastle FAQ 中提到了这一点。他们还声明了以下内容:

      附带的 RSA 实现 充气城堡只允许 加密单个数据块。 RSA算法不适合 流数据,不应使用 那样。在这种情况下你 应该使用加密数据 随机生成的密钥和一个对称的 密码,之后你应该加密 使用 RSA 随机生成的密钥, 然后发送加密数据和 对方的加密随机密钥 结束他们可以逆转过程的地方 (即使用解密随机密钥 他们的 RSA 私钥,然后解密 数据)。

      【讨论】:

      • 比你的解释要复杂一点。无论使用何种填充方案,任何 RSA 加密提供商都不会加密超过一个块的数据。对于 RSA,“块”是小于模数的正整数,而不是一定数量的位或字节。
      • 您可能需要注意 Java 调用 PKCS#7-padding "PKCS5Padding"。
      • 格雷格 - 谢谢。我阅读了它并更新了我的答案。如果信息不正确,请随时发表评论。
      【解决方案4】:

      向下滚动一下,您就会看到它。它不是密码算法(如 RSA)或密码模式(如 CBC),而是对证书编码为字节的方式的描述(即数据结构语法)。你可以找到它的规范here

      【讨论】:

        【解决方案5】:

        您不应直接使用 RSA 加密您的数据。使用随机对称密钥(即 AES/CBC/PKCS5Padding)加密您的数据,并使用 RSA/None/PKCS1Padding 加密对称密钥。

        【讨论】:

          【解决方案6】:

          RSA 是一种带有约束的一次性非对称加密。它一次性加密单个“消息”,但消息必须符合基于公钥大小的相当严格的限制。对于典型的 1024 位 RSA 密钥,最大输入消息长度(使用PKCS#1 标准中描述的 RSA)为 117 个字节,仅此而已。此外,使用这样的密钥,加密消息的长度为 128 字节,而与输入消息的长度无关。 RSA作为一种通用的加密机制,效率很低,而且浪费网络带宽。

          对称加密系统(例如 AES 或 3DES)效率更高,并且它们带有“链接模式”,允许它们处理任意长度的输入消息。但它们不具备 RSA 的“非对称”属性:使用 RSA,您可以公开加密密钥而不泄露解密密钥。这就是 RSA 的全部意义所在。使用对称加密,有权加密消息的人也拥有解密消息所需的所有信息,因此您不能公开加密密钥,因为它也会公开解密密钥。

          因此,习惯上使用混合系统,其中(大)消息使用对称密钥(例如,AES)进行对称加密(使用对称密钥,即随机字节的任意短序列),并具有使用 RSA 加密的密钥。接收方然后使用 RSA 解密来恢复该对称密钥,然后使用它来解密消息本身。

          除了上面相当简单的描述之外,加密系统,尤其是混合系统,充满了一些小细节,如果不加以注意,可能会使您的应用程序对攻击者的攻击非常弱。因此,最好使用具有已经处理所有这些艰苦工作的实现的协议。 PKCS#7 就是这样一个协议。如今,它被标准化为CMS。它用于多个地方,例如S/MIME(加密和签名电子邮件的标准)的核心。另一个用于加密网络流量的著名协议是 SSL(现在以 TLS 的名义标准化,并且经常与 HTTP 结合使用作为著名的“HTTPS”协议)。

          Java 包含 SSL 的实现(请参阅javax.net.ssl)。 Java 不包含 CMS 实现(至少在其 API 中不包含),但 Bouncy Castle 有一些 CMS 代码。

          【讨论】:

            【解决方案7】:

            此错误表示输入数据大小大于密钥模数大小。您将需要更大的密钥大小来加密数据。如果更改密钥长度不是一种选择,或者您可能需要调查您是否真的期望大输入数据。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-04-27
              • 1970-01-01
              • 2021-10-11
              • 1970-01-01
              • 1970-01-01
              • 2019-03-17
              • 2017-03-27
              相关资源
              最近更新 更多