谈论javax.crypto.Cipher
我试图使用Cipher.getInstance("RSA/None/NoPadding", "BC")
加密数据,但我得到了例外:
ArrayIndexOutOfBoundsException: too much data for RSA block
看起来与“NoPadding”有关,所以,阅读填充,看起来像CBC是这里使用的最佳方法。
我在google上发现了一些关于“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")
,它也会抛出相同的异常。
答案 0 :(得分:11)
如果使用分组密码,则输入必须是块比特长度的精确倍数。
为了加密任意长度的数据,首先需要将数据填充到块长度的倍数。这可以通过任何方法完成,但有许多标准。 PKCS7非常常见,您可以看到概述on the wikipedia article on padding。
由于块cipers在块上运行,因此您还需要提供一种连接加密块的方法。这非常重要,因为天真的技术大大降低了加密的强度。还有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 <0 beef_16&lt; d46f473a2d746537de2056ae3092c451_16,您的加密将成功。它应该以低于d46f473a2d746537de2056ae3092c451_16的任何数字成功。
bouncycastle FAQ中提到了这一点。他们还说明了以下内容:
随附的RSA实施 Bouncy Castle只允许 加密单个数据块。 RSA算法不适合 流数据,不应使用 那样。在这样的情况下你 应使用a加密数据 随机生成的密钥和对称的 密码,之后你应该加密 使用RSA随机生成的密钥, 然后发送加密数据和 加密的随机密钥到另一个 结束他们可以扭转这个过程 (即,使用解密随机密钥 他们的RSA私钥然后解密 数据)。
答案 1 :(得分:4)
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代码。
答案 2 :(得分:1)
此错误表示输入数据大小大于密钥模数大小。您需要更大的密钥大小来加密数据。如果不能更改密钥长度,或者您可能需要调查是否真的期望大输入数据。
答案 3 :(得分:0)
RSA只能用于加密,当用于加密的位数大于你要加密的东西的大小+ 11个字节时
公钥加密标准 - PKCS
答案 4 :(得分:0)
列出了PKCS#7(指你的链接)。它的编码是PKCS7
说明
PKCS#7 SignedData对象,带有 只有重要的领域 证书。
使用java.security.cert.CertificateFactory
时使用CertPath
或PKCS7
。
RSA是分组密码。它加密相同密钥大小的块。 因此,如果您尝试加密块,BouncyCastle RSA会出现异常 这比密钥大小要长。
到目前为止,我只能告诉你。
答案 5 :(得分:0)
向下滚动一下,你会看到它。它不是一个密码算法(如RSA)或像CBC这样的密码模式,而是描述证书被编码为字节的方式(即数据结构语法)。您可以找到它的规范here。
答案 6 :(得分:0)
您不应直接使用RSA加密数据。使用随机对称密钥(即AES / CBC / PKCS5Padding)加密数据,并使用RSA / None / PKCS1Padding加密对称密钥。