C#.NET与JAVA互通之AES加密解密V2024
视频:
注意点:
- KEY 和 IV 从字符串转byte数组时,双方要约定好编码,一般是UTF8。
2.明文从字符串转byte数组时,双方要约定好编码,一般是UTF8,也可以GB2312,但不能Encoding.Default。
3.加密后的结果,从byte数组转字符串时,双方要约定好编码,一般是Base64字符串。
4.NET 的PKCS7Padding 对应 JAVA 的:PKCS5Padding
- 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() );
}
}
}