背景:
RAS是一個非常好的非對稱加密算法。AES則是一個目前國際應用廣泛的對稱加密算法。這兩者優劣皆有,需要結合具體的加密場景,選擇不同的加密方式,針對性能和安全,大家需要做出自己的判斷。
我們設計了兩個工具類,將RAS和AES的一些邏輯封裝進去,適合我們在開發時候直接調用。
代碼邏輯:
首先是AES算法。
/**
* AES工具類,密鑰必須是16位字符串
*/
public class AESUtils {
首先給他一個隨機密鑰的方法。
/**
* 產生隨機密鑰(這裏產生密鑰必須是16位)
*/
public static String generateKey() {
String key = UUID.randomUUID().toString().
replace("-", "").substring(0, 16);// 替換掉-號
return key;
}
這裏是加密算法Encrypt。
public static String encryptData(String key, String content) {
byte[] encryptedBytes = new byte[0];
try {
//缺省是ISO-8859-1,content可能是中文,所以這裏用UTF-8。
byte[] byteContent = content.getBytes("UTF-8");
// 爲了與 iOS 統一, 這裏的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = key.getBytes();
//會把參數放到secretKeySpec的變量Key裏,AES會放到algorithm。
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
//取得常量的編碼。
byte[] initParam = IV_STRING.getBytes();
//把initParam裏的字節編碼copy到ivParameterSpec的iv字節數組裏
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//初始化。用密鑰和一組算法參數初始化此 Cipher。
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
//執行
encryptedBytes = cipher.doFinal(byteContent);
// 同樣對加密後數據進行 base64 編碼
return Base64Utils.encode(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
解密的代碼這裏就不放了。
RAS加密AES密鑰。
這是一個測試程序。
這個RAEUtils類的加密解密邏輯如下:
public static String encryptByPublicKey(String data) throws Exception {
byte[] dataByte = data.getBytes();
byte[] keyBytes = Base64Utils.decode(PUBLIC_KEY);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key key = keyFactory.generatePublic(x509KeySpec);
// 對數據加密
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
/** 得到Cipher對象來實現對源數據的RSA加密 */
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(dataByte);
return Base64Utils.encode(encryptedData);
解密方法:
public static String decryptByPrivateKey(String data) throws Exception {
byte[] encryptedData = Base64Utils.decode(data);
byte[] keyBytes = Base64Utils.decode(PRIVATE_KEY);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
//解密
cipher.init(Cipher.DECRYPT_MODE, privateK);
這是解密代碼。
byte[] encryptedData = Base64Utils.decode(data);
byte[] keyBytes = Base64Utils.decode(PRIVATE_KEY);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數據分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher
.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher
.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData);
因爲密文太長,所以要分段解碼。
這裏用了默認的公鑰值,對AES產生的密鑰進行加密。先對數據進行編碼處理,然後就是用cipher對象對數據進行加密,一些參數的含義,有興趣的可以查閱JDK文檔和JAVAX文檔瞭解。
補充:
AES絕對不要使用 ECB 模式!
https://zh.wikipedia.org/wiki/塊密碼的工作模式
RSA 1024已經不安全了,起碼要2048!
參考文檔: https://segmentfault.com/a/1190000015943620
https://github.com/wustrive2008/aes-rsa-java/blob/master/src/main/java/com/wustrive/aesrsa/util/RSA.java
http://tool.oschina.net/uploads/apidocs/jdk-zh/javax/crypto/Cipher.html#init(int, java.security.Key, java.security.spec.AlgorithmParameterSpec)
http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
源碼地址: 添加鏈接描述
有空的,可以star一下,後續源碼會更新的。看到問題可以提Issue給我。