C#.NET与JAVA互通之AES加密解密V2024
视频:
注意点:
1. KEY 和 IV 从字符串转byte数组时,双方要约定好编码,一般是UTF8。
2.明文从字符串转byte数组时,双方要约定好编码,一般是UTF8,也可以GB2312,但不能Encoding.Default。
3.加密后的结果,从byte数组转字符串时,双方要约定好编码,一般是Base64字符串。
4.NET 的PKCS7Padding 对应 JAVA 的:PKCS5Padding
5. AES128指的是密钥长度为16个字符串(16Byte * 8 = 128 bit),AES256指的是密钥长度为32个字符串(32Byte * 8 = 256 bit)。
6.AES KEY 的变种(MD5 HASH 后,正好是AES256,或截取前16位作为AES128)
一、C#.NET:
AesUtil:
using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; namespace CommonUtils { /// <summary> /// AES 工具类,2024-06-07,runliuv。 /// </summary> public static class AesUtil { /// <summary> /// AES CBC 解密 /// </summary> /// <param name="decryptStr">要解密的串,base64字符串</param> /// <param name="aesKey">密钥</param> /// <param name="aesIV">IV</param> /// <returns></returns> public static string AesDecryptCBC(string decryptStr, string aesKey, string aesIV) { byte[] byteKEY = Encoding.UTF8.GetBytes(aesKey); byte[] byteIV = Encoding.UTF8.GetBytes(aesIV); byte[] byteDecrypt = Convert.FromBase64String(decryptStr); var _aes = new RijndaelManaged(); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.CBC; _aes.Key = byteKEY; _aes.IV = byteIV; var _crypto = _aes.CreateDecryptor(); byte[] decrypted = _crypto.TransformFinalBlock( byteDecrypt, 0, byteDecrypt.Length); _crypto.Dispose(); return Encoding.UTF8.GetString(decrypted); } /// <summary> /// AES CBC 解密,输入输出都是byte[],方便用各种编码转换成字符串 /// </summary> /// <param name="byteDecrypt"></param> /// <param name="byteKEY"></param> /// <param name="byteIV"></param> /// <returns></returns> public static byte[] AesDecryptCBC(byte[] byteDecrypt, byte[] byteKEY, byte[] byteIV) { var _aes = new RijndaelManaged(); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.CBC; _aes.Key = byteKEY; _aes.IV = byteIV; var _crypto = _aes.CreateDecryptor(); byte[] decrypted = _crypto.TransformFinalBlock( byteDecrypt, 0, byteDecrypt.Length); _crypto.Dispose(); return decrypted; } /// <summary> /// AES CBC 加密,输出base64字符串。 /// </summary> /// <param name="content"></param> /// <param name="aesKey"></param> /// <param name="aesIV"></param> /// <returns></returns> public static string AesEncryptCBC(string content, string aesKey, string aesIV) { byte[] byteKEY = Encoding.UTF8.GetBytes(aesKey); byte[] byteIV = Encoding.UTF8.GetBytes(aesIV); byte[] byteContnet = Encoding.UTF8.GetBytes(content); var _aes = new RijndaelManaged(); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.CBC; _aes.Key = byteKEY; _aes.IV = byteIV; var _crypto = _aes.CreateEncryptor(); byte[] decrypted = _crypto.TransformFinalBlock( byteContnet, 0, byteContnet.Length); _crypto.Dispose(); return Convert.ToBase64String(decrypted); } /// <summary> /// AES CBC 加密,输入输出都是byte[],方便用各种编码转换成字符串 /// </summary> /// <param name="byteContnet"></param> /// <param name="byteKEY"></param> /// <param name="byteIV"></param> /// <returns></returns> public static byte[] AesEncryptCBC(byte[] byteContnet, byte[] byteKEY, byte[] byteIV) { var _aes = new RijndaelManaged(); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.CBC; _aes.Key = byteKEY; _aes.IV = byteIV; var _crypto = _aes.CreateEncryptor(); byte[] decrypted = _crypto.TransformFinalBlock( byteContnet, 0, byteContnet.Length); _crypto.Dispose(); return decrypted; } public static string AesEncryptECB(string content, string aesKey) { byte[] byteKEY = Encoding.UTF8.GetBytes(aesKey); byte[] byteContnet = Encoding.UTF8.GetBytes(content); var _aes = new RijndaelManaged(); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.ECB; _aes.Key = byteKEY; var _crypto = _aes.CreateEncryptor(); byte[] decrypted = _crypto.TransformFinalBlock( byteContnet, 0, byteContnet.Length); _crypto.Dispose(); return Convert.ToBase64String(decrypted); } public static string AesDecryptECB(string decryptStr, string aesKey) { byte[] byteKEY = Encoding.UTF8.GetBytes(aesKey); byte[] byteDecrypt = Convert.FromBase64String(decryptStr); var _aes = new RijndaelManaged(); _aes.Padding = PaddingMode.PKCS7; _aes.Mode = CipherMode.ECB; _aes.Key = byteKEY; var _crypto = _aes.CreateDecryptor(); byte[] decrypted = _crypto.TransformFinalBlock( byteDecrypt, 0, byteDecrypt.Length); _crypto.Dispose(); return Encoding.UTF8.GetString(decrypted); } } }
HashUtil
using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; namespace CommonUtils { public static class HashUtil { public static string GetMd5(string src) { MD5 md = MD5.Create(); byte[] bytes = Encoding.UTF8.GetBytes(src); byte[] buffer2 = md.ComputeHash(bytes); string str = ""; for (int i = 0; i < buffer2.Length; i++) { str = str + buffer2[i].ToString("x2"); } return str; } public static IDictionary<string, string> ModelToDic<T1>(T1 cfgItem) { IDictionary<string, string> sdCfgItem = new Dictionary<string, string>(); System.Reflection.PropertyInfo[] cfgItemProperties = cfgItem.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); foreach (System.Reflection.PropertyInfo item in cfgItemProperties) { string name = item.Name; object value = item.GetValue(cfgItem, null); if (value != null && (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String")) && !string.IsNullOrWhiteSpace(value.ToString())) { sdCfgItem.Add(name, value.ToString()); } } return sdCfgItem; } public static IDictionary<string, string> AsciiDictionary(IDictionary<string, string> sArray) { IDictionary<string, string> asciiDic = new Dictionary<string, string>(); string[] arrKeys = sArray.Keys.ToArray(); Array.Sort(arrKeys, string.CompareOrdinal); foreach (var key in arrKeys) { string value = sArray[key]; asciiDic.Add(key, value); } return asciiDic; } public static string BuildQueryString(IDictionary<string, string> sArray) { //拼接 K=V&A=B&c=1 这种URL StringBuilder sc = new StringBuilder(); foreach (var item in sArray) { string name = item.Key; string value = item.Value; if (!string.IsNullOrWhiteSpace(value)) { sc.AppendFormat("{0}={1}&", name, value); } } string fnlStr = sc.ToString(); fnlStr = fnlStr.TrimEnd('&'); return fnlStr; } } }
.NET 使用:
using CommonUtils; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { try { //.NET 的PKCS7 对应 JAVA 的:PKCS5Padding TestAesCbc(); TestAesECB(); //JAVA加密,.NET 解密 string javaStr = "FfRdTUVCFSknpYNXoMHQOAQNEqkHY1uU19u/3wWH6Js="; Console.WriteLine("JAVA加密后的字符串:" + javaStr); string aesKey = "1234567890123456";//AES KEY 与 JAVA 保持一致 string decryptedStr = AesUtil.AesDecryptECB(javaStr, aesKey); Console.WriteLine("自加,自解:" + decryptedStr); //有的AES加密,不是直接把字符串当KEY使用,而是把原密钥 MD5 HASH后,作为AES 的KEY。 TestAesKeyTrap(); } catch (Exception ex) { Console.WriteLine("ex!" + ex.Message); } Console.WriteLine("结束!"); Console.ReadKey(); } static void TestAesCbc() { Console.WriteLine("-- TestAesCbc --" ); string aesKey = "1234567890123456";//16位或32位 string aesIV = "abcdefghABCDEFGH"; string aa = "这边是 .net aes 加密"; Console.WriteLine("待加密字符串:" + aa); string encryptedStr = AesUtil.AesEncryptCBC(aa, aesKey, aesIV); Console.WriteLine("加密字符串:" + encryptedStr); //自加,自解 string decryptedStr = AesUtil.AesDecryptCBC(encryptedStr, aesKey, aesIV); Console.WriteLine("自加,自解:" + decryptedStr); } static void TestAesECB() { Console.WriteLine("-- TestAesECB --"); string aesKey = "1234567890123456";//16位或32位 string aa = ".net aes 加密"; Console.WriteLine("待加密字符串:" + aa); string encryptedStr = AesUtil.AesEncryptECB(aa, aesKey); Console.WriteLine("加密字符串:" + encryptedStr); //自加,自解 string decryptedStr = AesUtil.AesDecryptECB(encryptedStr, aesKey); Console.WriteLine("自加,自解:" + decryptedStr); } /// <summary> /// AES KEY 的变种(MD5 HASH 后,正好是AES256,或截取前16位作为AES128) /// </summary> static void TestAesKeyTrap() { Console.WriteLine("-- TestAesECB --"); string orgKey = "HelloWorld";//不够16位或32位 //string aesKey = "HelloWorld";//不够16位或32位 string aesKey = HashUtil.GetMd5(orgKey).ToLower().Substring(16); Console.WriteLine("GetMd5后的长度:" + aesKey.Length.ToString()); string aa = ".net aes 加密"; Console.WriteLine("待加密字符串:" + aa); string encryptedStr = AesUtil.AesEncryptECB(aa, aesKey); Console.WriteLine("加密字符串:" + encryptedStr); //自加,自解 string decryptedStr = AesUtil.AesDecryptECB(encryptedStr, aesKey); Console.WriteLine("自加,自解:" + decryptedStr); } } }
二、JAVA:
AesUtil:
package org.runliuv; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class AesUtil { private static final String charset = "UTF-8"; /** * 加密 * @param content * @param key * @param iv * @return * @throws Exception */ public static String AesEncryptCBC(String content, String key, String iv) throws Exception { //明文 byte[] contentBytes = content.getBytes(charset); //AES KEY byte[] keyBytes = key.getBytes(charset); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); //AES IV byte[] initParam = iv.getBytes(charset); IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec); byte[] byEnd = cipher.doFinal(contentBytes); //加密后的byte数组转BASE64字符串 String strEnd = Base64.getEncoder().encodeToString(byEnd); return strEnd; } /** * 解密 * @param content * @param key * @param iv * @return * @throws Exception */ public static String AesDecryptCBC(String content, String key, String iv) throws Exception { //反向解析BASE64字符串为byte数组 byte[] encryptedBytes = Base64.getDecoder().decode(content); //AES KEY byte[] keyBytes = key.getBytes(charset); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); //AES IV byte[] initParam = iv.getBytes(charset); IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec); byte[] byEnd = cipher.doFinal(encryptedBytes); //加密后的byte数组直接转字符串 String strEnd = new String(byEnd, charset); return strEnd; } public static String AesEncryptECB(String content, String key) throws Exception { //明文 byte[] contentBytes = content.getBytes(charset); //AES KEY byte[] keyBytes = key.getBytes(charset); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] byEnd = cipher.doFinal(contentBytes); //加密后的byte数组转BASE64字符串 String strEnd = Base64.getEncoder().encodeToString(byEnd); return strEnd; } public static String AesDecryptECB(String content, String key) throws Exception { //反向解析BASE64字符串为byte数组 byte[] encryptedBytes = Base64.getDecoder().decode(content); //AES KEY byte[] keyBytes = key.getBytes(charset); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] byEnd = cipher.doFinal(encryptedBytes); //加密后的byte数组直接转字符串 String strEnd = new String(byEnd, charset); return strEnd; } }
java使用:
package org.runliuv; /** * Hello world! * */ public class App { public static void main( String[] args ) { try { TestAesCbc(); TestAesECB(); //.net aes cbc 加,JAVA 解密 String netStr="Mrs6Ufo2Y4ulWiksFSWFnUNKVSO5COruAlgQ5ws7FL0="; System.out.println("-- .net 加密后的内容:" + netStr); String aesKey = "1234567890123456";//与.net 保持一致 String aesIV = "abcdefghABCDEFGH";//与.net 保持一致 String decryptedStr = AesUtil.AesDecryptCBC(netStr, aesKey, aesIV); System.out.println(".net aes cbc 加,JAVA 解密:" + decryptedStr); }catch (Exception ex){ System.out.println( "ex!"+ex.getMessage() ); } System.out.println( "结束!" ); } static void TestAesCbc(){ try { System.out.println("-- TestAesCbc --" ); //16位 (32位,jdk8 老版本,需要放JAR包才支持32位的) String aesKey = "1234567890123456"; String aesIV = "abcdefghABCDEFGH"; String aa = "java aes 加密,2024-06-07。"; System.out.println("待加密字符串:" + aa); String encryptedStr = AesUtil.AesEncryptCBC(aa, aesKey, aesIV); System.out.println("加密字符串:" + encryptedStr); //自加,自解 String decryptedStr = AesUtil.AesDecryptCBC(encryptedStr, aesKey, aesIV); System.out.println("自加,自解:" + decryptedStr); }catch (Exception ex){ System.out.println( "ex!"+ex.getMessage() ); } } static void TestAesECB(){ try { System.out.println("-- TestAesECB --" ); //16位 (32位,jdk8 老版本,需要放JAR包才支持32位的) String aesKey = "1234567890123456"; String aa = "java aes 加密,2024-06-07。"; System.out.println("待加密字符串:" + aa); String encryptedStr = AesUtil.AesEncryptECB(aa, aesKey); System.out.println("加密字符串:" + encryptedStr); //自加,自解 String decryptedStr = AesUtil.AesDecryptECB(encryptedStr, aesKey); System.out.println("自加,自解:" + decryptedStr); }catch (Exception ex){ System.out.println( "ex!"+ex.getMessage() ); } } }
-