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

时间:2021-01-19 18:34:26

I am using rsa key to encrypt a long string which I will send to my server(will encrypt it with server's public key and my private key) But it throws an exception like javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes I feel that I have not understood the working of rsa properly till now(using the inbuilt libraries are the cause for this).
Can some one please explain why this exception is being thrown. Is it not at all possible to send long string encrypted?

我使用rsa密钥加密一个长字符串,我将发送到我的服务器(将使用服务器的公钥和我的私钥加密它)但它抛出一个异常,如javax.crypto.IllegalBlockSizeException:数据不得超过256字节我觉得到目前为止我还没有正确理解rsa的工作(使用内置库是导致这种情况的原因)。有人可以解释为什么抛出这个异常。是不是可以发送长字符串加密?

5 个解决方案

#1


52  

The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.

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

So basicly you divide the key length with 8 -11(if you have padding). For example if you have a 2048bit key you can encrypt 2048/8 = 256 bytes (- 11 bytes if you have padding). So, either use a larger key or you encrypt the data with a symmetric key, and encrypt that key with rsa (which is the recommended approach).

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

That will require you to:

这将要求你:

  1. generate a symmetric key
  2. 生成对称密钥

  3. Encrypt the data with the symmetric key
  4. 使用对称密钥加密数据

  5. Encrypt the symmetric key with rsa
  6. 使用rsa加密对称密钥

  7. send the encrypted key and the data
  8. 发送加密密钥和数据

  9. Decrypt the encrypted symmetric key with rsa
  10. 用rsa解密加密的对称密钥

  11. decrypt the data with the symmetric key
  12. 使用对称密钥解密数据

  13. done :)

#2


14  

Based on @John Snow answer, I did an example

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

  1. Generate Symmetric Key (AES with 128 bits)

    生成对称密钥(128位AES)

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

    使用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. Encrypt the key using RSA public key

    使用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. Send encrypted data (byteCipherText) + encrypted AES Key (encryptedKey)

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

  5. On the client side, decrypt symmetric key using RSA private key

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

    cipher.init(Cipher.PRIVATE_KEY, prKey);
    byte[] decryptedKey = cipher.doFinal(encryptedKey);
    
  6. Decrypt the cipher using decrypted symmetric key

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

    //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


10  

You should not use RSA on your secret data directly. You should only ever use RSA on pseudo-random or completely random data, such as session keys or message authentication codes.

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

You've gotten the problem at 256 bytes -- that is because you're probably working with 2048 bit keys. The keys are able to encrypt any integer in the range 0 to 2^2048 - 1 into the same range, and that means your data must be 256 bytes or smaller.

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

If you intend to encrypt more than this, please use one RSA encryption to encrypt a session key for a symmetric algorithm, and use that to encrypt your data.

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

#4


2  

To follow on from John Snow's answer above I created a simple random-symmetric-crypt library that you can use to simply encrypt any length data using a private key.

为了继续上面的John Snow的回答,我创建了一个简单的随机对称加密库,您可以使用它来简单地使用私钥加密任何长度的数据。

You can find the library at GitHub - random-symmetric-crypto

您可以在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);

#5


-4  

you need split your data by the publicKey

您需要通过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);
}

#1


52  

The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.

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

So basicly you divide the key length with 8 -11(if you have padding). For example if you have a 2048bit key you can encrypt 2048/8 = 256 bytes (- 11 bytes if you have padding). So, either use a larger key or you encrypt the data with a symmetric key, and encrypt that key with rsa (which is the recommended approach).

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

That will require you to:

这将要求你:

  1. generate a symmetric key
  2. 生成对称密钥

  3. Encrypt the data with the symmetric key
  4. 使用对称密钥加密数据

  5. Encrypt the symmetric key with rsa
  6. 使用rsa加密对称密钥

  7. send the encrypted key and the data
  8. 发送加密密钥和数据

  9. Decrypt the encrypted symmetric key with rsa
  10. 用rsa解密加密的对称密钥

  11. decrypt the data with the symmetric key
  12. 使用对称密钥解密数据

  13. done :)

#2


14  

Based on @John Snow answer, I did an example

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

  1. Generate Symmetric Key (AES with 128 bits)

    生成对称密钥(128位AES)

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

    使用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. Encrypt the key using RSA public key

    使用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. Send encrypted data (byteCipherText) + encrypted AES Key (encryptedKey)

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

  5. On the client side, decrypt symmetric key using RSA private key

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

    cipher.init(Cipher.PRIVATE_KEY, prKey);
    byte[] decryptedKey = cipher.doFinal(encryptedKey);
    
  6. Decrypt the cipher using decrypted symmetric key

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

    //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


10  

You should not use RSA on your secret data directly. You should only ever use RSA on pseudo-random or completely random data, such as session keys or message authentication codes.

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

You've gotten the problem at 256 bytes -- that is because you're probably working with 2048 bit keys. The keys are able to encrypt any integer in the range 0 to 2^2048 - 1 into the same range, and that means your data must be 256 bytes or smaller.

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

If you intend to encrypt more than this, please use one RSA encryption to encrypt a session key for a symmetric algorithm, and use that to encrypt your data.

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

#4


2  

To follow on from John Snow's answer above I created a simple random-symmetric-crypt library that you can use to simply encrypt any length data using a private key.

为了继续上面的John Snow的回答,我创建了一个简单的随机对称加密库,您可以使用它来简单地使用私钥加密任何长度的数据。

You can find the library at GitHub - random-symmetric-crypto

您可以在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);

#5


-4  

you need split your data by the publicKey

您需要通过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);
}