AES加密(java和C#)

需求:Java和C#进行数据交互,互相采用AES/CBC/PKCS5Padding进行加解密

Java加密和解密的代码如下:

/**
 * 加密 1.构造密钥生成器 2.根据 ecnodeRules 规则初始化密钥生成器 3.产生密钥 4.创建和初始化密码器 5.内容加密 6.返回字符串
 * @param encodeRules 密钥规则,类似于密钥
 * @param content 待加密内容
 * @return
 */
public static String AESEncode(String encodeRules, String content) {
    // 初始化向量,必须 16 位
    String ivStr = "AESCBCPKCS5Paddi";
    try {
        // 1.构造密钥生成器,指定为 AES 算法,不区分大小写
        KeyGenerator keygen = KeyGenerator.getInstance("AES");
        // 新增下面两行,处理 Linux 操作系统下随机数生成不一致的问题
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(encodeRules.getBytes());
        keygen.init(128, secureRandom);
        // 3.产生原始对称密钥
        SecretKey original_key = keygen.generateKey();
        // 4.获得原始对称密钥的字节数组
        byte[] raw = original_key.getEncoded();
        System.out.println(Base64.getEncoder().encodeToString(raw));

        // 5.根据字节数组生成 AES 密钥
        SecretKey key = new SecretKeySpec(raw, "AES");
        // 6.根据指定算法 AES 自成密码器
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的 KEY
        //// 指定一个初始化向量 (Initialization vector,IV), IV 必须是 16 位
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivStr.getBytes("UTF-8")));
        // 8.获取加密内容的字节数组(这里要设置为 utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
        byte[] byte_encode = content.getBytes("utf-8");
        // 9.根据密码器的初始化方式--加密:将数据加密
        byte[] byte_AES = cipher.doFinal(byte_encode);
        // 10.将加密后的数据转换为字符串
        // 这里用 Base64Encoder 中会找不到包
        // 解决办法:
        // 在项目的 Build path 中先移除 JRE System Library,再添加库 JRE System
        // Library,重新编译后就一切正常了。
        String AES_encode = new String(Base64.getEncoder().encode(byte_AES));
        // 11.将字符串返回
        return AES_encode;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }
    // 如果有错就返加 nulll
    return null;
}

/**
 * 解密 解密过程: 1.同加密1-4步 2.将加密后的字符串反纺成byte[]数组 3.将加密内容解密
 * @param encodeRules 密钥规则,类似于密钥
 * @param content 待加密内容
 * @return
 */
public static String AESDecode(String encodeRules, String content) {
    // 初始化向量,必须16位
    String ivStr = "AESCBCPKCS5Paddi";
    try {
        // 1.构造密钥生成器,指定为AES算法,不区分大小写
        KeyGenerator keygen = KeyGenerator.getInstance("AES");
        // 新增下面两行,处理 Linux 操作系统下随机数生成不一致的问题
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(encodeRules.getBytes());
        keygen.init(128, secureRandom);
        // 3.产生原始对称密钥
        SecretKey original_key = keygen.generateKey();
        // 4.获得原始对称密钥的字节数组
        byte[] raw = original_key.getEncoded();
        // 5.根据字节数组生成AES密钥
        SecretKey key = new SecretKeySpec(raw, "AES");
        // 6.根据指定算法AES自成密码器
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
        //// 指定一个初始化向量 (Initialization vector,IV), IV 必须是16位
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivStr.getBytes("UTF-8")));
        // 8.将加密并编码后的内容解码成字节数组
        byte[] byte_content = Base64.getDecoder().decode(content);
        /*
         * 解密
         */
        byte[] byte_decode = cipher.doFinal(byte_content);
        String AES_decode = new String(byte_decode, "utf-8");
        return AES_decode;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }
    // 如果有错就返加nulll
    return null;
}

这里面有一个情况,就是构建密钥生成器,C#里面有一个类库BouncyCastle.Crypto.dll完全可以把Java的代码翻译过来,但是翻译过来就卡在了密钥生成器这里,大家可以百度“java SecureRandom”就会知道其中的原因。后来通过不断查找资料并参考了:https://www.cnblogs.com/sheng9hhd/articles/11125705.html

 

 

最终实现了如下的C#加解密代码:

public class AESHelper
{
    /// <summary>
    /// 用种子获取密钥字节
    /// </summary>
    /// <param name="strKey">密钥种子</param>
    /// <param name="encoding">编码格式</param>
    /// <param name="nLen">密钥长度(一般为16,不清楚时不要随意动)</param>
    /// <returns></returns>
    public static byte[] GetKeyBySeed(string strKey, Encoding encoding,int nLen =16)
    {
        byte[] bySeed = encoding.GetBytes(strKey);
        byte[] byKeyArray = null;
        using (var st = new SHA1CryptoServiceProvider())
        {
            using (var nd = new SHA1CryptoServiceProvider())
            {
                var rd = nd.ComputeHash(st.ComputeHash(bySeed));
                byKeyArray = rd.Take(nLen).ToArray();
            }
        }
        return byKeyArray;
    }

    /// <summary>
    ///  加密 参数:string
    /// </summary>
    /// <param name="strCon">加密内容</param>
    /// <param name="byteKey">密钥字节数组</param>
    /// <param name="strIv">向量(注意目前只研究支持16位长度)</param>
    /// <param name="encoding">编码方式</param>
    /// <returns>string:密文</returns>
    public static string Encrypt(string strCon, byte[] byteKey, string strIv, Encoding encoding)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(strCon))
            {
                return null;
            }

            byte[] byCon = encoding.GetBytes(strCon);
            var rm = new RijndaelManaged
            {
                IV = encoding.GetBytes(strIv),
                Key = byteKey,
                Mode = CipherMode.CBC,
                Padding = PaddingMode.PKCS7
            };
            ICryptoTransform cTransform = rm.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(byCon, 0, byCon.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        catch
        {
            return "";
        }
    }

    public static string Decrypt(string strCon, byte[] byteKey, string strIv, Encoding encoding)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(strCon))
            {
                return null;
            }

            byte[] byCon =Convert.FromBase64String(strCon);
            var rm = new RijndaelManaged
            {
                IV = encoding.GetBytes(strIv),
                Key = byteKey,
                Mode = CipherMode.CBC,
                Padding = PaddingMode.PKCS7
            };
            ICryptoTransform cTransform = rm.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(byCon, 0, byCon.Length);
            return encoding.GetString(resultArray);
        }
        catch
        {
            return "";
        }
    }

}

上面的C#代码和Java代码可以实现互相加解密,同时也要感谢我的同事”陈江”的鼎立支持。

posted @ 2019-09-25 14:37  段江涛IT  阅读(5230)  评论(11编辑  收藏  举报
页脚HTML代码