一. 现象:
有一段老代码用来加密的,可是在使用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加解密密钥)加密对称密钥。原问题中因为双方约定了非对称加密的方式,因此用分段加密来解决了问题,可是能够知道这样是比较低效的。