一、Hutool-crypto概述
加密分為三種:
1、對稱加密(symmetric),例如:AES、EDS等
2、非對稱加密(asymmetric),例如:RSA、DSA等
3、摘要加密(digest),例如:MD5、SHA-1、SHA-256、HMAC等
二、對稱加密
對稱加密(也叫私鑰加密)指加密和解密使用相同密鑰的加密算法。有時又叫傳統密碼算法,就是加密密鑰能夠從解密密鑰中推算出來,同時解密密鑰時也可以從加密密鑰中推算出來。而在大多數的對稱算法中,加密密鑰和解密密鑰是相同的,是以也稱這種加密算法為秘密密鑰算法或單密鑰算法。它要求發送方和接收方在安全通信之前,商定一個密鑰。對稱算法的安全性依賴于密鑰,洩露密鑰就意味着任何人都可以對他們發送或接收的消息解密,是以密鑰的保密性對通信安全性至關重要。
代碼示例:
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
public class HutoolSecure {
public static void main(String[] args) {
//生成UUID
String s = SecureUtil.simpleUUID();
System.out.println(s); //5f210cbe09154af08564ffab22e66b6f
//第一種:以AES算法
String content = "test中文";
//随機生成密鑰
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
//建構
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES,key);
//加密
byte[] encrypt = aes.encrypt(content);
//解密
byte[] decrypt = aes.decrypt(encrypt);
//加密16進制表示
String encryptHex = aes.encryptHex(content);
System.out.println("AES加密16進制表示:" + encryptHex); //46953def8ec02e21f7c9bb4405243a70
//解密為字元串
String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);
System.out.println("AES解密為字元串:" + decryptStr); //test中文
//第二種 DESede加密
String content2 = "test中文";
byte[] key2 = SecureUtil.generateKey(SymmetricAlgorithm.DESede.getValue()).getEncoded();
SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DESede,key2);
//加密
byte[] encrypt2 = des.encrypt(content2);
//解密
byte[] decrypt2 = des.decrypt(encrypt2);
//加密為16進制字元串(Hex表示)
String encryptHex2 = des.encryptHex(content2);
System.out.println("DESede加密16進制表示:" + encryptHex2); //fcedfe2478d3e65b1a525d60676a6d88
String decryptStr2 = des.decryptStr(encryptHex2);
System.out.println("DESede解密為字元串:" + decryptStr2); //test中文
//第三種AES封裝
String content3 = "test中文";
//随機生成密鑰
byte[] key3 = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
//建構
AES aes3 = SecureUtil.aes(key3);
//加密
byte[] encrypt3 = aes3.encrypt(content3);
//解密
byte[] decrypt3 = aes3.decrypt(encrypt3);
//加密為16進制表示
String encryptHex3 = aes3.encryptHex(content3);
System.out.println("AES封裝加密16進制表示:" + encryptHex3); //7300c26ae4081f9d9bd2395b9390f9ec
//解密為字元串
String decryptStr3 = aes3.decryptStr(encryptHex3, CharsetUtil.CHARSET_UTF_8);
System.out.println("AES封裝解密為字元串:" + decryptStr3); //test中文
//由于IOS等移動端對AES加密有要求,必須為PKCS7Padding模式
AES aes4 = new AES("CBC","PKCS7Padding",
//密鑰,可以自定義
"0123456789ABHAEQ".getBytes(),
//iv加鹽,按照實際需求添加
"DYgjCEIMVrj2W9xN".getBytes());
//加密為16進制表示
String encryptHex4 = aes4.encryptHex(content3);
System.out.println("加密後:" + encryptHex4); //90beca190dbe25c29925a8493ef6a538
String decryptStr4 = aes4.decryptStr(encryptHex4);
System.out.println("解密後:" + decryptStr4); //test中文
//SM4國密算法
String content5 = "test國密算法";
SymmetricCrypto sm4 = new SymmetricCrypto("SM4");
String encryptHex5 = sm4.encryptHex(content5);
System.out.println("SM4加密後:" + encryptHex5); //7d00175c6f2570fc132603f3922df7ca38c35f1c2fff3782140db4b915bf486b
String decryptStr5 = sm4.decryptStr(encryptHex5, CharsetUtil.CHARSET_UTF_8);
System.out.println("SM4解密後:" + decryptStr5); //test國密算法
}
}
三、非對稱加密
非對稱加密最常用的就是RSA和DSA,在Hutool中使用AsymmetricCrypto對象來負責加密解密。
非對稱加密有公鑰和私鑰兩個概念,私鑰自己擁有,不能給别人,公鑰公開。根據應用的不同,我們可以選擇使用不同的密鑰加密;
1、簽名:使用私鑰加密,公鑰解密。用于讓所有公鑰所有者驗證私鑰所有者的身份并且用來防止私鑰所有者釋出的内容被篡改,但是不用來保證内容不被他人獲得。
2、加密:用公鑰加密,私鑰解密。用于向公鑰所有者釋出資訊,這個資訊可能被他人篡改,但是無法被他人獲得。
使用:在非對稱加密中,我們可以通過AsymmetricCrypto(AsymmetricAlgorithm algorithm)構造方法,通過傳入不同的算法枚舉,獲得其加密解密器。
當然,為了友善,我們針對最常用的RSA算法建構了單獨的對象:RSA。
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import sun.misc.BASE64Decoder;
import java.io.IOException;
public class HutoolAsymmetricCrypto {
public static void main(String[] args) throws IOException {
//非對稱加密
RSA rsa = new RSA();
//獲得私鑰
rsa.getPrivateKey();
rsa.getPrivateKeyBase64();
//獲得公鑰
rsa.getPublicKey();
rsa.getPublicKeyBase64();
//公鑰加密,私鑰加密
byte[] encrypt = rsa.encrypt(StrUtil.bytes("RAS測試", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
String encode = Base64.encode(decrypt);
System.out.println(encode); //UkFT5rWL6K+V
//單元測試
// Assert.assertEquals("我是一段測試aaaa", StrUtil.str(decrypt, CharsetUtil.UTF_8));
//私鑰加密,公鑰解密
byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("RAS測試", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
byte[] decrypt2 = rsa.decrypt(encrypt2,KeyType.PublicKey);
String encode2 = Base64.encode(decrypt2);
System.out.println(encode2); //UkFT5rWL6K+V
//Base64解碼
BASE64Decoder decoder = new BASE64Decoder();
String str = new String(decoder.decodeBuffer(encode2), "UTF-8");
System.out.println("解碼後:" + str); //RAS測試
//已知私鑰及密文如何加解密
String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIL7pbQ+5KKGYRhw7jE31hmA"
+ "f8Q60ybd+xZuRmuO5kOFBRqXGxKTQ9TfQI+aMW+0lw/kibKzaD/EKV91107xE384qOy6IcuBfaR5lv39OcoqNZ"
+ "5l+Dah5ABGnVkBP9fKOFhPgghBknTRo0/rZFGI6Q1UHXb+4atP++LNFlDymJcPAgMBAAECgYBammGb1alndta"
+ "xBmTtLLdveoBmp14p04D8mhkiC33iFKBcLUvvxGg2Vpuc+cbagyu/NZG+R/WDrlgEDUp6861M5BeFN0L9O4hz"
+ "GAEn8xyTE96f8sh4VlRmBOvVdwZqRO+ilkOM96+KL88A9RKdp8V2tna7TM6oI3LHDyf/JBoXaQJBAMcVN7fKlYP"
+ "Skzfh/yZzW2fmC0ZNg/qaW8Oa/wfDxlWjgnS0p/EKWZ8BxjR/d199L3i/KMaGdfpaWbYZLvYENqUCQQCobjsuCW"
+ "nlZhcWajjzpsSuy8/bICVEpUax1fUZ58Mq69CQXfaZemD9Ar4omzuEAAs2/uee3kt3AvCBaeq05NyjAkBme8SwB0iK"
+ "kLcaeGuJlq7CQIkjSrobIqUEf+CzVZPe+AorG+isS+Cw2w/2bHu+G0p5xSYvdH59P0+ZT0N+f9LFAkA6v3Ae56OrI"
+ "wfMhrJksfeKbIaMjNLS9b8JynIaXg9iCiyOHmgkMl5gAbPoH/ULXqSKwzBw5mJ2GW1gBlyaSfV3AkA/RJC+adIjsRGg"
+ "JOkiRjSmPpGv3FOhl9fsBPjupZBEIuoMWOC8GXK/73DHxwmfNmN7C9+sIi4RBcjEeQ5F5FHZ";
RSA rs2 = new RSA(PRIVATE_KEY, null);
String a = "2707F9FD4288CEF302C972058712F24A5F3EC62C5A14AD2FC59DAB93503AA0FA17113A020EE4EA35EB53F"+
"75F36564BA1DABAA20F3B90FD39315C30E68FE8A1803B36C29029B23EB612C06ACF3A34BE815074F5EB5AA3A"+
"C0C8832EC42DA725B4E1C38EF4EA1B85904F8B10B2D62EA782B813229F9090E6F7394E42E6F44494BB8";
byte[] aByte = HexUtil.decodeHex(a);
byte[] decrypt3 = rs2.decrypt(aByte,KeyType.PrivateKey);
String encode1 = Base64.encode(decrypt3);
String txt = new String(decoder.decodeBuffer(encode1));
System.out.println("解密後的文本資訊:" + txt); //虎頭闖杭州,多擡頭看天,切勿隻管種地
}
}
四、摘要加密算法
摘要算法是一種能産生特殊輸出格式的算法,這種算法的特點是:無論使用者輸入什麼長度的原始資料,經過計算後輸出的密文都是固定産的長度的。這種算法的原理是根據一定的運算規則對原資料進行某種形式的提取,這種提取就是摘要,被摘要的資料内容與原資料有密切的聯系,隻要原資料稍有改變,輸出的“摘要”便完全不同,是以,基于這種原理的算法便能對資料完整性提供較為健全的保障。
但是由于輸出的密文是提取資料經過處理的定長值,是以它已經不能還原資料,即消息摘要算法是不可逆的,理論上無法通過反向運算取得原資料内容,是以它通常隻能采用來做資料完整性驗證。
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.66</version>
</dependency>
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.Digester;
public class HutoolDegister {
public static void main(String[] args) {
//擷取摘要
//MD5
String testStr = "5393554e94bf0eb6436f240a4fd71282";
//方式一
Digester md5 = new Digester(DigestAlgorithm.MD5);
String str = md5.digestHex(testStr);
System.out.println(str); //c3b68dca7f530043db5590f8c5e53a1a
//方式二
String string = DigestUtil.md5Hex(testStr);
System.out.println(string); //c3b68dca7f530043db5590f8c5e53a1a
//SM3
Digester digester = DigestUtil.digester("sm3");
String digestHex = digester.digestHex(testStr);
System.out.println(digestHex); //73bea8ca42b88469ebd8221896636db83fb0297c5cdaf98787344c07abf480dd
}
}