一. 現象:
有一段老代碼用來加密的,可是在使用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爲單位分段,從而使得一個密鑰報錯,另外一個不報錯。
四.擴展:
- 爲何一次加密的數據長度爲 (密鑰長度/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。
- 爲何有不一樣長度的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以上。
- 對稱加密vs分對稱加密?
對稱加密是最快速、最簡單的一種加密方式,加密(encryption)與解密(decryption)用的是一樣的密鑰(secret key)。對稱加密有不少種算法,因爲它效率很高,因此被普遍使用在不少加密協議的核心當中。對稱加密一般使用的是相對較小的密鑰,通常小於256 bit。由於密鑰越大,加密越強,但加密與解密的過程越慢。密鑰的大小既要照顧到安全性,也要照顧到效率,是一個trade-off。對稱加密的一大缺點是密鑰的管理與分配。
非對稱加密爲數據的加密與解密提供了一個很是安全的方法,它使用了一對密鑰,公鑰(public key)和私鑰(private key)。私鑰只能由一方安全保管,不能外泄,而公鑰則能夠發給任何請求它的人。非對稱加密使用這對密鑰中的一個進行加密,而解密則須要另外一個密鑰。雖然非對稱加密很安全,可是和對稱加密比起來,它很是的慢。
將二者結合起來,將對稱加密的密鑰使用非對稱加密的公鑰進行加密,而後發送出去,接收方使用私鑰進行解密獲得對稱加密的密鑰,而後雙方可使用對稱加密來進行溝通。
五.結論:
優先選擇方案:使用對稱密鑰(好比
AES/
DES等加解密方法)加密數據,而後使用非對稱密鑰(RSA加解密密鑰)加密對稱密鑰。原問題中因爲雙方約定了非對稱加密的方式,因此用分段加密來解決了問題,可是能夠知道這樣是比較低效的。