在上一遍Android中常用的加密算法——AES加密中我們介紹了對(duì)稱加密和非對(duì)稱加密,對(duì)稱加密由于加密和解密使用同一個(gè)秘鑰因此安全性與非對(duì)稱加密相比要低得多。這一篇我們就來介紹一種被廣泛應(yīng)用的非對(duì)稱加密——RSA加密。
RSA加密算法
RSA是一種應(yīng)用十分廣泛的非對(duì)稱加密算法,在公開密鑰加密和電子商業(yè)中RSA被廣泛使用。RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當(dāng)時(shí)他們?nèi)硕荚诼槭±砉W(xué)院工作。RSA就是他們?nèi)诵帐祥_頭字母拼在一起組成的。1973年,在英國政府通訊總部工作的數(shù)學(xué)家克利福德·柯克斯(Clifford Cocks)在一個(gè)內(nèi)部文件中提出了一個(gè)相同的算法,但他的發(fā)現(xiàn)被列入機(jī)密,一直到1997年才被發(fā)表?!?a target="_blank" rel="nofollow">維基百科
其算法原理基于很簡單的數(shù)學(xué)知識(shí):既對(duì)兩個(gè)大素?cái)?shù)相乘得到其乘積很簡單,但對(duì)乘積進(jìn)行因數(shù)分解很難,兩個(gè)大素?cái)?shù)組合即為公鑰,乘積未秘鑰。只要保證兩個(gè)不想等的素?cái)?shù)足夠大就可以保證加密足夠安全。
算法實(shí)現(xiàn)
生成秘鑰
2009年12月12日,編號(hào)為RSA-768(768 bits, 232 digits)數(shù)也被成功分解[10]。這一事件威脅了現(xiàn)通行的1024-bit密鑰的安全性,普遍認(rèn)為用戶應(yīng)盡快升級(jí)到2048-bit或以上?!?a target="_blank" rel="nofollow">維基百科
Android同樣提供了系統(tǒng)類KeyPairGenerator用于輔助生成秘鑰。
/**
* 生產(chǎn)秘鑰 秘鑰長度建議不要小于1024
* @param keyLength 秘鑰長度,范圍512 —— 2048
* @return the secretKey
* @throws Exception
*/
public static KeyPair generateKey(int keyLength) throws NoSuchAlgorithmException {
// 獲取秘鑰生成器
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
keyGenerator.initialize(keyLength);
// 生成秘鑰并返回
return keyGenerator.genKeyPair();
}
/**
* 生產(chǎn)秘鑰, 默認(rèn)秘鑰長度1024
*
* @return the secretKey
* @throws Exception
*/
public static KeyPair generateKey() throws NoSuchAlgorithmException {
return generateKey(1024);
}
上面的方法可以很便捷的在Android上生成秘鑰,但實(shí)際開發(fā)中因?yàn)榘踩詥栴}很少在Android端生成秘鑰(不排除特殊需求),通常是服務(wù)器端將公鑰發(fā)送給Android端,Android端用公鑰進(jìn)行加密后發(fā)送給服務(wù)器端,服務(wù)器用私鑰進(jìn)行解密。秘鑰的保存和傳輸通常會(huì)轉(zhuǎn)換成一串ASCII碼如下:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxlSpJwBEM/ia2P5jLTAGSxMexRxSKlmF
gIrPX7g0DsPIrqhMZMleTSLXOMT8D+1+T1UlHfwsJOybpVoliLoXIPIpk62TrJwDMPoXAZXP8AHS
vdcrDawQsypT3ZomUWup9yUoXUhcECqkyOeumfSKSCd1465UzWYJU3rKop/NTPWyELjBPZPRM+7V
X8ymmNLnTu01sCirM8KVVAkYmtZ1S15MnQaMCEyNOJGYHc0ctLcMKGfcesG/o+eapddJ0lvIwxWq
s6GsBTOui1e172SqdLqib8Zw/41WeVCXXIlj/O+b78k4gLM3tZBoAx9jOChRPZXW8cy/5Ug2HCS2
S+wNMwIDAQAB
-----END PUBLIC KEY-----
第一行和最后一行是標(biāo)注秘鑰的開始和結(jié)束,在轉(zhuǎn)換為私鑰和公鑰對(duì)象是需要去掉。
//秘鑰字符串生成Key需要通過Base64轉(zhuǎn)換為byte[]
publicKey = RSAUtil.getPublicKey(Base64.decode(publicKeyStr, Base64.DEFAULT));
privateKey = RSAUtil.getPrivateKey(Base64.decode(privateKeyStr, Base64.DEFAULT));
轉(zhuǎn)換為秘鑰對(duì)象是需要先使用Base64類對(duì)秘鑰字符串進(jìn)行解碼decode,同理如果要將Android端生成的秘鑰進(jìn)行保存或者傳輸通常也需要用Base64類進(jìn)行編碼encode轉(zhuǎn)換為字符串。
/**
* 將公鑰的byte[]數(shù)據(jù)還原為PublicKey
* @param publicKeyBytes
* @return PublicKey
* @throws Exception
*/
public static PublicKey getPublicKey(byte[] publicKeyBytes) throws Exception{
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* 將私鑰的byte[]數(shù)據(jù)還原為PrivateKey
* @param privateKey
* @return PrivateKey
* @throws Exception
*/
public static PrivateKey getPrivateKey(byte[] privateKey) throws Exception{
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
注意私鑰轉(zhuǎn)換和公鑰轉(zhuǎn)換用的類是不同的。
加密
/**
* 用公鑰加密,默認(rèn)填充模式 RSA/ECB/PKCS1Padding
* <br>每次加密的字節(jié)數(shù),不能超過密鑰的長度值減去11
*
* @param data 需加密數(shù)據(jù)的byte數(shù)據(jù)
* @param publicKey 公鑰
* @return 加密后的byte型數(shù)據(jù)
*/
public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
return encrypt(data, publicKey, "RSA/ECB/PKCS1Padding");
}
/**
* 用公鑰加密
* <br>每次加密的字節(jié)數(shù),不能超過密鑰的長度值減去11
*
* @param data 需加密數(shù)據(jù)的byte數(shù)據(jù)
* @param publicKey 公鑰
* @param transformation 加密模式和填充方式
* @return 加密后的byte型數(shù)據(jù)
*/
public static byte[] encrypt(byte[] data, PublicKey publicKey, String transformation) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
// 編碼前設(shè)定編碼方式及密鑰
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 傳入編碼數(shù)據(jù)并返回編碼結(jié)果
return cipher.doFinal(data);
}
加密模式和填充方式同樣不過多探討了參考上一篇提到的幾篇文章,只要保存加密和解密端保持模式一致就好。
需要注意的是:加密時(shí)只能一次性加密秘鑰長度減11字節(jié)的數(shù)據(jù),假設(shè)秘鑰長度為1024(下同),那么每次只能加密117字節(jié)(1024 / 8 - 11),若數(shù)據(jù)過長請(qǐng)分段加密。
有分段加密同樣的也需要分段解密,前面我們說了每次只能加密117字節(jié)的數(shù)據(jù),而這些數(shù)據(jù)加密之后的長度正好是秘鑰長度1024bit,所以我們?cè)诮饷軙r(shí)需要對(duì)每1024bit數(shù)據(jù)進(jìn)行解密。
解密
/**
* 用私鑰解密,默認(rèn)加密模式和填充方式為 RSA/ECB/PKCS1Padding"
*
* @param encryptedData 經(jīng)過encrypt()加密返回的byte數(shù)據(jù)
* @param privateKey私鑰
* @return
*/
public static byte[] decrypt(byte[] encryptedData, PrivateKey privateKey) throws Exception {
return decrypt(encryptedData, privateKey,"RSA/ECB/PKCS1Padding");
}
/**
* 用私鑰解密
*
* @param encryptedData 經(jīng)過encrypt()加密返回的byte數(shù)據(jù)
* @param privateKey私鑰
* @param transformation 加密模式和填充方式
* @return
*/
public static byte[] decrypt(byte[] encryptedData, PrivateKey privateKey, String transformation) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encryptedData);
}
注意加密模式和填充方式必須與加密時(shí)保持一致。