寫在前面
基于項目安全性需要,有時候我們的項目會使用AES 256加解密算法蛉幸。以下,是針對實現(xiàn)AES256 Padding7加密算法實現(xiàn)的關(guān)鍵步驟解析以及此過程遇到的一些問題總結(jié)丛晦。
GitHub鏈接地址:https://github.com/zhijunhong/common_utils/tree/master/aes256
一些概念
對稱加密算法
加密和解密用到的密鑰是相同的奕纫,這種加密方式加密速度非常快烫沙,適合經(jīng)常發(fā)送數(shù)據(jù)的場合匹层;缺點是密鑰的傳輸比較麻煩。
非對稱加密算法
加密和解密用的密鑰是不同的锌蓄,這種加密方式是用數(shù)學(xué)上的難解問題構(gòu)造的升筏,通常加密解密的速度比較慢,適合偶爾發(fā)送數(shù)據(jù)的場合瘸爽;優(yōu)點是密鑰傳輸方便您访。常見的非對稱加密算法為RSA、ECC和EIGamal等剪决。
實際應(yīng)用中灵汪,一般是通過RSA加密AES的密鑰,傳輸?shù)浇邮辗街绾矗邮辗浇饷艿玫紸ES密鑰识虚,然后發(fā)送方和接收方用AES密鑰來通信。
關(guān)于AES 256
高級加密標準(AES,Advanced Encryption Standard)為最常見的對稱加密算法妒茬。對稱加密算法:簡單來說就是加密和解密過程中使用的秘鑰(根據(jù)一定的規(guī)則生成)是相同的担锤。
[站外圖片上傳中...(image-eb2b46-1619076938237)]
下面簡單介紹下各個部分的作用與意義:
明文P | 需要加密的明文 |
---|---|
密鑰K | 用來加密明文的密碼,在對稱加密算法中乍钻,加密與解密的密鑰是相同的肛循。密鑰為接收方與發(fā)送方協(xié)商產(chǎn)生铭腕,但不可以直接在網(wǎng)絡(luò)上傳輸,否則會導(dǎo)致密鑰泄漏多糠,通常是通過非對稱加密算法加密密鑰累舷,然后再通過網(wǎng)絡(luò)傳輸給對方,或者直接面對面商量密鑰夹孔。密鑰是絕對不可以泄漏的被盈,否則會被攻擊者還原密文,竊取機密數(shù)據(jù) |
AES加密算法 | 設(shè)AES加密函數(shù)為E搭伤,則 C = E(K, P),其中P為明文只怎,K為密鑰,C為密文怜俐。也就是說身堡,把明文P和密鑰K作為加密函數(shù)的參數(shù)輸入,則加密函數(shù)E會輸出密文C |
密文C | 經(jīng)加密函數(shù)處理后拍鲤,可以在網(wǎng)絡(luò)傳輸中傳遞的密文數(shù) |
AES解密算法 | 設(shè)AES解密函數(shù)為D贴谎,則 P = D(K, C),其中C為密文,K為密鑰季稳,P為明文擅这。也就是說,把密文C和密鑰K作為解密函數(shù)的參數(shù)輸入绞幌,則解密函數(shù)會輸出明文P |
AES 256加解密算法實現(xiàn)
本博客重點講解AES 256加解密算法實現(xiàn)過程蕾哟;有關(guān)AES算法原理部分,網(wǎng)上有很多相關(guān)的博客莲蜘,這里不再贅述谭确。這里要特別指出的一點是,AES 256中的256指的是秘鑰K的長度票渠,常見的密鑰長度還有128位逐哈、192位。密鑰的長度不同问顷,推薦加密輪數(shù)也不同昂秃,如下表所示:
AES | 密鑰長度(32位比特字) | 分組長度(32位比特字) | 加密輪數(shù) |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
生成秘鑰
生成秘鑰的方式是需要另一端解密人員一起協(xié)定的,不同的廠商乃至不同的項目杜窄,生成秘鑰的方式理論上應(yīng)該都要是不同的肠骆。
這里只是為簡單舉例:
使用用戶名username
,密碼password
和隨機數(shù)random
經(jīng)過MD5加密后再經(jīng)過HAS-256 hash后生成一組密鑰,具體代碼如下:
byte[] pkey = generatePkey("zhijunhong", "123456", "1111");
/**
* 生成秘鑰
*
* @param username
* @param password
* @param random
* @return
* @throws NoSuchAlgorithmException
*/
private byte[] generatePkey(String username, String password, String random) throws NoSuchAlgorithmException {
String mD5Str = MD5Utility.getMD5DefaultEncode(username + random + password); //MD5加密,加密算法見github代碼
return Sha256Utils.getSHA256ByteArray(random + mD5Str); //經(jīng)過HAS-256 hash
}
使用密鑰加密明文
從步驟1獲取的密鑰pkey塞耕,還需要指定向量IV蚀腿,這里隨機指定IV為一組數(shù)據(jù)串,實際項目中,需要和解密端協(xié)定統(tǒng)一的IV向量莉钙。
String base64EncryptStr = AESUtils.aesEncryptStr("我是明文 ", pkey, AESUtils.IV); //密文
/**
* @param content 加密前原內(nèi)容
* @param iv
* @return base64EncodeStr aes加密完成后內(nèi)容
* @throws
* @Title: aesEncryptStr
* @Description: aes對稱加密
*/
public static String aesEncryptStr(String content, byte[] pkey, String iv) {
byte[] aesEncrypt = aesEncrypt(content, pkey, iv); //具體方法解析廓脆,見下文
System.out.println("加密后的byte數(shù)組:" + Arrays.toString(aesEncrypt));
String base64EncryptStr = Base64Utils.encode(aesEncrypt);
System.out.println("加密后 base64EncodeStr:" + base64EncryptStr);
return base64EncryptStr;
}
對上述加密方法,進行參數(shù)說明:
- content:待加密的明文
- pkey:“生成秘鑰”步驟生成的加密秘鑰
- iv:加密向量IV
其中磁玉,具體加密方法aesEncrypt(String content, byte[] pkey, String IV)
如下:
/**
* @param content 需要加密的原內(nèi)容
* @param pkey 密匙
* @param
* @return
*/
public static byte[] aesEncrypt(String content, byte[] pkey, String IV) {
try {
//SecretKey secretKey = generateKey(pkey);
//byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec skey = new SecretKeySpec(pkey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");// "算法/加密/填充"
IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skey, iv);//初始化加密器
byte[] encrypted = cipher.doFinal(content.getBytes("UTF-8"));
return encrypted; // 加密
} catch (Exception e) {
Log.i(TAG,"aesEncrypt() method error:", e);
}
return null;
}
最后停忿,進行一輪base64轉(zhuǎn)碼String base64EncryptStr = Base64Utils.encode(aesEncrypt);
操作后,輸出密文字符串base64EncryptStr蚊伞。
使用密鑰解密密文
秘鑰解密的過程其實就是加密的逆過程席赂,如下:解密方法
/**
* @param base64EncryptStr base64處理過的字符串
* @param pkey 密匙
* @param
* @return String 返回類型
* @throws Exception
* @throws
* @Title: aesDecodeStr
* @Description: 解密 失敗將返回NULL
*/
public static String aesDecodeStr3(String base64EncryptStr, byte[] pkey, String IV) throws Exception {
byte[] base64DecodeStr = Base64Utils.decode(base64EncryptStr);
byte[] aesDecode = aesDecode(base64DecodeStr, pkey, IV);
if (aesDecode == null) {
return null;
}
String result;
result = new String(aesDecode, "UTF-8");
return result;
}
對上述解密方法,進行參數(shù)說明:
- base64EncryptStr:base64編碼過的加密密文
- pkey:秘鑰(同加密秘鑰)
- IV:向量
具體解密過程:先通過base64還原密文編碼时迫,再通過aesDecode(byte[] encryptStr, byte[] pkey, String IV)
方法進行解密
其中氧枣,具體解密方法aesDecode(byte[] encryptStr, byte[] pkey, String IV)
,基本和加密過程相差不大别垮,如下:
/**
* 解密
*
* @param encryptStr 解密前的byte數(shù)組
* @param pkey 密匙
* @param IV
* @return result 解密后的byte數(shù)組
* @throws Exception
*/
public static byte[] aesDecode(byte[] encryptStr, byte[] pkey, String IV) throws Exception {
//SecretKey secretKey = generateKey(pkey);
//byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec skey = new SecretKeySpec(pkey, "AES");
IvParameterSpec iv = new IvParameterSpec(IV.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");// 創(chuàng)建密碼器
cipher.init(Cipher.DECRYPT_MODE, skey, iv);// 初始化解密器
byte[] result = cipher.doFinal(encryptStr);
return result; // 解密
}
通過上述一系列操作后,最后將獲取的字符數(shù)組扎谎,通過new String(aesDecode, "UTF-8")
操作碳想,就可以將密文重新解密成明文"我是明文"。
這里有一點需要特別說明一下: 字符的編碼往往會影響加解密的結(jié)果毁靶。同一個字符被ASCII胧奔、Unicode和UTF-8編碼時,字符編碼的位數(shù)都是有區(qū)別的预吆。很多同學(xué)沒有注意這個問題龙填,結(jié)果會導(dǎo)致解密失敗。所以兩端在聯(lián)調(diào)加解密算法的時候拐叉,最好能約定字符編碼統(tǒng)一岩遗。
打印明文和解密出的明文日志,如下:
2021-02-24 18:05:33.651 21560-21560/com.example.aes256 I/MainActivity: encryptStr: y9COgiC06V2E1CIuhJbPfg==
2021-02-24 18:05:33.652 21560-21560/com.example.aes256 I/MainActivity: decodeStr: 我是明文
完整代碼:https://github.com/zhijunhong/common_utils/tree/master/aes256
最后凤瘦,如果這篇博文對你有所幫助宿礁,別忘了點個贊喲 _
參考
SSL在線工具-AES在線加解密|AES在線加密 非常好用的在線驗證AES加解密結(jié)果網(wǎng)站