Android中常用的加密算法——RSA加密

在上一遍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í)保持一致。

參考文章:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容