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

时间:2023-02-25 08:26:50
 

1.情景展示

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

2.原因分析

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

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

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

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

  参数说明:

  加密模式:ECB(默认值)、CBC
  填充模式:PKCS5Padding(java只有这一种,其它语言使用PKCS7Padding即可,5和7没有区别)
  数据块:128位(java只有这一种)

3.解决方案

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

  方案一:推荐使用

  思路:

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

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

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

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

  流程图:

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

  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 解密

/// <summary>
/// AES 解密
/// </summary>
/// <param name="toDecrypt">密文(待解密)</param>
/// <param name="hexKey">密钥(16进制)</param>
/// <returns></returns>
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#一样。

/// <summary>
/// 将16进制字符串转二进制
/// </summary>
/// <param name="hexString">需要进行解码的字符串</param>
/// <returns></returns>
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;
} /// <summary>
/// AES 解密
/// </summary>
/// <param name="str">密文(待解密)</param>
/// <param name="key">密钥</param>
/// <returns></returns>
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加密

/// <summary>
/// AES 加密
/// </summary>
/// <param name="toEncrypt">明文(待加密)</param>
/// <param name="hexKey">密钥(确保java提供给你的是16进制密钥,不是十进制!)</param>
/// <returns>AES加密结果</returns>
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);
} /// <summary>
/// 将一个byte数组转换成一个格式化的16进制字符串
/// </summary>
/// <param name="data">byte数组</param>
/// <returns>格式化的16进制字符串</returns>
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文件

<script type="text/javascript" src="crypt/crypto-js.js"></script>

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);//张三 }

  

 

java与C#、.NET AES加密、解密 解决方案的更多相关文章

  1. java使用RSA与AES加密解密

    首先了解下,什么是堆成加密,什么是非对称加密? 对称加密:加密与解密的密钥是相同的,加解密速度很快,比如AES 非对称加密:加密与解密的秘钥是不同的,速度较慢,比如RSA 先看代码(先会用在研究) 相 ...

  2. 【java工具类】AES加密解密

    百度百科一下,AES:高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦*采用的一种区块加密标准.这个标准 ...

  3. 你真的了解字典&lpar;Dictionary&rpar;吗&quest; C&num; Memory Cache 踩坑记录 &period;net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的&period;NET开源项目 &period;net 笔试面试总结&lpar;3&rpar; &period;net 笔试面试总结&lpar;2&rpar; 依赖注入 C&num; RSA 加密 C&num;与Java AES 加密解密

    你真的了解字典(Dictionary)吗?   从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...

  4. AES加密解密通用版Object-C &sol; C&num; &sol; JAVA

    1.无向量 128位 /// <summary> /// AES加密(无向量) /// </summary> /// <param name="plainByt ...

  5. C&num;&comma; Java&comma; PHP&comma; Python和Javascript几种语言的AES加密解密实现&lbrack;转载&rsqb;

    原文:http://outofmemory.cn/code-snippet/35524/AES-with-javascript-java-csharp-python-or-php c#里面的AES加密 ...

  6. java使用AES加密解密 AES-128-ECB加密

    java使用AES加密解密 AES-128-ECB加密 import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; impo ...

  7. AES加密解密——AES在JavaWeb项目中前台JS加密,后台Java解密的使用

    一:前言 在软件开发中,经常要对数据进行传输,数据在传输的过程中可能被拦截,被监听,所以在传输数据的时候使用数据的原始内容进行传输的话,安全隐患是非常大的.因此就要对需要传输的数据进行在客户端进行加密 ...

  8. php与java通用AES加密解密算法

    AES指高级加密标准(Advanced Encryption Standard),是当前最流行的一种密码算法,在web应用开发,特别是对外提供接口时经常会用到,下面是我整理的一套php与java通用的 ...

  9. Java 关于密码处理的工具类&lbrack;MD5编码&rsqb;&lbrack;AES加密&sol;解密&rsqb;

    项目中又遇到了加密问题,又去翻了半天,然后做测试,干脆就把常用的两类小结一下. 1.第一种所谓的MD5加密 其实也不算加密,只是基于Hash算法的不可逆编码而已,等于说,一旦经过MD5处理,是不可能从 ...

  10. C&num; 实现 JAVA AES加密解密&lbrack;原创&rsqb;

    以下是网上普遍能收到的JAVA AES加密解密方法. 因为里面用到了KeyGenerator 和 SecureRandom,但是.NET 里面没有这2个类.无法使用安全随机数生成KEY. 我们在接收J ...

随机推荐

  1. 运动 js

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. IEnumerable接口的Aggregate方法

    以前小猪为了累加一个集合中的类容通常会写出类似这样的C#代码: string result ="": foreach (var item in items) { result+=i ...

  3. MVC权限验证之ActionFilterAttribute

    参考:http://www.cnblogs.com/waitingfor/archive/2011/12/27/2303784.html ActionFilterAttribute是Action过滤类 ...

  4. Json-lib - java&period;util&period;Date 转换问题

    使用 JSON-lib 将 java.util.Date 对象直接转换成 JSON 字符串时,得到的通常不是想要格式: System.out.println(JSONSerializer.toJSON ...

  5. 李洪强iOS开发Swift篇—06&lowbar;流程控制

    李洪强iOS开发Swift篇—06_流程控制 一.swift中的流程控制 Swift支持的流程结构如下: 循环结构:for.for-in.while.do-while 选择结构:if.switch 注 ...

  6. Java中的自动装箱与拆箱

    自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...

  7. git 创建分支,删除分支,管理分支

    参考  http://blog.csdn.net/dijason/article/details/9042425   查看分支: 1 查看本地分支: $ git branch 2 查看远程分支 $ g ...

  8. 在Oracle中添加用户登录名称

    第一步,打开Oracle客户端单击 “帮助”-->"支持信息"-->”TNS名“,加入红色部分.页面如下: 第二步,再次打开Oracle客户端时,就会显示数据库了,只需 ...

  9. AJAX&plus;springmvc遇到的问题

    当我使用AJAX将表单的值传入处理器中后,经过了一个判断再进行页面跳转时,不能在处理器中使用重定向,它会将重定向的页面内容在AJAX的data中显示出来而不是显示一个页面 所以只能在AJAX 的suc ...

  10. TODO java 作业-梭哈--待完成

    作业:定义一个类,该类用于封装一桌梭哈游戏,这个类应该包含桌上剩下的牌的信息,并包含5个玩家的状态的信息,他们各自的位置,游戏状态(正在游戏或已放弃),手上已有的牌等信息.如果有可能,这个类还应该实现 ...