一、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 } }
五、消息認證算法-HMac
HMAC,全稱為"Hash Message Authentication Code", 中文名“散列消息鑒別碼”,主要是利用哈希算法,以一個密鑰和一個消息為輸入,生成一個消息摘要作為輸出。一般的,消息鑒別碼用於驗證傳輸於兩個共同享有一個密鑰的單位之間的消息。HMAC可以與任何迭代散列函數捆綁使用。MD5和SHA-1就是這種散列函數。HMAC還可以使用一個用於計算和確認消息鑒別值得密鑰。