最近在学AES加密,通过网上学习查找资料,封装了一个AES加密解密工具类,说几点:
- 算法/模式/填充,这个最好使用"AES/CBC/PKCS5Padding","ECB"在IOS中不安全(网上查找资料得知),不要使用默认填充
- 参数密钥要使用16位,不使用"AES/CBC/PKCS5Padding"这种模式是不需要16位密钥的,不过这种安全性比较高,关于安全性可以查找下资料
- 这里主要是代码方面,理论方面要查找其他资料(修改版:第一次发的代码,跨域解密不出来,这次的可以了)
- (重点)Linux下解密一定要加上SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(secretKey.getBytes());否则会报错,javax.crypto.BadPaddingException: Given final block not properly padded - (重点)使用Base64再包一层是为了防止加密后在变量之间传递丢字符
如果有不对的地方,请大家多指点。 多反馈。
模式/填充参考:
算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度 AES/CBC/NoPadding 16 不支持 AES/CBC/PKCS5Padding 32 16 AES/CBC/ISO10126Padding 32 16 AES/CFB/NoPadding 16 原始数据长度 AES/CFB/PKCS5Padding 32 16 AES/ECB/NoPadding 16 不支持 AES/ECB/PKCS5Padding 32 16 AES/ECB/ISO10126Padding 32 16 AES/OFB/NoPadding 16 原始数据长度 AES/OFB/PKCS5Padding 32 16 AES/OFB/ISO10126Padding 32 16 AES/PCBC/NoPadding 16 不支持 AES/PCBC/PKCS5Padding 32 16 AES/PCBC/ISO10126Padding 32 16 多了一行。。。
代码如下:
package com.test;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
* AES加密和解密工具类
*
* @author zhancy
*/
public class AESUtilFinal {
// AES密码器
private static Cipher cipher;
// 字符串编码
private static final String KEY_CHARSET = "UTF-8";
// 算法方式
private static final String KEY_ALGORITHM = "AES";
// 算法/模式/填充
private static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
// 私钥大小128/192/256(bits)位 即:16/24/32bytes,暂时使用128,如果扩大需要更换java/jre里面的jar包
private static final Integer PRIVATE_KEY_SIZE_BIT = 128;
private static final Integer PRIVATE_KEY_SIZE_BYTE = 16;
public static void main(String[] args) {
String secretKey = "1234567812345678";
String text = "test123测试";
String miwen = encrypt(secretKey, text);
System.out.println("密文为:" + miwen);
String mingwen = decrypt(secretKey, miwen);
System.out.println("明文为:" + mingwen);
}
/**
* 加密
*
* @param secretKey
* 密钥:加密的规则 16位
* @param plainText
* 明文:要加密的内容
* @return cipherText
* 密文:加密后的内容,如有异常返回空串:""
*/
public static String encrypt(String secretKey, String plainText) {
if (secretKey.length() != PRIVATE_KEY_SIZE_BYTE) {
throw new RuntimeException("AESUtil:Invalid AES secretKey length (must be 16 bytes)");
}
// 密文字符串
String cipherText = "";
try {
// 加密模式初始化参数
initParam(secretKey, Cipher.ENCRYPT_MODE);
// 获取加密内容的字节数组
byte[] bytePlainText = plainText.getBytes(KEY_CHARSET);
// 执行加密
byte[] byteCipherText = cipher.doFinal(bytePlainText);
cipherText = Base64.encodeBase64String(byteCipherText);
} catch (Exception e) {
throw new RuntimeException("AESUtil:encrypt fail!", e);
}
return cipherText;
}
/**
* 解密
*
* @param secretKey
* 密钥:加密的规则 16位
* @param cipherText
* 密文:加密后的内容,即需要解密的内容
* @return plainText
* 明文:解密后的内容即加密前的内容,如有异常返回空串:""
*/
public static String decrypt(String secretKey, String cipherText) {
if (secretKey.length() != PRIVATE_KEY_SIZE_BYTE) {
throw new RuntimeException("AESUtil:Invalid AES secretKey length (must be 16 bytes)");
}
// 明文字符串
String plainText = "";
try {
initParam(secretKey, Cipher.DECRYPT_MODE);
// 将加密并编码后的内容解码成字节数组
byte[] byteCipherText = Base64.decodeBase64(cipherText);
// 解密
byte[] bytePlainText = cipher.doFinal(byteCipherText);
plainText = new String(bytePlainText, KEY_CHARSET);
} catch (Exception e) {
throw new RuntimeException("AESUtil:decrypt fail!", e);
}
return plainText;
}
/**
* 初始化参数
* @param secretKey
* 密钥:加密的规则 16位
* @param mode
* 加密模式:加密or解密
*/
public static void initParam(String secretKey, int mode) {
try {
// 防止Linux下生成随机key
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(secretKey.getBytes());
// 获取key生成器
KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
keygen.init(PRIVATE_KEY_SIZE_BIT, secureRandom);
// 获得原始对称密钥的字节数组
byte[] raw = secretKey.getBytes();
// 根据字节数组生成AES内部密钥
SecretKeySpec key = new SecretKeySpec(raw, KEY_ALGORITHM);
// 根据指定算法"AES/CBC/PKCS5Padding"实例化密码器
cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
IvParameterSpec iv = new IvParameterSpec(secretKey.getBytes());
System.out.println("iv:" + new String(iv.getIV()));
cipher.init(mode, key, iv);
} catch (Exception e) {
throw new RuntimeException("AESUtil:initParam fail!", e);
}
}
}