rsa加解密的內容超長的問題解決

一. 現象:
     有一段老代碼用來加密的,可是在使用key A的時候,拋出了異常: javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes老代碼已經作了分段的加密,應該是已經考慮了加密長度的問題纔對。換了另外一個線上代碼中的key B,正常加密沒有異常。

二. 解決:
     老代碼以下:
private static String  encryptByPublicKey(String plainText String publicKey)  throws Exception {
  int MAX_ENCRYPT_BLOCK =  128 ;
  byte[] data = plainText.getBytes( "utf-8") ;
  Key e = RSASignature. getPublicKey(publicKey) ;
  //  對數據加密
  Cipher cipher = Cipher. getInstance( "RSA") ;
  cipher.init(Cipher. ENCRYPT_MODE e) ;
  int inputLen = data. length ;
  ByteArrayOutputStream out =  new ByteArrayOutputStream() ;
  int offSet =  0 ;
  byte[] cache ;
  int i =  0 ;
  //  對數據分段加密
  while (inputLen - offSet >  0) {
    if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
      cache = cipher.doFinal(data offSet MAX_ENCRYPT_BLOCK) ;
    else {
      cache = cipher.doFinal(data offSet inputLen - offSet) ;
    }
    out.write(cache 0 cache. length) ;
    i++ ;
    offSet = i * MAX_ENCRYPT_BLOCK ;
  }
  byte[] encryptedData = out.toByteArray() ;
  out.close() ;
  return Base64. encodeBase64String(encryptedData) ;
}
     MAX_ENCRYPT_BLOCK值換爲64就解決了問題。按報錯提示的改成117也能夠,不過爲了湊整,選擇了64。

三. 緣由:
     實際使用RSA加解密算法一般有兩種不一樣的方式,一種是使用對稱密鑰(好比 AES/ DES等加解密方法)加密數據,而後使用非對稱密鑰(RSA加解密密鑰)加密對稱密鑰;另外一種是直接使用非對稱密鑰加密數據。第一種方式安全性高,複雜度也高,不存在加密數據長度限制問題,第二種方式安全性差一些,複雜度低,可是存在加密數據限制問題(即便用非對稱密鑰加密數據時,一次加密的數據長度是(密鑰長度/8-11))。
     目前雙方約定的方式爲第二種方式,而對應於本次拋錯的密鑰,key長度爲1024位,1024/8 - 11 = 117,因此一次加密內容不能超過117bytes。另外一個密鑰沒有問題,由於key的長度爲2048位, 2048 /8 - 11 = 245, 一次加密內容不能超過 245 bytes。而分段加密代碼中用128爲單位分段,從而使得一個密鑰報錯,另外一個不報錯。

四.擴展:
  1. 爲何一次加密的數據長度爲 (密鑰長度/8-11) ?
    網上有說明文長度小於等於密鑰長度(Bytes)-11,這說法自己不太準確,會給人感受RSA 1024只能加密117字節長度明文。實際上,RSA算法自己要求加密內容也就是明文長度m必須0<m<n,也就是說內容這個大整數不能超過n,不然就出錯。那麼若是m=0是什麼結果?廣泛RSA加密器會直接返回全0結果。若是m>n,因爲 m e  ≡ c (mod n) ,c爲密文,m爲明文,e和n組成公鑰,顯然當m>n時,m與m-n得出的密文同樣,沒法解密,運算就會出錯。
    因此,RSA 1024 實際可加密的明文長度最大也是1024bits,但問題就來了:

    若是小於這個長度怎麼辦?就須要進行padding,由於若是沒有padding,用戶沒法確分解密後內容的真實長度,字符串之類的內容問題還不大,以0做爲結束符,但對二進制數據就很難理解,由於不肯定後面的0是內容仍是內容結束符。html

    只要用到padding,那麼就要佔用實際的明文長度,因而纔有117字節的說法。咱們通常使用的padding標準有NoPPadding、OAEPPadding、PKCS1Padding等,其中PKCS#1建議的padding就佔用了11個字節。java

    若是大於這個長度怎麼辦?不少算法的padding每每是在後邊的,但PKCS的padding則是在前面的,此爲有意設計,有意的把第一個字節置0以確保m的值小於n。算法

    這樣,128字節(1024bits)-減去11字節正好是117字節,但對於RSA加密來說,padding也是參與加密的,因此,依然按照1024bits去理解,但實際的明文只有117字節了。安全

    關於PKCS#1 padding規範可參考:RFC2313 chapter 8.1,咱們在把明文送給RSA加密器前,要確認這個值是否是大於n,也就是若是接近n位長,那麼須要先padding再分段加密。除非咱們是「定長定量本身可控可理解」的加密不須要padding。
  2. 爲何有不一樣長度的key?
    看一下密鑰的生成過程:
    第一步,隨機選擇兩個不相等的質數p和q。
    第二步,計算p和q的乘積n。n即密鑰長度。
    第三步,計算n的歐拉函數φ(n)。
    第四步,隨機選擇一個整數e,條件是1< e < φ(n),且e與φ(n) 互質。
    第五步,計算e對於φ(n)的模反元素d。
    第六步,將n和e封裝成公鑰,n和d封裝成私鑰。
    加密(c爲密文,m爲明文):   m e  ≡ c (mod n)
    解密 (c爲密文,m爲明文)   c d  ≡ m (mod n)
    對極大整數作因數分解(由n,e推出d)的難度決定了RSA算法的可靠性。換言之,對一極大整數作因數分解愈困難,RSA算法愈可靠。假若有人找到一種快速因數分解的算法,那麼RSA的可靠性就會極度降低。但找到這樣的算法的可能性是很是小的。今天只有短的RSA密鑰纔可能被暴力破解。只要密鑰長度足夠長,用RSA加密的信息其實是不能被解破的。目前通常爲1024 bit以上的密鑰,推薦2048 bit以上。
  3. 對稱加密vs分對稱加密?
    對稱加密是最快速、最簡單的一種加密方式,加密(encryption)與解密(decryption)用的是一樣的密鑰(secret key)。對稱加密有不少種算法,因爲它效率很高,因此被普遍使用在不少加密協議的核心當中。對稱加密一般使用的是相對較小的密鑰,通常小於256 bit。由於密鑰越大,加密越強,但加密與解密的過程越慢。密鑰的大小既要照顧到安全性,也要照顧到效率,是一個trade-off。對稱加密的一大缺點是密鑰的管理與分配。
    非對稱加密爲數據的加密與解密提供了一個很是安全的方法,它使用了一對密鑰,公鑰(public key)和私鑰(private key)。私鑰只能由一方安全保管,不能外泄,而公鑰則能夠發給任何請求它的人。非對稱加密使用這對密鑰中的一個進行加密,而解密則須要另外一個密鑰。雖然非對稱加密很安全,可是和對稱加密比起來,它很是的慢。
    將二者結合起來,將對稱加密的密鑰使用非對稱加密的公鑰進行加密,而後發送出去,接收方使用私鑰進行解密獲得對稱加密的密鑰,而後雙方可使用對稱加密來進行溝通。

五.結論:
     優先選擇方案:使用對稱密鑰(好比 AES/ DES等加解密方法)加密數據,而後使用非對稱密鑰(RSA加解密密鑰)加密對稱密鑰。原問題中因爲雙方約定了非對稱加密的方式,因此用分段加密來解決了問題,可是能夠知道這樣是比較低效的。
相關文章
相關標籤/搜索