AES加密解密,MySQL函数、Oracle自定义函数、Java、C#互通的AES加密解密互通实现

5 篇文章 1 订阅
2 篇文章 0 订阅

一、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
解密结果:一二三

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值