目录
■前言
■代码
■运行效果
■其它
・Access restriction. (访问限制)
・MD5、SHA-256 等 MessageDigest 算法 ,生成 Hash序列
■AES 坑 :【InvalidKeyException】
===
■前言
WebAPI直接,HTTP传送数据,数据加密
■代码
注意,加密之后,使用Base64转换位字符串,方便传输。
package com.sxz.study.aes;import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;public class TestAES {public static void main(String[] args) {String encodeStr;String decodeStr;try {encodeStr = AESEncode("123encodeKey", "中国大连-2023年3月14日");System.out.println(encodeStr); decodeStr = AESDecode("123encodeKey", encodeStr);System.out.println(decodeStr); } catch (Exception e) {e.printStackTrace();}System.out.println("大連".getBytes(StandardCharsets.UTF_8).length); System.out.println("大連".getBytes(Charset.forName("MS932")).length); System.out.println("大連".getBytes(Charset.forName("GBK")).length);System.out.println("大連".getBytes(Charset.forName("UTF8")).length); System.out.println("大連".getBytes(Charset.forName("UTF-8")).length);}/*** 加密* 1.构造密钥生成器* 2.根据ecnodeRules规则初始化密钥生成器* 3.产生密钥* 4.创建和初始化密码器* 5.内容加密* 6.返回字符串*/public static String AESEncode(String encodeRules, String content) throws Exception {// 1.构造密钥生成器,指定为AES算法,不区分大小写 KeyGenerator keygen = KeyGenerator.getInstance("AES");// 2.根据ecnodeRules规则初始化密钥生成器// 生成一个128位的随机源,根据传入的字节数组keygen.init(128, new SecureRandom(encodeRules.getBytes()));// 3.产生原始对称密钥SecretKey originalKey = keygen.generateKey();// 3'.另外一种生成Key的方法
// byte[] bytes32 = "123456789012345678901234567890AB".getBytes();
// SecretKey originalKey = new SecretKeySpec(bytes32,"AES");// --------- 加密 KEY 確認 用代碼 ------- 【START】BigInteger bi = null;bi = new BigInteger(1, originalKey.getEncoded());String keyHexStr = bi.toString(16);String md5StrFormat = String.format("%32s", keyHexStr); // 不足32位,前面补空格keyHexStr = md5StrFormat.replace(" ", "0"); // 把空格替换成0System.out.println(bytesToBin(originalKey.getEncoded()));System.out.println("---AES_KEY--- bigInt :" + bi);System.out.println("---AES_KEY--- 16进制 :" + keyHexStr);System.out.println("---AES_KEY--- 16进制 长度:" + keyHexStr.length());System.out.println("---AES_KEY--- 2进制 长度:" + keyHexStr.length()*4);// --------- 加密 KEY 確認 用代碼 ------- 【 END 】// // 4.获得原始对称密钥的字节数组
// byte[] raw = originalKey.getEncoded();
// // 5.根据字节数组生成AES密钥
// SecretKey key = new SecretKeySpec(raw, "AES");// 6.根据指定算法AES自成密码器 【★★★ 这里才是真正的ASE加密相关的代码!】
// Cipher cipher = Cipher.getInstance("AES");// 算法:AES// 加密模式:ECB (默认)// 如果需要使用CBC模式,则需要加入额外的IV参数。// 填充模式:pkcs5padding(默认)Cipher cipher = Cipher.getInstance("AES/ECB/pkcs5padding"); // 算法/模式/补码方式
// =============================================================================================
// 【加密模式】
// AES五种加密模式(ECB、CBC、CTR、OCF、CFB)
// 块加密,常用的加密模式有ECB、CBC。
// ECB,即electronic code book,
// 将整个明文分成若干段相同小段,然后每小段进行加密,
// 每段互不依赖,可以并行处理,同样的明文就会生成同样的密文;
// CBC,即cipher block chaining,
// 密文分组链模式,密文分组间如同链条相互连接,先将明文切割为若干段,
// 每一小段与上一段的密文段运算后(第一个块没有上个密文段,故而使用IV进行运算),
// 再同秘钥进行加密,因为是串行处理,所以同样明文每次生成的密文不一样。 // 【填充模式】 (pkcs5padding)
// 块加密中,常用还有填充模式,对于固定加密算法,每个块有固定大小,
// 如AES的块大小为16个字节的整数倍,
// 明文分块时,如果块大小不够,则需要使用固定数据进行填充。
//
// 【IV】(初始化向量)
// AES的Cipher.getInstance调用时,使用AES即可,
// 默认使用的分组模式就是ECB,填充模式为PKCS5Padding。
// 如果需要使用CBC模式,则需要加入额外的Iv参数。
// (ECB模式不需要 IV,强制使用会出错)
// (java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV)
// =======================
// String sIv = "1234567890123ABC";//16位自定义向量
// byte[] bytes = sIv.getBytes();
// IvParameterSpec iv = new IvParameterSpec(bytes);//使用CBC模式,需要一个向量iv,可增加加密算法的强度
//
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"
// cipher.init(Cipher.ENCRYPT_MODE, originalKey, iv);
// =======================
// =============================================================================================// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEYcipher.init(Cipher.ENCRYPT_MODE, originalKey);// Cipher 英 [ˈsaɪfə] n. 密码 v. 使用密码, // cipher,通常指的是使用一种特殊的算法进行加密// 8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8);// 9.根据密码器的初始化方式--加密:将数据加密byte[] aes = cipher.doFinal(byteEncode);// return Base64.encodeBase64String(aes);String AES_encode = Base64.getEncoder().encodeToString(aes);return AES_encode;}/*** 解密* 解密过程:* 1.同加密1-4步* 2.将加密后的字符串反纺成byte[]数组* 3.将加密内容解密*/public static String AESDecode(String encodeRules, String content) throws Exception {// 1.构造密钥生成器,指定为AES算法,不区分大小写KeyGenerator keygen = KeyGenerator.getInstance("AES");// 2.根据ecnodeRules规则初始化密钥生成器// 生成一个128位的随机源,根据传入的字节数组keygen.init(128, new SecureRandom(encodeRules.getBytes()));// 3.产生原始对称密钥SecretKey originalKey = keygen.generateKey();// 3'.另外一种生成Key的方法
// byte[] bytes32 = "123456789012345678901234567890AB".getBytes();
// SecretKey originalKey = new SecretKeySpec(bytes32,"AES");// // 4.获得原始对称密钥的字节数组
// byte[] raw = originalKey.getEncoded();
// // 5.根据字节数组生成AES密钥
// SecretKey key = new SecretKeySpec(raw, "AES");// 6.根据指定算法AES自成密码器Cipher cipher = Cipher.getInstance("AES");// 【IV】
// =====================
// String sIv = "1234567890123ABC";//16位自定义向量
// byte[] bytes = sIv.getBytes();
// IvParameterSpec iv = new IvParameterSpec(bytes);//使用CBC模式,需要一个向量iv,可增加加密算法的强度
//
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"
// cipher.init(Cipher.DECRYPT_MODE, originalKey, iv);
// =====================// 7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEYcipher.init(Cipher.DECRYPT_MODE, originalKey);// 7.5.获取加密字符串(Base64)的 字节数组// byte[] byteContent = Base64.decodeBase64(content); // 不再推荐使用的方法byte[] byteContent = Base64.getDecoder().decode(content);// 8.解码byte[] byteDecode = cipher.doFinal(byteContent);return new String(byteDecode, StandardCharsets.UTF_8);}/*** 把多个字节转换成二进制字符串*/public static String bytesToBin(byte[] bytes) {StringBuffer sb = new StringBuffer();for (byte b : bytes) {String zero = "00000000";String binStr = Integer.toBinaryString(b & 0xFF);if(binStr.length() < 8) {binStr = zero.substring(0, 8 -binStr.length()) + binStr;}sb.append(binStr);}return sb.toString();}/*** 把单个字节转换成二进制字符串*/public static String byteToBin(byte b) {String zero = "00000000";String binStr = Integer.toBinaryString(b & 0xFF);if(binStr.length() < 8) {binStr = zero.substring(0, 8 -binStr.length()) + binStr;}return binStr;}}
■运行效果
00100111011011011111110101111011100000101001010000101100000101001010001110101010110010010110011000001001101011000001100001001100
---AES_KEY--- bigInt :52410993428297908844820493916267354188
---AES_KEY--- 16进制 :276dfd7b82942c14a3aac96609ac184c
---AES_KEY--- 16进制 长度:32
---AES_KEY--- 2进制 长度:128
Ux38V/CaQjMWSfyFP1Qmqnkk8oqwigUZuLR5Op2FBAI=
中国大连-2023年3月14日
6
4
4
6
6
===
■其它
・Access restriction. (访问限制)
从JDK1.8开始,SUN公司就已经建议不再使用 sun.misc.BASE64Encoder与sun.misc.BASE64Decoder了,推荐使用 java.util.Base64 工具类来将其替换.
使用sun.misc.BASE64时,会出现【Access restriction】 的警告。
(restriction 英 [rɪˈstrɪkʃən] n. 限制;约束 )
Multiple markers at this line- Access restriction: The constructor 'BASE64Encoder()' is not API (restriction on required library 'C:\java\8\8\jre\lib\rt.jar')- Access restriction: The method 'CharacterEncoder.encode(byte[])' is not API (restriction on required library 'C:\java\8\8\jre\lib\rt.jar')- Access restriction: The type 'BASE64Encoder' is not API (restriction on required library 'C:\java\8\8\jre\lib\rt.jar')
・MD5、SHA-256 等 MessageDigest 算法 ,生成 Hash序列
MD5 与 Base64一起使用 加密,计算原理_md5 base64_sun0322的博客-CSDN博客
---
package com.sxz.study.messageDigest;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class MessageDigestTest {public static void main(String[] args) {String md5str1 = getMessageDigestByStr("5123", "MD5");System.out.println(md5str1);String md5str2 = getMessageDigestByStr("5123", "SHA-256");System.out.println(md5str2);String md5str3 = getMessageDigestByFile("C:\\test\\to\\sss.txt");System.out.println(md5str3);String md5str1_method2 = getMessageDigestByStr_method2("5123", "MD5");System.out.println(md5str1_method2);System.out.println(String.format("%32s", "5123")); // 不足32位,前面补空格String md5str1_bug_method = getMessageDigestByStrBugMethod("5123", "MD5");System.out.println(md5str1_bug_method);}public static String getMessageDigestByStr(String msgStr, String algorithmStr) {BigInteger bi = null;try {MessageDigest md = MessageDigest.getInstance(algorithmStr);byte[] buffer = md.digest(msgStr.getBytes("utf-8"));bi = new BigInteger(1, buffer);} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}// 使用下面代码解决bug (PowerShell的值,有时会不相同) (MD5的值,是一个32位长度的字符串)String md5Str = bi.toString(16);// MD5の場合、 不足32位,前面补空格// SHA-256の場合、 不足64位,前面补空格String md5StrFormat = String.format("%32s", md5Str); String result = md5StrFormat.replace(" ", "0"); // 把空格替换成0return result.toUpperCase();}public static String getMessageDigestByFile(String filePath) {BigInteger bi = null;try {byte[] buffer = new byte[8192];int len = 0;MessageDigest md = MessageDigest.getInstance("MD5");File f = new File(filePath);FileInputStream fis = new FileInputStream(f);while ((len = fis.read(buffer)) != -1) {md.update(buffer, 0, len);}fis.close();byte[] b = md.digest();bi = new BigInteger(1, b);} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}// 下面代码有BUG,当MD5的Hash值,第一位是0是,第一位会被省略。// return bi.toString(16);// 使用下面代码解决bug (MD5的值,是一个32位长度的字符串)String md5Str = bi.toString(16);String md5StrFormat = String.format("%32s", md5Str); // 不足32位,前面补空格String result = md5StrFormat.replace(" ", "0"); // 把空格替换成0return result.toUpperCase();}public static String getMessageDigestByStr_method2(String msgStr, String algorithmStr) {BigInteger bi = null;// 方法2byte[] buffer = null;try {MessageDigest md = MessageDigest.getInstance(algorithmStr);// 方法2buffer = md.digest(msgStr.getBytes("utf-8"));// 方法1
// byte[] buffer = md.digest(msgStr.getBytes("utf-8"));
// bi = new BigInteger(1, buffer);} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}// 方法1
// // 使用下面代码解决bug (PowerShell的值,有时会不相同) (MD5的值,是一个32位长度的字符串)
// String md5Str = bi.toString(16);
//
// // MD5の場合、 不足32位,前面补空格
// // SHA-256の場合、 不足64位,前面补空格
// String md5StrFormat = String.format("%32s", md5Str);
// String result = md5StrFormat.replace(" ", "0"); // 把空格替换成0
//
// return result.toUpperCase();// 方法2return hex(buffer);}public static String hex(byte[] bytes) {StringBuilder result = new StringBuilder();for (byte aByte : bytes) {result.append(String.format("%02x", aByte));// upper case// result.append(String.format("%02X", aByte));}return result.toString();}public static String getMessageDigestByStrBugMethod(String msgStr, String algorithmStr) {BigInteger bi = null;try {MessageDigest md = MessageDigest.getInstance(algorithmStr);byte[] buffer = md.digest(msgStr.getBytes("utf-8"));bi = new BigInteger(1, buffer);} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}// 使用下面代码解决bug (PowerShell的值,有时会不相同) (MD5的值,是一个32位长度的字符串)String md5Str = bi.toString(16);// // MD5の場合、 不足32位,前面补空格
// // SHA-256の場合、 不足64位,前面补空格
// String md5StrFormat = String.format("%32s", md5Str);
// String result = md5StrFormat.replace(" ", "0"); // 把空格替换成0
//
// return result.toUpperCase();
// return md5Str;}}
===
結果
037A595E6F4F0576A9EFE43154D71C18
4F9EB48D371E25B05D5DF80EEBB343C6BFB067D274301DB24DD26D26E8AEB6AB
037A595E6F4F0576A9EFE43154D71C18
037a595e6f4f0576a9efe43154d71c185123
37a595e6f4f0576a9efe43154d71c18
===
■AES 坑 :【InvalidKeyException】
========================
1. 项目使用AES加密,出现异常如下:
java.security.InvalidKeyException: Illegal key size
2. 为解决“AES的256位密钥加解密报 java.security.InvalidKeyException: Illegal key size or default parameters 异常”问题:
需要使用oracle提供的无政策限制权限文件,在oracle官网上下载JDK对应版本的JCE文件,替换jre1.x\lib\security下面的local_policy.jar和
US_export_policy.jar两个文件。
异常原因:如果密钥大于128, 会抛出java.security.InvalidKeyException: Illegal key size 异常. 因为密钥长度是受限制的, java运行时环境读到的是受限的policy文件. 文件位于${java_home}/jre/lib/security, 这种限制是因为美国对软件出口的控制.
不过,一般的JDK中的JRE中的Jar,不存在以上这个问题
====
====
========================