java .net aes解密_java与C#、.NET AES加密、解密 解决方案

1.情景展示

Java提供的密钥,C#无法解密。

2.原因分析

在Java中,AES的实际密钥需要用到KeyGenerator 和 SecureRandom,但是C#和.NET 里面没有这2个类,

所以,无法使用安全随机数生成KEY,进而导致解密失败。

Java对密钥做的进一步处理:

4b4a5f015ddab3dec79637c05b63981e.png

参数说明:

加密模式:ECB(默认值)、CBC

填充模式:PKCS5Padding(java只有这一种,其它语言使用PKCS7Padding即可,5和7没有区别)

数据块:128位(java只有这一种)

3.解决方案

超级简单的方法见最后(20190921)

方案一:推荐使用

思路:

将由Java生成的AES所需要的实际密钥,提供给C#,然后C#用这个实际的key去解密。

由于C#中byte范围是[0,255],而Java中的byte范围是[-128,127],所以,我们需要对生成的二进制密钥进行处理。

1449a48bdab471fab0e3aaa36ab5848e.png

因此,Java作为密钥的提供方,需要将二进制转成16进制,C#将接收到的16进制密钥转换成二进制即可。

流程图:

0ae3b859a9af0dd721231be91b739b51.png

java AES 加密

import java.security.SecureRandom;

import javax.crypto.Cipher;

import javax.crypto.KeyGenerator;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;

import org.apache.log4j.Logger;

/**

* AES加密算法工具类

* @explain 可逆算法:加密、解密

* AES/ECB/PKCS5Padding

* @author Marydon

* @creationTime 2018年7月7日下午2:17:43

* @version 3.0

* @since 2.0

* @email marydon20170307@163.com

*/

public class AESUtils {

private static Logger log = Logger.getLogger(AESUtils.class);

// 定义字符集

private static final String ENCODING = "UTF-8";

/**

* 根据提供的密钥生成AES专用密钥

* @explain

* @param password

* 可以是中文、英文、16进制字符串

* @return AES密钥

* @throws Exception

*/

public static byte[] generateKey(String password) throws Exception {

byte[] keyByteArray = null;

// 创建AES的Key生产者

KeyGenerator kgen = KeyGenerator.getInstance("AES");

// 利用用户密码作为随机数初始化

// 指定强随机数的生成方式

// 兼容linux

SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

random.setSeed(password.getBytes(ENCODING));

kgen.init(128, random);// 只能是128位

// 根据用户密码,生成一个密钥

SecretKey secretKey = kgen.generateKey();

// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回null。

keyByteArray = secretKey.getEncoded();

return keyByteArray;

}

/**

* AES加密字符串

* @param content

* 需要被加密的字符串

* @param password

* 加密需要的密码

* @return 16进制的密文(密文的长度随着待加密字符串的长度变化而变化,至少32位)

*/

public static String encrypt(String content, String password) {

String cipherHexString = "";// 返回字符串

try {

// 转换为AES专用密钥

byte[] keyBytes = generateKey(password);

SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");

// 将待加密字符串转二进制

byte[] clearTextBytes = content.getBytes(ENCODING);

// 创建密码器,默认参数:AES/EBC/PKCS5Padding

Cipher cipher = Cipher.getInstance("AES");

// 初始化为加密模式的密码器

cipher.init(Cipher.ENCRYPT_MODE, sks);

// 加密结果

byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);

// byte[]-->hexString

cipherHexString = ByteUtils.toHex(cipherTextBytes);

} catch (Exception e) {

e.printStackTrace();

log.error("AES加密失败:" + e.getMessage());

}

log.info("AES加密结果:" + cipherHexString);

return cipherHexString;

}

}

先调用generateKey()方法,然后将二进制转换成16进制(如果byte[]转16进制不会,见文末链接)。

C# AES 解密

///

/// AES 解密

///

/// 密文(待解密)

/// 密钥(16进制)

///

public static string AesDecrypt(string toDecrypt, string hexKey)

{

if (string.IsNullOrEmpty(toDecrypt)) return null;

//将16进制的密文转为字节数组

var toDecryptArray = new byte[toDecrypt.Length / 2];

for (var x = 0; x < toDecryptArray.Length; x++)

{

var i = Convert.ToInt32(toDecrypt.Substring(x * 2, 2), 16);

toDecryptArray[x] = (byte)i;

}

//将16进制的秘钥转成字节数组

var keyArray = new byte[hexKey.Length / 2];

for (var x = 0; x < keyArray.Length; x++)

{

var i = Convert.ToInt32(hexKey.Substring(x * 2, 2), 16);

keyArray[x] = (byte)i;

}

RijndaelManaged rm = new RijndaelManaged

{

Key = keyArray,

Mode = CipherMode.ECB,//必须设置为ECB

Padding = PaddingMode.PKCS7//必须设置为PKCS7

};

ICryptoTransform cTransform = rm.CreateDecryptor();

Byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);

return Encoding.UTF8.GetString(resultArray);

}

测试

public static void main(String[] args) throws Exception {

String text = "Marydon";

String password = "521";

System.out.println(ByteUtils.toHex(generateKey(password)));// FB511ED54B1B3D71093309D4F6DEBD61

// 加密

String encrypt = encrypt(text, password);// 7468F296C547B321AE1086741BAC13C4

}

方案二:密钥使用16位的,自行百度。

方案三: 改变填充模式

Java默认的填充模式为PKCS5Padding,可以将Java和C#统一采用NoPadding,需要自己定义这种填充模式。

方案四:使用dll动态库实现。

2019/05/08

.NET的解决方案与C#一样。

///

/// 将16进制字符串转二进制

///

/// 需要进行解码的字符串

///

public static byte[] HexStrToByte(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;

}

///

/// AES 解密

///

/// 密文(待解密)

/// 密钥

///

public static string AesDecrypt(string str, string key)

{

if (string.IsNullOrEmpty(str)) return null;

//将16进制密文转为字节数组

var toEncryptArray = new byte[str.Length / 2];

for (var x = 0; x < toEncryptArray.Length; x++)

{

var i = Convert.ToInt32(str.Substring(x * 2, 2), 16);

toEncryptArray[x] = (byte)i;

}

//将16进制秘钥转成字节数组

var inputByteArray = HexStrToByte(key);

RijndaelManaged rm = new RijndaelManaged

{

Key = inputByteArray,

Mode = CipherMode.ECB,//必须设置为ECB

Padding = PaddingMode.PKCS7//必须设置为PKCS7

};

ICryptoTransform cTransform = rm.CreateDecryptor();

Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

return Encoding.UTF8.GetString(resultArray);

}

2019/08/28

C# AES加密

///

/// AES 加密

///

/// 明文(待加密)

/// 密钥(确保java提供给你的是16进制密钥,不是十进制!)

/// AES加密结果

public static string AesEncrypt(string toEncrypt, string hexKey)

{

//将16进制秘钥转成字节数组

var keyArray = new byte[hexKey.Length / 2];

for (var x = 0; x < keyArray.Length; x++)

{

var i = Convert.ToInt32(hexKey.Substring(x * 2, 2), 16);

keyArray[x] = (byte)i;

}

byte[] toEncryptArray = Encoding.UTF8.GetBytes(toEncrypt);

RijndaelManaged rDel = new RijndaelManaged();

rDel.Key = keyArray;

rDel.Mode = CipherMode.ECB;

rDel.Padding = PaddingMode.PKCS7;

ICryptoTransform cTransform = rDel.CreateEncryptor();

byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

return ByteArrayToHexString(resultArray);

}

///

/// 将一个byte数组转换成一个格式化的16进制字符串

///

/// byte数组

/// 格式化的16进制字符串

public static string ByteArrayToHexString(byte[] data)

{

StringBuilder sb = new StringBuilder(data.Length * 3);

foreach (byte b in data)

{

sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));

}

return sb.ToString().ToUpper();

}

解决方案五:20190921 最省心

在实际对接过程中,每次都要和对方解释半天才能搞定,真是浪费时间,我们不妨这样想一想:

由于密码生成器是java所独有的,其它语言都不支持(IOS,ANDROID,C#,.NET等),既然java这么特立独行,我们是不是可以不使用这个密码生成器呢?

经实践发现,只要不用java特有的密钥生成器对初始密钥做进一步处理,不论是哪一种语言,其加密结果都是一致的。

/**

* AES加密字符串(兼容任何语言)

* @explain 没有使用java独有的密码生成器

* @param content

* 需要被加密的字符串

* @param password

* 加密需要的密码

* @return 16进制的密文(密文的长度随着待加密字符串的长度变化而变化,至少32位)

*/

public static String encrypt(String content, String password) {

String cipherHexString = "";// 返回字符串

try {

// 转换为AES专用密钥(这一步直接废弃)

// byte[] keyBytes = generateKey(password);

// 直接当做密钥使用(除java以外的语言,其它语言都是直接把它当做密钥)

byte[] keyBytes = password.getBytes(ENCODING);

SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");

// 将待加密字符串转byte[]

byte[] clearTextBytes = content.getBytes(ENCODING);

// 创建密码器

Cipher cipher = Cipher.getInstance("AES");

// 初始化为加密模式的密码器

cipher.init(Cipher.ENCRYPT_MODE, sks);

// 加密结果

byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);

// byte[]-->hexString

cipherHexString = ByteUtils.toHex(cipherTextBytes);

} catch (Exception e) {

e.printStackTrace();

log.error("AES加密失败:" + e.getMessage());

}

log.info("AES加密结果:" + cipherHexString);

return cipherHexString;

}

2019/10/14

php加密,解密

/**

* 加密

*

* @param $str

* @return string

*/

function encrypt($str){

$secret = $this->getSecret();

$data = openssl_encrypt($str, 'aes-128-ecb', $secret, OPENSSL_PKCS1_PADDING);

$data = bin2hex($data)

return $data;

}

/**

* 解密

*

* @param $str

*/

function decrypt($str){

$data = hex2bin($str);

$secret = $this->getSecret();

$data = openssl_decrypt($data, 'aes-128-ecb', $secret, OPENSSL_PKCS1_PADDING);

return $data;

}

/**

* 密钥处理

*/

function getSecret(){

// 16进制密钥

$hex = 'F07D896FD9098039D0F666525FD9EDE2';

// 转二进制

$hex = pack('H*', $hex);

return $hex;

}

2019/12/27

oracle加密

CREATE OR REPLACE FUNCTION FUN_ENCRYPT_AES(V_STR VARCHAR2,

V_KEY VARCHAR2)

RETURN VARCHAR2 AS

V_KEY_RAW RAW(32);

V_STR_RAW RAW(2000);

V_RETURN_STR VARCHAR2(2000);

V_TYPE PLS_INTEGER;

BEGIN

/*************************************************

加密函数 FUN_ENCRYPT_AES

入参:

V_STR 待加密字符串

V_KEY 密钥

返回值:

V_RETURN_STR 返回密文字符串,约定返回为 16进制密文字符串

加密方式 128/ebc/pkcs5

密钥位数:AES128 DBMS_CRYPTO.ENCRYPT_AES128

连接方式:EBC DBMS_CRYPTO.CHAIN_EBC

填充方式:PKCS5 DBMS_CRYPTO.PAD_PKCS5

**************************************************/

--将字符串varchar2转换成位串raw,并按照utf-8格式进行解析

V_KEY_RAW := UTL_I18N.STRING_TO_RAW(V_KEY, 'AL32UTF8');

/*V_KEY_RAW := '40146E5CA7AF57D01959C0FAFB7B7330';*/

V_STR_RAW := UTL_I18N.STRING_TO_RAW(V_STR, 'AL32UTF8');

/*注意:需保证当前登录用户有调用包DBMS_CRYPTO的权限*/

-- 指定:密钥算法、工作模式、填充方式

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也是需要Java提前转换成16进制的密钥。

2020/01/02

powerbuilder(PB)

//AES/ECB/PKCS5Padding 加密 以 Hex 编码返回

blob lbb_iv

ls_param_encrypt = _codec.HexEncode(_crypto.SymEncrypt(1, _codec.ToUTF8(ls_param_set), _codec.HexDecode(is_aes_key), _crypto.CIPHER_MODE_ECB, lbb_iv))

2020/01/08

javascript

前提:引入cryptojs文件

aes加密、解密

/**

* aes加密

* @param clearText 待加密字符串

* @param hexKey 加密密钥,由java提供(16进制)

* @explain AES/ECB/PKCS7Padding

* 偏移量(填充方式):PKCS7Padding,对应java的PKCS5Padding

* 加密模式:ECB

* return 加密结果(16进制)

*/

function encrypt(clearText, hexKey) {

var key = CryptoJS.enc.Hex.parse(hexKey);

var encrytedData = CryptoJS.AES.encrypt(clearText, key, {

mode : CryptoJS.mode.ECB,

padding : CryptoJS.pad.Pkcs7

});

return encrytedData.ciphertext.toString().toUpperCase();

}

/**

* aes解密

* @param cipherText 待解密字符串(16进制)

* @param hexKey 解密密钥,由java提供(16进制)

* return 解密结果(以utf-8进行编码)

*/

function decrypt(cipherText, hexKey) {

// 1.将16进制转换成数组

var hexArray = CryptoJS.enc.Hex.parse(cipherText);

// 2.将数组转换成base64字符串

var base64Str = CryptoJS.enc.Base64.stringify(hexArray);

// 3.将密钥转换成数组

var key = CryptoJS.enc.Hex.parse(hexKey);

// 4.解密

var decryptedData = CryptoJS.AES.decrypt(base64Str, key, {

mode : CryptoJS.mode.ECB,

padding : CryptoJS.pad.Pkcs7

});

// 5.以utf-8进行编码解密结果

return decryptedData.toString(CryptoJS.enc.Utf8);

}

测试

window.onload = function() {

var clearText = "张三";

var hexKey = "E341BACB74574E03051D2BB1FD48BD99";

var cipherText = encrypt(clearText, hexKey);

console.log(cipherText);//6E3AC3434E1F8C371EA81DDB124AA5D7

clearText = decrypt(cipherText,hexKey);

console.log(clearText);//张三

}

已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页