获取 IllegalBlockSizeException:使用 rsa 时数据不得超过 256 个字节

Posted

技术标签:

【中文标题】获取 IllegalBlockSizeException:使用 rsa 时数据不得超过 256 个字节【英文标题】:getting a IllegalBlockSizeException: Data must not be longer than 256 bytes when using rsa 【发布时间】:2012-04-17 22:14:18 【问题描述】:

我正在使用 rsa 密钥加密一个长字符串,我将发送到我的服务器(将使用服务器的公钥和我的私钥对其进行加密)但它会引发类似 javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes 的异常 我觉得到目前为止我还没有正确理解 rsa 的工作原理(使用内置库是造成这种情况的原因)。 有人可以解释一下为什么抛出这个异常。是不是根本不可能发送加密的长字符串?

【问题讨论】:

只要使用HTTPS,加密就透明了。 【参考方案1】:

RSA 算法只能加密具有最大字节长度的数据 RSA 密钥长度的位数除以 8 减去 11 填充 字节数,即最大字节数 = 密钥长度(位)/8 - 11。

所以基本上你将密钥长度除以 8 -11(如果你有填充)。例如,如果您有一个 2048 位密钥,您可以加密 2048/8 = 256 个字节(如果有填充,则为 11 个字节)。因此,要么使用更大的密钥,要么使用对称密钥加密数据,然后使用 rsa 加密该密钥(这是推荐的方法)。

这将要求您:

    生成对称密钥 使用对称密钥加密数据 使用 rsa 加密对称密钥 发送加密密钥和数据 用 rsa 解密加密的对称密钥 使用对称密钥解密数据 完成:)

【讨论】:

为什么会有这样的限制,只能加密一定长度的数据? 限制key length in bits / 8 - 11仅在使用PKCS1Padding时有效。例如,NoPadding 的限制将是 key length in bits / 8 PKCS#1v1.5 padding 和 no padding 都是不安全的。使用 OAEP 填充(将块大小减少 11 个字节以上) 优秀。这个答案帮助我加密了大量数据。example 这是正确答案。此外,Waleed Abdalmajeed 提供了一个很好的例子:-)【参考方案2】:

根据@John Snow 的回答,我做了一个例子

    生成对称密钥(128 位 AES)

    KeyGenerator generator = KeyGenerator.getInstance("AES");
    generator.init(128); // The AES key size in number of bits
    SecretKey secKey = generator.generateKey();
    

    使用 AES 加密纯文本

    String plainText = "Please encrypt me urgently..."
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
    

    使用 RSA 公钥加密密钥

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(2048);
    KeyPair keyPair = kpg.generateKeyPair();
    
    PublicKey puKey = keyPair.getPublic();
    PrivateKey prKey = keyPair.getPrivate();
    
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.PUBLIC_KEY, puKey);
    byte[] encryptedKey = cipher.doFinal(secKey.getEncoded()/*Seceret Key From Step 1*/);
    

    发送加密数据(byteCipherText)+加密AES密钥(encryptedKey)

    在客户端,使用 RSA 私钥解密对称密钥

    cipher.init(Cipher.PRIVATE_KEY, prKey);
    byte[] decryptedKey = cipher.doFinal(encryptedKey);
    

    使用解密的对称密钥解密密文

    //Convert bytes to AES SecertKey
    SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES");
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.DECRYPT_MODE, originalKey);
    byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
    String plainText = new String(bytePlainText);`
    

【讨论】:

第3步有错吗?我没有看到“secKey”被加密而不是 Cipher 被初始化,因为 RSA 正在加密加密的数据。 它说数据不能超过256字节 @MrDumb 你能提供更多细节吗?在哪一步?【参考方案3】:

您不应该直接对您的秘密数据使用 RSA。您应该只对伪随机完全随机数据使用RSA,例如会话密钥或消息验证码。

您在 256 字节处遇到了问题——那是因为您可能正在使用 2048 位密钥。密钥能够将02^2048 - 1 范围内的任何整数加密到相同的范围内,这意味着您的数据必须为 256 字节或更小。

如果您打算加密更多,请使用一种 RSA 加密来加密对称算法的会话密钥,并使用 那个 来加密您的数据。

【讨论】:

为什么会有这样的限制,只能加密一定长度的数据? 因为 RSA 是在 有限的 ring 上执行的,所以唯一存在的数字是 [0, 2^2048-1] 范围内的整数,包括在内。任何长度超过 2048 位的消息都表示超出此范围的数字,并且必须在两个块中进行编码,或者 - 如果您想要安全 - 整个消息应该在会话密钥中加密。实际部署的 RSA 必须防止 multiple attacks,并且永远不要处理“原始”明文是安全使用 RSA 的重要部分。【参考方案4】:

按照上面 John Snow 的回答,我创建了一个简单的随机对称加密库,您可以使用该库使用私钥简单地加密任意长度的数据。

你可以在GitHub - random-symmetric-crypto找到图书馆

 final RandomSymmetricCipher cipher = new RandomSymmetricCipher();

 // Encrypt the data and the random symmetric key.
 final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64);

 // Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end.
 final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter();
 final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket);
 System.out.println("Base64EncryptedData=" + base64EncryptedData);

 // Decrypt the Base64 encoded (and encrypted) String.
 final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64);

【讨论】:

我找不到您指定使用哪种填充模式的位置。您应该明确指定 OAEP 填充,因为其他常见的填充(如 PKCS#1v1.5 或根本没有填充)是不安全的。 很高兴接受拉取请求。你有一个 URL 来支持没有填充是不安全的吗? Boneh 对attacks against RSA 进行了很好的概述。 Crypto.se 也有 lots of questions about textbook RSA。我在您的代码的对称部分中找不到 MAC。这意味着您将容易受到主动攻击,例如填充预言机。旧且仍然流行的 PKCS#1v1.5 填充也容易受到主动攻击(除非您真的仔细解决这个弱点),即 Bleichenbacher 的攻击。 填充在 RandomSymmetricCipher 类中指定。它使用“DESede/CBC/PKCS5Padding”作为对称密码,使用“RSA/ECB/PKCS1Padding”作为公钥密码。改变它们是微不足道的。 ***.com/a/10935308/493682 列出了几个选项和更多链接。 因此,如果您有一个接收消息并直接解密它们的服务器,如果它无效则返回错误(一种非常常见的配置),攻击者可以使用该服务器来解密消息。有两种相关的攻击,针对未经身份验证的 CBC 的标准填充预言和针对具有 PKCS#1v1.5 填充的 RSA 的 Bleichenbacher。【参考方案5】:

您需要通过公钥拆分数据

int keyLength = publicKey.getModulus().bitLength() / 16;
String[] datas = splitString(data, keyLength - 11);
String mi = ""//the data after encrypted;
for (String s : datas) 
    mi += bcd2Str(cipher.doFinal(s.getBytes()));

return mi;


public static String bcd2Str(byte[] bytes) 
    char temp[] = new char[bytes.length * 2], val;

    for (int i = 0; i < bytes.length; i++) 
        val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
        temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

        val = (char) (bytes[i] & 0x0f);
        temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
    
   return new String(temp);

【讨论】:

仅仅因为你可以做某事并不意味着你应该这样做。需要的是混合加密,其中数据使用对称算法(例如 AES)使用随机密钥加密,密钥使用 RSA 加密。 注意事项: 1、“data”为复数形式,“datum”为单数形式。 2.bcd2Str方法确实是在进行十六进制编码[0-9A-Z]。 3. RSA加密可能不需要对二进制输出进行编码,如果需要编码,十六进制也可以,通常情况下使用Base64编码,更紧凑。

以上是关于获取 IllegalBlockSizeException:使用 rsa 时数据不得超过 256 个字节的主要内容,如果未能解决你的问题,请参考以下文章

java反射获取属性值

Shell 获取路径

iOS 获取文件大小

根据日期字符串获取星期几,日期获取星期,时间获取星期,js获取星期

js如何获取时间点?

iOS 获取设备的各种信息的方法