目录
■前言
■代码
■运行效果
■其它
・Access restriction. (访问限制)
・MD5、SHA-256 等 MessageDigest 算法 ,生成 Hash序列
■DES介绍
■DES的Java代码
■DES 和 AES 的区别
■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"); // 把空格替换成0
System.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)操作,第二个参数为使用的KEY
cipher.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)操作,第二个参数为使用的KEY
cipher.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
===
从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 与 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"); // 把空格替换成0
return 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"); // 把空格替换成0
return result.toUpperCase();
}
public static String getMessageDigestByStr_method2(String msgStr, String algorithmStr) {
BigInteger bi = null;
// 方法2
byte[] buffer = null;
try {
MessageDigest md = MessageDigest.getInstance(algorithmStr);
// 方法2
buffer = 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();
// 方法2
return 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
037a595e6f4f0576a9efe43154d71c18
5123
37a595e6f4f0576a9efe43154d71c18
===
"DES"是Data Encryption Standard(数据加密标准)的缩写。DES是一种对称加密算法,它使用相同的密钥来加密和解密数据。DES算法最早在1977年被美国国家标准技术研究所(NIST)采纳为联邦标准,但现在已经被更安全的算法替代。
DES使用56位密钥,将64位的明文输入划分成16个56位的子块,然后对每个子块执行一系列的复杂混合和替换操作。它的目的是将明文转换为随机的密文,以保护数据的机密性。
尽管DES在过去几十年间被广泛使用,但由于56位密钥空间较小,容易受到攻击,因此现在已经被更安全的加密算法(如AES)所取代。
xxx
==
该示例使用了Java加密库中的
Cipher
类和SecretKeyFactory
类来实现DES加密。在encrypt
方法中,首先使用密钥生成DESKeySpec对象,然后使用SecretKeyFactory生成SecretKey对象。接下来,通过Cipher类的init
方法指定加密模式和密钥,然后通过调用doFinal
方法对明文进行加密。在decrypt
方法中,步骤与加密相似,只是将加密模式设置为解密模式,并对密文进行解密操作。请注意,由于DES算法已经很旧且不够安全,不建议在实际应用中使用。
==
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class DESExample {
public static void main(String[] args) throws Exception {
String plainText = "This is the message to be encrypted";
String key = "ThisIsKey";
byte[] encryptedText = encrypt(plainText, key);
System.out.println("Encrypted Text: " + Base64.getEncoder().encodeToString(encryptedText));
String decryptedText = decrypt(encryptedText, key);
System.out.println("Decrypted Text: " + decryptedText);
}
public static byte[] encrypt(String plainText, String key) throws Exception {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
}
public static String decrypt(byte[] encryptedText, String key) throws Exception {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(encryptedText);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}
===
DES(Data Encryption Standard)和AES(Advanced Encryption Standard)是两种对称加密算法,它们在加密和解密过程中使用相同的密钥。
主要区别如下:
密钥长度:DES使用56位密钥,而AES可以使用128位、192位或256位密钥。AES密钥长度更长,提供更高的安全性。
安全性:由于DES密钥空间较小,容易受到穷举攻击(遍历所有可能的密钥值)和差分分析等攻击。相比之下,AES具有更大且更复杂的密钥空间,更难以被破解。
密码分组长度:DES将64位明文分为16个56位子块,每个子块进行加密操作。而AES将明文分为128位块,并在加密过程中对整个块进行操作。
应用领域:DES较为适用于早期的应用,如金融交易和电子邮件加密。而AES被广泛认可为目前最安全和最常用的加密算法,用于保护敏感信息,如数据库、云存储和网络通信等领域。
总结来说,AES比DES更安全和高效,因此在现代加密应用中,AES是首选的对称加密算法。
xxx
===
========================
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,不存在以上这个问题
====
====
===
package com.sxz.study.aes;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESUTil {
public static void main(String[] args) {
String content = "大连";
String seretKeyStr = "aabbCCdrewrwyda1235rwetrwadfafdsfABCewrwerO="; // 44
String ivStr = "abcdefgh123456AB"; // 16
String encodeStr = encodeByAES(content,seretKeyStr,ivStr);
System.out.println(encodeStr);
String decodeStr = decodeByAES(encodeStr,seretKeyStr,ivStr);
System.out.println(decodeStr);
}
public static String encodeByAES(String content, String seretKeyStr, String ivStr) {
String contentEncode = "";
byte by[] = Base64.getDecoder().decode(seretKeyStr); // length:44*(6/8)= 33; 32 *8=256bit
SecretKey secretKey = new SecretKeySpec(by, "AES");
try {
Cipher cipher = Cipher.getInstance("AES/CBC/pkcs5padding");
IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] encryptData = cipher.doFinal(content.getBytes("UTF-8"));
contentEncode = Base64.getEncoder().encodeToString(encryptData);
} catch (Exception e) {
e.printStackTrace();
}
return contentEncode;
}
public static String decodeByAES(String content, String seretKeyStr, String ivStr) {
String contentDecode = "";
byte by[] = Base64.getDecoder().decode(seretKeyStr);
SecretKey secretKey = new SecretKeySpec(by, "AES");
try {
Cipher cipher = Cipher.getInstance("AES/CBC/pkcs5padding");
IvParameterSpec iv = new IvParameterSpec(ivStr.getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decodeBase64 = Base64.getDecoder().decode(content);
byte[] decode = cipher.doFinal(decodeBase64);
contentDecode = new String (decode, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return contentDecode;
}
}
========================
irGJLXmvOa6NJWTlMBWxzQ==
大连
・base64编码
Base64编码_base64区分大小写吗-CSDN博客
・base64编码后面的=
(为什么 // length:44*(6/8)= 33; 32 *8=256bit)
base64编码后面数据存在“=”或“==”是因为编码数据二进制转化后,按照Base64规则进行编码不够而进行补得位,缺一位就补一个“=”,缺两位就补两个“=”;
如果需要表示上面的64个字符,那么需要6bit,也就是2^6=64,base64的核心思想就是,
将3个字节拆分成4个6bit,然后对每个6bit的高位补2个0,构成1个字节。
也即是每3个字节最终结果将变成4个字节。
如果原始字符串的字节数不是3的整数倍,那么就用0来填充,用来填充的0就被编码成了'=',这就是出现=的原因,并且只会出现在结果出,如果原始字符刚好是3字节的整数倍,那么就没有等号了。
======