58

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

4

5 回答 5

85

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

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

这将要求您:

  1. 生成对称密钥
  2. 使用对称密钥加密数据
  3. 使用 rsa 加密对称密钥
  4. 发送加密密钥和数据
  5. 使用 rsa 解密加密的对称密钥
  6. 使用对称密钥解密数据
  7. 完毕 :)
于 2012-04-04T08:14:37.343 回答
51

基于@John Snow的回答,我做了一个例子

  1. 生成对称密钥(128 位 AES)

    KeyGenerator generator = KeyGenerator.getInstance("AES");
    generator.init(128); // The AES key size in number of bits
    SecretKey secKey = generator.generateKey();
    
  2. 使用 AES 加密纯文本

    String plainText = "Please encrypt me urgently..."
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
    
  3. 使用 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*/);
    
  4. 发送加密数据(byteCipherText)+加密AES密钥(encryptedKey)

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

    cipher.init(Cipher.PRIVATE_KEY, prKey);
    byte[] decryptedKey = cipher.doFinal(encryptedKey);
    
  6. 使用解密的对称密钥解密密文

    //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);`
    
于 2017-10-19T11:02:19.853 回答
15

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

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

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

于 2012-04-04T08:37:22.527 回答
3

为了继续上面 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);
于 2013-12-07T14:26:15.970 回答
-3

您需要通过 publicKey 拆分数据

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);
}
于 2016-07-19T03:26:42.563 回答