背景:
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给我。