一、MySQL的AES
加密写法:
SELECT HEX(AES_ENCRYPT('一二三','key1key1key1key1key1QAZ'))
加密结果:C796C6C418AA82A90FC7C326102CF119
解密写法:
SELECT AES_DECRYPT(UNHEX('C796C6C418AA82A90FC7C326102CF119'),'key1key1key1key1key1QAZ')
解密结果:一二三
二、Oracle自定义AES函数
处理key的函数:
CREATE OR REPLACE FUNCTION FUN_GENERATE_AESKEY(V_KEY VARCHAR2)
RETURN VARCHAR2 AS
V_KEYSTR VARCHAR2(4000); --原始key字符串
V_KEYSTR_TMP VARCHAR2(4000); --用于保存超出16位部分的字符串
V_KEYSTR_BYTE1 RAW(1); --作为key的16个字节
V_KEYSTR_BYTE2 RAW(1); --超出部分的字节,用于按字节做异或运算
v_char1 varchar(10); --16个字节里当前用于做异或运算的1个字节
v_char2 varchar(10); --超出16个字节部分的字符串,一次取一个字节
v_char3 varchar(10); --异或预算之后的字节对应的字符
V_NUM INT; --从0开始,用于计数超出16个字节部分,当前处理到第几个字节
V_BYTE_INDEX INT; --0到15,用于标记16个字节当前处理到第几个字节
BEGIN
IF LENGTHB(V_KEY) < 16 THEN
V_KEYSTR := V_KEY || RPAD(CHR(0), 16 - lengthb(V_KEY), CHR(0)); --小于16字节的 补0对应的字符,凑足16个字节
ELSE
V_KEYSTR := SUBSTRB(V_KEY, 1, 16); --大于等于16字节的,先保存前16个字节
--dbms_output.Put_line(V_KEYSTR);
IF LENGTHB(V_KEY) > 16 THEN
V_KEYSTR_TMP := SUBSTRB(V_KEY, 17, LENGTHB(V_KEY) - 16); --超出16个字节的部分
--dbms_output.Put_line(V_KEYSTR_TMP);
V_NUM := 0; --超出16个字节的已处理字符数计数
WHILE LENGTHB(V_KEYSTR_TMP) > 0 LOOP
--所有超出16位的字符处理完结束循环
V_BYTE_INDEX := MOD(V_NUM, 16); --取余,值为0到15
--dbms_output.Put_line(V_BYTE_INDEX);
v_char2 := SUBSTRB(V_KEYSTR_TMP, 1, 1); --超出16个字节部分的字符串,一次取一个字节
v_char1 := SUBSTRB(V_KEYSTR, V_BYTE_INDEX + 1, 1); --16个字节里当前用于做异或运算的1个字节
--dbms_output.Put_line(v_char2);
--dbms_output.Put_line(v_char1);
V_KEYSTR_BYTE2 := UTL_I18N.STRING_TO_RAW(v_char2); --得到这个字节的二进制数据
V_KEYSTR_BYTE1 := UTL_I18N.STRING_TO_RAW(v_char1); --得到这个字节的二进制数据
--异或运算之后的字节对应的字符串
v_char3 := CHR(TO_NUMBER(RAWTOHEX(UTL_RAW.BIT_XOR(V_KEYSTR_BYTE1,
V_KEYSTR_BYTE2)),
'XX'));
--替换更新的字节,组成新的16字节秘钥字符串
V_KEYSTR := SUBSTRB(V_KEYSTR, 1, V_BYTE_INDEX) || v_char3 ||
SUBSTRB(V_KEYSTR,
V_BYTE_INDEX + 2,
16 - (V_BYTE_INDEX + 1)); --使用异或结果替换0到15对应的字节
--dbms_output.Put_line(V_KEYSTR);
V_KEYSTR_TMP := SUBSTRB(V_KEYSTR_TMP, 2, LENGTHB(V_KEYSTR_TMP) - 1); --处理一个字节减少一个字节,直至处理完
V_NUM := V_NUM + 1; --字节数加1 代表处理下一个字节
END LOOP;
END IF;
END IF;
RETURN V_KEYSTR; --处理后的16字节密钥字符串
END;
加密函数(调用上面的处理key的函数):
CREATE OR REPLACE FUNCTION FUN_AES_ENCRYPT(V_STR VARCHAR2, V_KEY VARCHAR2)
RETURN VARCHAR2 AS
V_KEY_RAW RAW(16);
V_STR_RAW RAW(2000);
V_RETURN_STR VARCHAR2(2000);
V_TYPE PLS_INTEGER;
V_KEYSTR VARCHAR2(4000);
BEGIN
V_KEYSTR := FUN_GENERATE_AESKEY(V_KEY);
V_KEY_RAW := UTL_I18N.STRING_TO_RAW(V_KEYSTR, 'UTF8');
V_STR_RAW := UTL_I18N.STRING_TO_RAW(V_STR, 'UTF8');
V_TYPE := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_ECB +
DBMS_CRYPTO.PAD_PKCS5;
V_STR_RAW := DBMS_CRYPTO.ENCRYPT(SRC => V_STR_RAW,
typ => V_TYPE,
key => V_KEY_RAW);
V_RETURN_STR := RAWTOHEX(V_STR_RAW);
RETURN V_RETURN_STR;
END;
解密函数(调用上面的处理key的函数):
CREATE OR REPLACE FUNCTION FUN_AES_DECRYPT(V_STR VARCHAR2, V_KEY VARCHAR2)
RETURN VARCHAR2 AS
V_KEY_RAW RAW(16);
V_STR_RAW RAW(2000);
V_RETURN_STR VARCHAR2(2000);
V_TYPE PLS_INTEGER;
V_KEYSTR VARCHAR2(4000);
BEGIN
V_KEYSTR := FUN_GENERATE_AESKEY(V_KEY);
V_KEY_RAW := UTL_I18N.STRING_TO_RAW(V_KEYSTR, 'UTF8');
V_STR_RAW := HEXTORAW(V_STR);
V_TYPE := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_ECB +
DBMS_CRYPTO.PAD_PKCS5;
V_STR_RAW := DBMS_CRYPTO.DECRYPT(SRC => V_STR_RAW,
typ => V_TYPE,
key => V_KEY_RAW);
V_RETURN_STR := UTL_I18N.RAW_TO_CHAR(V_STR_RAW, 'UTF8');
RETURN V_RETURN_STR;
END;
加密写法:
SELECT FUN_AES_ENCRYPT('一二三','key1key1key1key1key1QAZ') FROM DUAL;
加密结果:C796C6C418AA82A90FC7C326102CF119
解密写法:
SELECT FUN_AES_DECRYPT('C796C6C418AA82A90FC7C326102CF119','key1key1key1key1key1QAZ') FROM DUAL;
解密结果:一二三
三、Java AES实现
测试类:
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class AES implements IEncryptionDecryption {
private static final Logger logger = LogManager.getLogger(AES.class);
public static void main(String... args) throws Exception {
String key = "key1key1key1key1key1QAZ";
AES aes = new AES();
String result = aes.encrpt("一二三", key);
System.out.println("加密结果:" + result); //加密
System.out.println("解密结果:" + aes.decrpt(result, key)); //解密加密的数据
}
/**
* AES解密
*
* @param data 待解密字符串
* @param key 解密秘钥
* @return 解密结果
* @throws Exception 抛出本方法的执行异常
*/
public String decrpt(String data, String key) throws Exception {
final Cipher decryptCipher = Cipher.getInstance("AES");
decryptCipher.init(Cipher.DECRYPT_MODE, generateMySQLAESKey(key, StandardCharsets.UTF_8));
return new String(decryptCipher.doFinal(Hex.decodeHex(data.toCharArray())),
StandardCharsets.UTF_8);
}
/**
* AES加密
*
* @param data 待加密字符串
* @param key 加密秘钥
* @return 加密结果
* @throws Exception 抛出本方法的执行异常
*/
public String encrpt(String data, String key) throws Exception {
final Cipher encryptCipher = Cipher.getInstance("AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, generateMySQLAESKey(key, StandardCharsets.UTF_8));
char[] code = Hex.encodeHex(encryptCipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
StringBuilder builder = new StringBuilder();
for (char d : code) {
builder.append(d);
}
return builder.toString().toUpperCase();
}
/**
* 生成AES加解密用的SecretKeySpec
*
* @param key 加解密用的秘钥
* @param charset 编码设置 默认UTF-8
* @return SecretKeySpec实例
*/
public static SecretKeySpec generateMySQLAESKey(String key, Charset charset) {
try {
final byte[] finalKey = new byte[16];
int i = 0;
for (byte b : key.getBytes(charset)) {
finalKey[i++ % 16] ^= b;
}
//System.out.println(new String(finalKey));
return new SecretKeySpec(finalKey, "AES");
} catch (Exception e) {
logger.error("执行异常,异常信息:", e);
return null;
}
}
}
加密结果:C796C6C418AA82A90FC7C326102CF119
解密结果:一二三
更新:解密方法返回结果不转大写,指定编码为UTF-8。
四、C# AES实现
测试类:
using System;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApp2
{
class AES
{
static void Main(string[] args)
{
string key = "key1key1key1key1key1QAZ";
string data = "一二三";
var result = encrpt(data, key);
Console.WriteLine("加密结果:" + result);
var dst = decrpt(result, key);
Console.WriteLine("解密结果:" + dst);
Console.ReadLine();
}
/// <summary>
/// AES初始化
/// </summary>
/// <param name="key">加密解密密钥</param>
public static Aes AESInit(string key)
{
byte[] finalKey = new byte[16];
byte[] srcKey = Encoding.UTF8.GetBytes(key);
int i = 0;
foreach (byte b in srcKey)
{
finalKey[i++ % 16] ^= b;
}
Aes aes = Aes.Create();
aes.Key = finalKey;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.ECB;
return aes;
}
/// <summary>
/// AES加密
/// </summary>
/// <param name="data">明文字符串</param>
/// <param name="key">加密密钥</param>
/// <returns>密文</returns>
public static string encrpt(string data, string key)
{
ICryptoTransform encryptor = AESInit(key).CreateEncryptor();
byte[] toEncryptArray = Encoding.UTF8.GetBytes(data);
byte[] resultArray = encryptor.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return ToHexStrFromBytes(resultArray);
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="data">密文16进制字符串</param>
/// <param name="key">解密密钥</param>
/// <returns></returns>
public static string decrpt(string data, string key)
{
ICryptoTransform decryptor = AESInit(key).CreateDecryptor();
byte[] toEncryptArray = ToBytesFromHexString(data);
byte[] resultArray = decryptor.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Encoding.UTF8.GetString(resultArray);
}
/// <summary>
/// 字节数组转16进制字符串
/// </summary>
/// <param name="byteDatas"></param>
/// <returns>字节数组对应的16进制字符串</returns>
public static string ToHexStrFromBytes(byte[] byteDatas)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < byteDatas.Length; i++)
{
builder.Append(byteDatas[i].ToString("X2"));
}
return builder.ToString().Trim();
}
/// <summary>
/// 16进制格式字符串转字节数组
/// </summary>
/// <param name="hexString">16进制字符串</param>
/// <returns>16进制字符串对应的字节数组</returns>
public static byte[] ToBytesFromHexString(string hexString)
{
hexString = hexString.Replace(" ", "");
if ((hexString.Length % 2) != 0)
{
hexString += " ";
}
byte[] returnBytes = new byte[hexString.Length / 2];
for (int i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return returnBytes;
}
}
}
加密结果:C796C6C418AA82A90FC7C326102CF119
解密结果:一二三