问题 RSA块的数据太多失败。什么是PKCS#7?


谈论 javax.crypto.Cipher

我试图使用加密数据 Cipher.getInstance("RSA/None/NoPadding", "BC") 但我得到了例外:

ArrayIndexOutOfBoundsException:RSA块的数据太多

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

我在google上发现了一些关于“RSA / CBC / PKCS#7”的内容,这是什么“PKCS#7”?为什么它没有列出 sun的标准算法名称

更新:

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

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") 它引发了同样的异常。


8700
2018-04-05 15:08


起源

布里托我没有仔细阅读这个问题。我会补充一下作为答案。现在见下文。 - ig0774
我更新了我的答案,对您的更新进行了合理的解释。 - Alexander Torstling


答案:


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

为了加密任意长度的数据,首先需要将数据填充到块长度的倍数。这可以通过任何方法完成,但有许多标准。 PKCS7是一个很常见的,你可以看到一个概述 关于填充的维基百科文章

由于块cipers在块上运行,因此您还需要提供一种连接加密块的方法。这非常重要,因为天真的技术大大降低了加密的强度。还有一个 维基百科关于此的文章

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

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

更新:

作为对您的更新和GregS评论的回应,我想承认GregS是对的(我不知道这个关于RSA),并详细说明:

RSA不对位进行操作,它对整数进行操作。因此,为了使用RSA,您需要将字符串消息转换为整数m: 0 < m < n,哪里 n 是在生成过程中选择的两个不同质数的模数。 RSA算法中密钥的大小通常是指 n。有关这方面的更多详细信息,请参阅 关于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 <beef_16 <d46f473a2d746537de2056ae3092c451_16开始,您的加密将成功。它应该以低于d46f473a2d746537de2056ae3092c451_16的任何数字成功。

这是在提到的 bouncycastle常见问题。他们还说明了以下内容:

随附的RSA实施   Bouncy Castle只允许   加密单个数据块。   RSA算法不适合   流数据,不应使用   那样。在这样的情况下你   应该使用a加密数据   随机生成密钥和对称   密码,之后你应该加密   使用RSA随机生成的密钥,   然后发送加密数据和   加密的随机密钥到另一个   结束他们可以扭转过程   (即,使用解密随机密钥   他们的RSA私钥然后解密   数据)。


11
2018-04-05 15:34



拜托,看看我的更新 - Tom Brito
它比你的解释复杂一点。无论使用何种填充方案,RSA加密提供商都不会加密多个块的数据。对于RSA,“块”是小于模数的正整数,而不是一定数量的位或字节。 - James K Polk
您可能需要注意Java调用PKCS#7-padding“PKCS5Padding”。 - Rasmus Faber
格雷格 - 谢谢。我读了它并更新了我的答案。如果信息不正确,请随时评论。 - Alexander Torstling


RSA是具有约束的一次性非对称加密。它一次加密单个“消息”,但消息必须符合基于公钥大小的相当严格的限制。对于典型的1024位RSA密钥,最大输入消息长度(使用RSA中描述的RSA) PKCS#1 标准)是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中),但是 充气城堡 有一些CMS的代码。


4
2018-04-06 11:09





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


1
2018-05-18 19:47





RSA只能用于加密,当用于加密的比特数大于您要加密的事物的大小+ 11字节时

公钥加密标准 - PKCS


0
2018-04-05 15:12



拜托,看看我的更新 - Tom Brito


向下滚动一下,你会看到它。它不是一个密码算法(如RSA)或像CBC这样的密码模式,而是描述证书被编码为字节的方式(即数据结构语法)。你可以找到它的规格 这里


0
2018-04-05 16:57



拜托,看看我的更新 - Tom Brito


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

描述

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

使用 java.security.cert.CertificateFactory 要么 CertPath 使用时 PKCS7


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

到目前为止,我可以告诉你这一切。


0
2018-04-05 15:19



拜托,看看我的更新 - Tom Brito
检查更新的响应。 - Buhake Sindi


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


0
2018-04-06 07:35