1.情景展示

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

2.原因分析

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

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

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

  参数说明:

  加密模式: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作为密钥的提供方,需要将二进制转成16进制,C#将接收到的16进制密钥转换成二进制即可。

  流程图:

  java AES 加密

  1. import java.security.SecureRandom;
  2. import javax.crypto.Cipher;
  3. import javax.crypto.KeyGenerator;
  4. import javax.crypto.SecretKey;
  5. import javax.crypto.spec.SecretKeySpec;
  6. import org.apache.log4j.Logger;
  7.  
  8. /**
  9. * AES加密算法工具类
  10. * @explain 可逆算法:加密、解密
  11. * AES/ECB/PKCS5Padding
  12. * @author Marydon
  13. * @creationTime 2018年7月7日下午2:17:43
  14. * @version 3.0
  15. * @since 2.0
  16. * @email marydon20170307@163.com
  17. */
  18. public class AESUtils {
  19.  
  20. private static Logger log = Logger.getLogger(AESUtils.class);
  21. // 定义字符集
  22. private static final String ENCODING = "UTF-8";
  23.  
  24. /**
  25. * 根据提供的密钥生成AES专用密钥
  26. * @explain
  27. * @param password
  28. * 可以是中文、英文、16进制字符串
  29. * @return AES密钥
  30. * @throws Exception
  31. */
  32. public static byte[] generateKey(String password) throws Exception {
  33. byte[] keyByteArray = null;
  34. // 创建AES的Key生产者
  35. KeyGenerator kgen = KeyGenerator.getInstance("AES");
  36. // 利用用户密码作为随机数初始化
  37. // 指定强随机数的生成方式
  38. // 兼容linux
  39. SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
  40. random.setSeed(password.getBytes(ENCODING));
  41. kgen.init(128, random);// 只能是128位
  42.  
  43. // 根据用户密码,生成一个密钥
  44. SecretKey secretKey = kgen.generateKey();
  45. // 返回基本编码格式的密钥,如果此密钥不支持编码,则返回null。
  46. keyByteArray = secretKey.getEncoded();
  47. return keyByteArray;
  48. }
  49.  
  50. /**
  51. * AES加密字符串
  52. * @param content
  53. * 需要被加密的字符串
  54. * @param password
  55. * 加密需要的密码
  56. * @return 16进制的密文(密文的长度随着待加密字符串的长度变化而变化,至少32位)
  57. */
  58. public static String encrypt(String content, String password) {
  59. String cipherHexString = "";// 返回字符串
  60. try {
  61. // 转换为AES专用密钥
  62. byte[] keyBytes = generateKey(password);
  63.  
  64. SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");
  65. // 将待加密字符串转二进制
  66. byte[] clearTextBytes = content.getBytes(ENCODING);
  67. // 创建密码器,默认参数:AES/EBC/PKCS5Padding
  68. Cipher cipher = Cipher.getInstance("AES");
  69. // 初始化为加密模式的密码器
  70. cipher.init(Cipher.ENCRYPT_MODE, sks);
  71. // 加密结果
  72. byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);
  73. // byte[]-->hexString
  74. cipherHexString = ByteUtils.toHex(cipherTextBytes);
  75. } catch (Exception e) {
  76. e.printStackTrace();
  77. log.error("AES加密失败:" + e.getMessage());
  78. }
  79. log.info("AES加密结果:" + cipherHexString);
  80. return cipherHexString;
  81. }
  82. }

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

  C# AES 解密

  1. /// <summary>
  2. /// AES 解密
  3. /// </summary>
  4. /// <param name="toDecrypt">密文(待解密)</param>
  5. /// <param name="hexKey">密钥(16进制)</param>
  6. /// <returns></returns>
  7. public static string AesDecrypt(string toDecrypt, string hexKey)
  8. {
  9. if (string.IsNullOrEmpty(toDecrypt)) return null;
  10. //将16进制的密文转为字节数组
  11. var toDecryptArray = new byte[toDecrypt.Length / 2];
  12. for (var x = 0; x < toDecryptArray.Length; x++)
  13. {
  14. var i = Convert.ToInt32(toDecrypt.Substring(x * 2, 2), 16);
  15. toDecryptArray[x] = (byte)i;
  16. }
  17.  
  18. //将16进制的秘钥转成字节数组
  19. var keyArray = new byte[hexKey.Length / 2];
  20. for (var x = 0; x < keyArray.Length; x++)
  21. {
  22. var i = Convert.ToInt32(hexKey.Substring(x * 2, 2), 16);
  23. keyArray[x] = (byte)i;
  24. }
  25.  
  26. RijndaelManaged rm = new RijndaelManaged
  27. {
  28. Key = keyArray,
  29. Mode = CipherMode.ECB,//必须设置为ECB
  30. Padding = PaddingMode.PKCS7//必须设置为PKCS7
  31. };
  32.  
  33. ICryptoTransform cTransform = rm.CreateDecryptor();
  34. Byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
  35.  
  36. return Encoding.UTF8.GetString(resultArray);
  37. } 

  测试

  1. public static void main(String[] args) throws Exception {
  2. String text = "Marydon";
  3. String password = "521";
  4. System.out.println(ByteUtils.toHex(generateKey(password)));// FB511ED54B1B3D71093309D4F6DEBD61
  5. // 加密
  6. String encrypt = encrypt(text, password);// 7468F296C547B321AE1086741BAC13C4
  7. }

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

  方案三: 改变填充模式

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

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

2019/05/08

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

  1. /// <summary>
  2. /// 将16进制字符串转二进制
  3. /// </summary>
  4. /// <param name="hexString">需要进行解码的字符串</param>
  5. /// <returns></returns>
  6. public static byte[] HexStrToByte(string hexString)
  7. {
  8. hexString = hexString.Replace(" ", "");
  9. if ((hexString.Length % 2) != 0)
  10. hexString += " ";
  11. byte[] returnBytes = new byte[hexString.Length / 2];
  12. for (int i = 0; i < returnBytes.Length; i++)
  13. returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
  14. return returnBytes;
  15. }
  16.  
  17. /// <summary>
  18. /// AES 解密
  19. /// </summary>
  20. /// <param name="str">密文(待解密)</param>
  21. /// <param name="key">密钥</param>
  22. /// <returns></returns>
  23. public static string AesDecrypt(string str, string key)
  24. {
  25. if (string.IsNullOrEmpty(str)) return null;
  26. //将16进制密文转为字节数组
  27. var toEncryptArray = new byte[str.Length / 2];
  28. for (var x = 0; x < toEncryptArray.Length; x++)
  29. {
  30. var i = Convert.ToInt32(str.Substring(x * 2, 2), 16);
  31. toEncryptArray[x] = (byte)i;
  32. }
  33.  
  34. //将16进制秘钥转成字节数组
  35. var inputByteArray = HexStrToByte(key);
  36.  
  37. RijndaelManaged rm = new RijndaelManaged
  38. {
  39. Key = inputByteArray,
  40. Mode = CipherMode.ECB,//必须设置为ECB
  41. Padding = PaddingMode.PKCS7//必须设置为PKCS7
  42. };
  43.  
  44. ICryptoTransform cTransform = rm.CreateDecryptor();
  45. Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
  46.  
  47. return Encoding.UTF8.GetString(resultArray);
  48. }

2019/08/28

C# AES加密

  1. /// <summary>
  2. /// AES 加密
  3. /// </summary>
  4. /// <param name="toEncrypt">明文(待加密)</param>
  5. /// <param name="hexKey">密钥(确保java提供给你的是16进制密钥,不是十进制!)</param>
  6. /// <returns>AES加密结果</returns>
  7. public static string AesEncrypt(string toEncrypt, string hexKey)
  8. {
  9. //将16进制秘钥转成字节数组
  10. var keyArray = new byte[hexKey.Length / 2];
  11. for (var x = 0; x < keyArray.Length; x++)
  12. {
  13. var i = Convert.ToInt32(hexKey.Substring(x * 2, 2), 16);
  14. keyArray[x] = (byte)i;
  15. }
  16.  
  17. byte[] toEncryptArray = Encoding.UTF8.GetBytes(toEncrypt);
  18. RijndaelManaged rDel = new RijndaelManaged();
  19. rDel.Key = keyArray;
  20. rDel.Mode = CipherMode.ECB;
  21. rDel.Padding = PaddingMode.PKCS7;
  22.  
  23. ICryptoTransform cTransform = rDel.CreateEncryptor();
  24. byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
  25.  
  26. return ByteArrayToHexString(resultArray);
  27. }
  28.  
  29. /// <summary>
  30. /// 将一个byte数组转换成一个格式化的16进制字符串
  31. /// </summary>
  32. /// <param name="data">byte数组</param>
  33. /// <returns>格式化的16进制字符串</returns>
  34. public static string ByteArrayToHexString(byte[] data)
  35. {
  36. StringBuilder sb = new StringBuilder(data.Length * 3);
  37. foreach (byte b in data)
  38. {
  39. sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
  40. }
  41. return sb.ToString().ToUpper();
  42. }

解决方案五:20190921 最省心

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

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

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

  1. /**
  2. * AES加密字符串(兼容任何语言)
  3. * @explain 没有使用java独有的密码生成器
  4. * @param content
  5. * 需要被加密的字符串
  6. * @param password
  7. * 加密需要的密码
  8. * @return 16进制的密文(密文的长度随着待加密字符串的长度变化而变化,至少32位)
  9. */
  10. public static String encrypt(String content, String password) {
  11. String cipherHexString = "";// 返回字符串
  12. try {
  13. // 转换为AES专用密钥(这一步直接废弃)
  14. // byte[] keyBytes = generateKey(password);
  15.  
  16. // 直接当做密钥使用(除java以外的语言,其它语言都是直接把它当做密钥)
  17. byte[] keyBytes = password.getBytes(ENCODING);
  18. SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");
  19. // 将待加密字符串转byte[]
  20. byte[] clearTextBytes = content.getBytes(ENCODING);
  21. // 创建密码器
  22. Cipher cipher = Cipher.getInstance("AES");
  23. // 初始化为加密模式的密码器
  24. cipher.init(Cipher.ENCRYPT_MODE, sks);
  25. // 加密结果
  26. byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);
  27. // byte[]-->hexString
  28. cipherHexString = ByteUtils.toHex(cipherTextBytes);
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. log.error("AES加密失败:" + e.getMessage());
  32. }
  33. log.info("AES加密结果:" + cipherHexString);
  34. return cipherHexString;
  35. }

2019/10/14

php加密,解密

  1. /**
  2. * 加密
  3. *
  4. * @param $str
  5. * @return string
  6. */
  7. function encrypt($str){
  8. $secret = $this->getSecret();
  9. $data = openssl_encrypt($str, 'aes-128-ecb', $secret, OPENSSL_PKCS1_PADDING);
  10. $data = bin2hex($data)
  11. return $data;
  12. }
  13.  
  14. /**
  15. * 解密
  16. *
  17. * @param $str
  18. */
  19. function decrypt($str){
  20. $data = hex2bin($str);
  21. $secret = $this->getSecret();
  22. $data = openssl_decrypt($data, 'aes-128-ecb', $secret, OPENSSL_PKCS1_PADDING);
  23. return $data;
  24. }
  25.  
  26. /**
  27. * 密钥处理
  28. */
  29. function getSecret(){
  30. // 16进制密钥
  31. $hex = 'F07D896FD9098039D0F666525FD9EDE2';
  32. // 转二进制
  33. $hex = pack('H*', $hex);
  34. return $hex;
  35. }

2019/12/27

oracle加密

  1. CREATE OR REPLACE FUNCTION FUN_ENCRYPT_AES(V_STR VARCHAR2,
  2. V_KEY VARCHAR2)
  3. RETURN VARCHAR2 AS
  4. V_KEY_RAW RAW(32);
  5. V_STR_RAW RAW(2000);
  6. V_RETURN_STR VARCHAR2(2000);
  7. V_TYPE PLS_INTEGER;
  8. BEGIN
  9. /*************************************************
  10. 加密函数 FUN_ENCRYPT_AES
  11. 入参:
  12. V_STR 待加密字符串
  13. V_KEY 密钥
  14. 返回值:
  15. V_RETURN_STR 返回密文字符串,约定返回为 16进制密文字符串
  16.  
  17. 加密方式 128/ebc/pkcs5
  18. 密钥位数:AES128 DBMS_CRYPTO.ENCRYPT_AES128
  19. 连接方式:EBC DBMS_CRYPTO.CHAIN_EBC
  20. 填充方式:PKCS5 DBMS_CRYPTO.PAD_PKCS5
  21. **************************************************/
  22. --将字符串varchar2转换成位串raw,并按照utf-8格式进行解析
  23. V_KEY_RAW := UTL_I18N.STRING_TO_RAW(V_KEY, 'AL32UTF8');
  24. /*V_KEY_RAW := '40146E5CA7AF57D01959C0FAFB7B7330';*/
  25. V_STR_RAW := UTL_I18N.STRING_TO_RAW(V_STR, 'AL32UTF8');
  26. /*注意:需保证当前登录用户有调用包DBMS_CRYPTO的权限*/
  27. -- 指定:密钥算法、工作模式、填充方式
  28. V_TYPE := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_ECB +
  29. DBMS_CRYPTO.PAD_PKCS5;
  30. V_STR_RAW := DBMS_CRYPTO.ENCRYPT(SRC => V_STR_RAW,
  31. TYP => V_TYPE,
  32. KEY => V_KEY_RAW);
  33. V_RETURN_STR := RAWTOHEX(V_STR_RAW);
  34. RETURN V_RETURN_STR;
  35.  
  36. END;

  注意:这里的Key也是需要Java提前转换成16进制的密钥。

2020/01/02

powerbuilder(PB)

  1. //AES/ECB/PKCS5Padding 加密 以 Hex 编码返回
  2. blob lbb_iv
  3. 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文件

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

aes加密、解密

  1. /**
  2. * aes加密
  3. * @param clearText 待加密字符串
  4. * @param hexKey 加密密钥,由java提供(16进制)
  5. * @explain AES/ECB/PKCS7Padding
  6. * 偏移量(填充方式):PKCS7Padding,对应java的PKCS5Padding
  7. * 加密模式:ECB
  8. * return 加密结果(16进制)
  9. */
  10. function encrypt(clearText, hexKey) {
  11. var key = CryptoJS.enc.Hex.parse(hexKey);
  12. var encrytedData = CryptoJS.AES.encrypt(clearText, key, {
  13. mode : CryptoJS.mode.ECB,
  14. padding : CryptoJS.pad.Pkcs7
  15. });
  16. return encrytedData.ciphertext.toString().toUpperCase();
  17. }
  18.  
  19. /**
  20. * aes解密
  21. * @param cipherText 待解密字符串(16进制)
  22. * @param hexKey 解密密钥,由java提供(16进制)
  23. * return 解密结果(以utf-8进行编码)
  24. */
  25. function decrypt(cipherText, hexKey) {
  26. // 1.将16进制转换成数组
  27. var hexArray = CryptoJS.enc.Hex.parse(cipherText);
  28. // 2.将数组转换成base64字符串
  29. var base64Str = CryptoJS.enc.Base64.stringify(hexArray);
  30. // 3.将密钥转换成数组
  31. var key = CryptoJS.enc.Hex.parse(hexKey);
  32. // 4.解密
  33. var decryptedData = CryptoJS.AES.decrypt(base64Str, key, {
  34. mode : CryptoJS.mode.ECB,
  35. padding : CryptoJS.pad.Pkcs7
  36. });
  37. // 5.以utf-8进行编码解密结果
  38. return decryptedData.toString(CryptoJS.enc.Utf8);
  39. }  

测试

  1. window.onload = function() {
  2. var clearText = "张三";
  3. var hexKey = "E341BACB74574E03051D2BB1FD48BD99";
  4. var cipherText = encrypt(clearText, hexKey);
  5. console.log(cipherText);//6E3AC3434E1F8C371EA81DDB124AA5D7
  6. clearText = decrypt(cipherText,hexKey);
  7. console.log(clearText);//张三
  8.  
  9. }

  

 

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

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

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

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

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

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

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

  4. AES加密解密通用版Object-C / C# / JAVA

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

  5. C#, Java, PHP, Python和Javascript几种语言的AES加密解密实现[转载]

    原文: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 关于密码处理的工具类[MD5编码][AES加密/解密]

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

  10. C# 实现 JAVA AES加密解密[原创]

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

随机推荐

  1. 根据ImageView的大小来压缩Bitmap,避免OOM

    Bitmap是引起OOM的罪魁祸首之一,当我们从网络上下载图片的时候无法知道网络图片的准确大小,所以为了节约内存,一般会在服务器上缓存一个缩略图,提升下载速度.除此之外,我们还可以在本地显示图片前将图 ...

  2. 用IO流向存储器或SD卡中存入/读取字符的工具类

    FileManager package com.kale.utils; import java.io.BufferedReader; import java.io.File; import java. ...

  3. jquery validate验证方法

    实例: equalTo方法 equalTo(其他)返回:布尔 说明:要求元素与另一个元素相同 等于(其他) 其他 类型:选择器 元素的选择器用于比较当前值 例子: 使“字段”必须与#other相同 1 ...

  4. Orchard模块开发全接触2:新建 ProductPart

    一:创建 Part 1:项目引用 Orchard.Framework: 2:创建 Models 文件夹: 3:在 Models 文件夹下创建类 ProductPartRecord,如下: public ...

  5. [转]MySQL主从复制(Master-Slave)与读写分离(MySQL-Proxy)实践

    转自:http://heylinux.com/archives/1004.html Mysql作为目前世界上使用最广泛的免费数据库,相信所有从事系统运维的工程师都一定接触过.但在实际的生产环境中,由单 ...

  6. Maven中的库(repository)详解

    Maven中的库(repository)是构件(artifact)的集合.构件以一定的布局存储在库中. 本地仓库 vs. 远程仓库 运行Maven的时候,Maven所需要的任何构件都是直接从本地仓库获 ...

  7. QT TCP网络编程

    首先介绍一下TCP:(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议.相比而言UDP,就是开放式.无连接.不可靠的传输层 ...

  8. Asp.net Page_ClientValidate 的应用和跳过

    其实网上说道的Page_ClientValidate的博客其实有很多.这里就不列举了,最近在开发遇到一个问题给大家分享一下, 整理后的代码 如下: HTML code, <%@ Page Lan ...

  9. Asp.net web Control Enable 属性设置

    最近手上有一个很简单的一个小项目,需要查看编辑的历史记录,先前设计的时候把数据都save 到DB了,现在时间紧迫 就不在画新的UI,而是采用以前的edit页面 来显示数据,这里就需要把页面上所有的co ...

  10. ul里不能直接嵌套div(在ie7以前版本)

    平时为了写某个js效果,从而忽略了标签的嵌套 从而导致了IE6-7混乱,在ul下,直接嵌套div,在ie7以前版本,会出现的状况是:div会被离它最近的li包裹住. 请看dome <ul cla ...