AES简介(高级加密)
高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥。
加密公式:
C=E(K,P)
C —— 加密后的字符串
E —— 加密函数
K —— 密钥(key)
P —— 需要加密的字符串(明文)
解密公式:
P=D(K,C)
C —— 输入加密字符串
D —— 解密函数
K —— 密钥(key)
P —— 解密后的字符串(明文)
AES 加密模式(Mode)
对称/分组密码一般分为流加密(如OFB、CFB等)和块加密(如ECB、CBC等)。对于流加密,需要将分组密码转化为流模式工作。对于块加密(或称分组加密),如果要加密超过块大小的数据,就需要涉及填充和链加密模式。
ECB(Electronic Code Book电子密码本)模式
ECB模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密密钥长度相同,然后每组都用相同的密钥进行加密。
优点:
1.简单;
2.有利于并行计算;
3.误差不会被传送;
缺点:
1.不能隐藏明文的模式;
2.可能对明文进行主动攻击;
因此,此模式适于加密小消息。
CBC(Cipher Block Chaining,加密块链)模式
优点:
1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
缺点:
1.不利于并行计算;
2.误差传递;
3.需要初始化向量IV
CFB(Cipher FeedBack Mode,加密反馈)模式
优点:
1.隐藏了明文模式;
2.分组密码转化为流模式;
3.可以及时加密传送小于分组的数据;
缺点:
1.不利于并行计算;
2.误差传送:一个明文单元损坏影响多个单元;
3.唯一的IV;
OFB(Output FeedBack,输出反馈)模式
优点:
1.隐藏了明文模式;
2.分组密码转化为流模式;
3.可以及时加密传送小于分组的数据;
缺点:
1.不利于并行计算;
2.对明文的主动攻击是可能的;
3.误差传送:一个明文单元损坏影响多个单元
AES 填充方式(Padding)
NoPadding
无填充
PKCS5Padding
填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。
ISO10126Padding
将原文的长度边长组的大小的整数倍。
最后一个填充是填充的字节数,其它位随机数。
PaddingMode.Zeros
尾部补零
PaddingMode.PKCS7
同PKCS5Padding一样
AES 加密说明
本文java、JS、C#三端均采用CBC模式加密,填充方式选择PKCS7
Java端
/*
* AES工具类
*/
public class AESUtil {
public static String IV = "1234567890000000"; // 自定义
public static String KEY = "ABCDEFG1234567ABCDEFG12345671111"; // 自定义
private static String MODE = "AES/CBC/PKCS5Padding"; // 选择CBC模型和PKCS5填充方式
private static String TYPE = "AES";
/*
* AES解密函数
*/
public static String AESDecrypt(String Data, String Key) throws UnsupportedEncodingException
{
byte[] inputBytes = Base64StringToByteArray(Data);
byte[] keyBytes = Key.getBytes("utf-8");
try {
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, TYPE);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.DECRYPT_MODE, keySpec,new IvParameterSpec(IV.getBytes("utf-8")));
inputBytes = cipher.doFinal(inputBytes);
} catch (Exception e) {
e.printStackTrace();
};
return new String(inputBytes, "utf-8");
}
/*
* AES加密函数
*/
public static String AESEncrypt(String data,String key) {
try {
IvParameterSpec iv = new IvParameterSpec(IV.getBytes("utf-8"));
SecretKeySpec sKeySpec = new SecretKeySpec(key.getBytes("utf-8"),TYPE);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec, iv);
byte[] encrypted = cipher.doFinal(data.getBytes("utf-8"));
return new String(Base64.encodeBase64(encrypted)); // 需要Base64转化函数
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/*
* Base64工具类转化
*/
public static byte[] Base64StringToByteArray(String s)
{
byte[] arr = Base64Decoder.decode(s.substring(s.indexOf(",")+1));
return arr;
}
}
Java这里可能会出现一个问题,key长度限制,需要更换Java运行环境,受限的policy文件,文件位于/jre/lib/security下
java.security.InvalidKeyException: Illegal key size
将下面链接中的jar包下载下来,替换jdk 与jre下两个jar包:local_policy.jar和US_export_policy.jar即可。
JDK6: http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
JDK7: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
jdk对应jar包的路径(找自己的路径):C:\Java\jdk1.7.0_67\jre\lib\security
jre对应jar包的路径(找自己的路径):C:\Java\jre7\lib\security\
JavaScript
JS我们可以下载crypto-js 加密库,里面包含了许多加密算法,引用的话只需要引用其中的aes.js
var keyStr = "ABCDEFG1234567ABCDEFG12345671111"
var IVStr = "1234567890000000";//16位
var iv = CryptoJS.enc.Utf8.parse(IVStr);
key = CryptoJS.enc.Utf8.parse(keyStr);
// 解密
function dataDecrypt(data){
var bytes = CryptoJS.AES.decrypt(data, key,{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
data = bytes.toString(CryptoJS.enc.Utf8);
return data;
}
// 加密
function dataEncrypt(jsonList){
var ciphertext = CryptoJS.AES.encrypt(jsonList, key,{iv:iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7}).toString();
return ciphertext;
}
C#
class AESUtil
{
const string IV = "1234567890000000";
const string key = "ABCDEFG1234567ABCDEFG12345671111";
/// <summary>
/// AES加密(无向量)
/// </summary>
/// <param name="Data">被加密的明文</param>
/// <param name="Key">密钥</param>
/// <returns>密文</returns>
public static string AESEncrypt(String Data, String Key)
{
MemoryStream mStream = new MemoryStream();
RijndaelManaged aes = new RijndaelManaged();
byte[] plainBytes = Encoding.UTF8.GetBytes(Data);
Byte[] bKey = new Byte[32];
Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
byte[] _iv = Encoding.UTF8.GetBytes(IV);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.KeySize = 256;
//aes.Key = _key;
aes.Key = bKey;
aes.IV = _iv;
CryptoStream cryptoStream = new CryptoStream(mStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
try
{
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
cryptoStream.FlushFinalBlock();
return Convert.ToBase64String(mStream.ToArray());
}
finally
{
cryptoStream.Close();
mStream.Close();
aes.Clear();
}
}
/// <summary>
/// AES解密(无向量)
/// </summary>
/// <param name="Data">被加密的明文</param>
/// <param name="Key">密钥</param>
/// <returns>明文</returns>
public static string AESDecrypt(String Data, String Key)
{
byte[] inputBytes = Base64StringToByteArray(Data);
byte[] keyBytes = Encoding.UTF8.GetBytes(Key.Substring(0, 32));
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = keyBytes;
aesAlg.IV = Encoding.UTF8.GetBytes(IV.Substring(0, 16));
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream(inputBytes))
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srEncrypt = new StreamReader(csEncrypt))
{
string str = srEncrypt.ReadToEnd();
srEncrypt.Close();
csEncrypt.Close();
msEncrypt.Close();
return str;
}
}
}
}
}
/// <summary>
/// 将指定的Base64字符串转换为byte数组
/// </summary>
/// <param name="s"></param>
/// <returns>16进制字符串对应的byte数组</returns>
public static byte[] Base64StringToByteArray(string s)
{
byte[] arr = Convert.FromBase64String(s.Substring(s.IndexOf(",") + 1));
return arr;
}
}