2012-04-04 81 views
25

我正在使用rsa密鑰來加密一個長字符串,我將發送給我的服務器(將使用服務器的公鑰和我的私鑰對其進行加密)但是,拋出一個異常,如javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes 我覺得直到現在我還沒有正確理解rsa的工作(使用內置庫是造成這種情況的原因)。
有人可以解釋爲什麼拋出這個異常。發送長字符串是否完全不可能加密?獲取IllegalBlockSizeException:使用rsa時數據長度不能超過256字節

+0

只需使用HTTPS和加密將被透明地進行。 – zaph 2016-07-19 04:50:20

回答

44

RSA算法只能加密具有在比特RSA密鑰長度的最大字節長度 與八個減去11填充 字節劃分,即最大字節數=密鑰長度以位/ 8位數據 - 11 。

所以基本上你用8 -11(如果你有填充)分割密鑰長度。例如,如果您有2048位密鑰,則可以加密2048/8 = 256個字節(如果您有填充,則爲 - 11個字節)。因此,要麼使用更大的密鑰,要麼使用對稱密鑰加密數據,並使用rsa加密該密鑰(這是推薦的方法)。

這將要求您:

  1. 生成對稱密鑰
  2. 加密與對稱密鑰
  3. 對數據進行加密與RSA對稱密鑰
  4. 發送加密密鑰和數據
  5. 使用rsa解密加密的對稱密鑰
  6. 使用對稱密鑰解密數據
  7. done :)
+1

爲什麼會有這樣的限制,只能將數據加密到一定的長度? – Ashwin 2012-04-07 01:17:12

+2

只有在使用'PKCS1Padding'時,'bits/8 - 11'中的密鑰長度纔有效。例如,'NoPadding'限制將是'密鑰長度,位數/ 8'。 – divanov 2013-11-18 21:38:57

+4

PKCS#1v1.5填充和無填充都不安全。使用OAEP填充(將塊大小減少11個字節以上) – CodesInChaos 2014-08-13 10:22:12

10

您不應該直接在您的機密數據上使用RSA。您應該只使用上的RSA僞隨機完全隨機的數據,如會話密鑰或消息認證碼。

你已經在256字節的問題 - 這是因爲你可能使用2048位密鑰。密鑰能夠將02^2048 - 1範圍內的任何整數加密到相同的範圍內,這意味着您的數據必須是256個字節或更小。

如果您打算加密的比這更多,請使用一個RSA加密爲對稱算法的會話密鑰加密,並使用加密您的數據。

+0

爲什麼會有這樣的限制,只能將數據加密到一定的長度? – Ashwin 2012-04-07 01:17:40

+3

由於RSA是在_finite [ring](http://en.wikipedia.org/wiki/Ring_(mathematics))_上執行的,因此唯一存在的數字是範圍爲[[0,2^2048- 1]',包括在內。任何長度超過2048位的消息都表示超出此範圍的數字,並且必須以兩個數據塊編碼或 - 如果您需要安全性 - 整個消息應在會話密鑰中加密。實際部署的RSA必須防止[多重攻擊](http://en.wikipedia.org/wiki/RSA_(算法)#Attacks_against_plain_RSA),並且從不處理「原始」明文是安全使用RSA的一個重要部分。 – sarnold 2012-04-08 22:50:04

2

要繼續從上面的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); 
+0

我無法找到您指定要使用哪種填充模式的位置。您應該明確指定OAEP填充,因爲其他常見填充(如PKCS#1v1.5或根本不填充)是不安全的。 – CodesInChaos 2014-08-13 10:27:09

+0

樂意接受拉請求。你有沒有填充不安全的URL? – William 2014-08-13 12:19:47

+0

Boneh寫了一篇關於[攻擊RSA]的好概述(http://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf)。 Crypto.se也有許多關於RSA的問題(http://crypto.stackexchange.com/search?q=textbook+rsa)。我在代碼的對稱部分找不到MAC。這意味着你很容易受到主動攻擊,比如填充神諭。舊的仍然流行的PKCS#1v1.5填充容易受到主動攻擊(除非你仔細研究弱點*),即Bleichenbacher的攻擊。 – CodesInChaos 2014-08-13 12:34:13

-4

你需要的公鑰

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); 
} 
+0

僅僅因爲你可以做點什麼並不意味着你應該這樣做。所需要的是混合加密,其中數據使用對稱算法(例如使用隨機密鑰的AES)進行加密,並且使用RSA對密鑰進行加密。 – zaph 2016-07-19 04:42:56

+0

註釋:1.「數據」是複數,「數據」是單數形式。 2.'bcd2Str'方法真正執行十六進制編碼[0-9A-Z]。 3.可能不需要通過RSA加密對二進制輸出進行編碼,如果需要編碼,而十六進制是正確的,通常情況下使用Base64編碼,它更緊湊。 – zaph 2016-07-19 04:54:30

3

基於@約翰雪回答您的數據拆分圖書館,我做了一個例子

  1. 生成對稱密鑰(AES,128位)

    01使用AES

    String plainText = "Please encrypt me urgently..." 
    Cipher aesCipher = Cipher.getInstance("AES"); 
    aesCipher.init(Cipher.ENCRYPT_MODE, secKey); 
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes()); 
    
  2. 加密使用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(byteCipherText); 
    
  3. 發送加密的數據(byteCipherText)鍵+加密AES密鑰(encryptedKey)

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

  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);` 
    
相關問題