背景 對外服務的介面為了安全起見,往往需要進行相應的安全處理:數據加密傳輸和身份認證。數據加密傳輸有對稱加密和非對稱加密兩種,為了更加安全起見採用非對稱加密比較好些,身份認證則採用數字簽名可以實現。 非對稱加密缺點:加解密速度慢、RSA有最大長度要求。 方案一 僅採用非對稱加密 RSA對內容長度的要 ...
背景
對外服務的介面為了安全起見,往往需要進行相應的安全處理:數據加密傳輸和身份認證。數據加密傳輸有對稱加密和非對稱加密兩種,為了更加安全起見採用非對稱加密比較好些,身份認證則採用數字簽名可以實現。
非對稱加密缺點:加解密速度慢、RSA有最大長度要求。
方案一
僅採用非對稱加密
RSA對內容長度的要求可以通過分組加解密解決
參考:https://blog.csdn.net/draven1122/article/details/55212252
方案二
非對稱加密+對稱加密
內容採用對稱加密(AES)加密,非對稱加密僅加密AES密鑰
調用方
1.簽名生成:介面參數->摘要演算法(SHA)->參數摘要->簽名(自己RSA私鑰)->簽名
2.內容(介面參數 + 簽名 ) -->對稱加密(AES)--> 內容密文
3.AES密鑰-->非對稱加密(對方RSA公鑰)--> AES密鑰密文
4. 內容密文(請求體)+AES密鑰密文(請求頭)-> 傳輸給接收方
接收方
1. 獲取AES密鑰密文-> 非對稱解密(自己RSA私鑰)->AES密鑰
2. 內容密文-> AES密鑰-> 內容明文
3. 驗證簽名:內容里的參數->摘要演算法(SHA)->參數摘要->驗簽(對方RSA公鑰)->簽名
4. 驗簽通過,繼續執行介面
5. 返回值(加解密可選)
加密工具類可以使用:https://hutool.cn/docs/#/crypto/%E6%A6%82%E8%BF%B0
基於hutool中密碼工具類實現的rsa和國密的非對稱加解密演算法和加簽驗簽演算法代碼如下
maven依賴
<!-- huTool工具箱 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.22</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15to18</artifactId> <version>1.69</version> </dependency>
rsa非對稱加密
package com.hdwang.test.hutool; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import cn.hutool.crypto.asymmetric.Sign; import cn.hutool.crypto.asymmetric.SignAlgorithm; import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Base64; /** * @author wanghuidong * @date 2022/5/25 21:00 */ public class RsaTest { public static void main(String[] args) { String text = "人最寶貴的是生命.生命對每個人只有一次.人的一生應當這樣度過:當他迴首往事的時候,不會因為虛度年華而悔恨,也不會因為碌碌無為而羞恥.這樣,在臨死的時候,他能夠說:“我已把自己的整個的生命和全部的精力獻給了世界上最壯麗的事業---------為人類的解放而鬥爭.”"; System.out.println("原文:" + text); //生成公私鑰對 KeyPair pair = SecureUtil.generateKeyPair("RSA"); PrivateKey privateKey = pair.getPrivate(); PublicKey publicKey = pair.getPublic(); //獲得私鑰 String privateKeyStr = bytesToBase64(privateKey.getEncoded()); System.out.println("私鑰:" + privateKeyStr); //獲得公鑰 String publicKeyStr = bytesToBase64(publicKey.getEncoded()); System.out.println("公鑰:" + publicKeyStr); RSA rsa = new RSA(privateKeyStr, publicKeyStr); System.out.println(rsa); //公鑰加密,私鑰解密 byte[] encrypt = rsa.encrypt(StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey); System.out.println("公鑰加密:" + bytesToBase64(encrypt)); byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey); System.out.println("私鑰解密:" + new String(decrypt,StandardCharsets.UTF_8)); // //私鑰加密,公鑰解密 // byte[] encrypt2 = rsa.encrypt(StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey); // System.out.println("私鑰加密:" + bytesToBase64(encrypt2)); // byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey); // System.out.println("公鑰解密:" + bytesToBase64(decrypt2)); Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKeyStr, publicKeyStr); //簽名 byte[] data = text.getBytes(StandardCharsets.UTF_8); byte[] signed = sign.sign(data); String signedStr = bytesToBase64(signed); System.out.println("簽名:" + signedStr); //驗證簽名 boolean verify = sign.verify(data, base64ToBytes(signedStr)); System.out.println("驗簽:" + verify); } /** * 位元組數組轉Base64編碼 * * @param bytes 位元組數組 * @return Base64編碼 */ private static String bytesToBase64(byte[] bytes) { byte[] encodedBytes = Base64.getEncoder().encode(bytes); return new String(encodedBytes, StandardCharsets.UTF_8); } /** * Base64編碼轉位元組數組 * * @param base64Str Base64編碼 * @return 位元組數組 */ private static byte[] base64ToBytes(String base64Str) { byte[] bytes = base64Str.getBytes(StandardCharsets.UTF_8); return Base64.getDecoder().decode(bytes); } }
國密非對稱加密(SM2)
package com.hdwang.test.hutool; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.SM2; import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.util.Base64; /** * 國密非對稱加解密和加簽驗簽演算法 * * @author wanghuidong * @date 2022/5/25 20:50 */ public class SmTest { public static void main(String[] args) { String text = "人最寶貴的是生命.生命對每個人只有一次.人的一生應當這樣度過:當他迴首往事的時候,不會因為虛度年華而悔恨,也不會因為碌碌無為而羞恥.這樣,在臨死的時候,他能夠說:“我已把自己的整個的生命和全部的精力獻給了世界上最壯麗的事業---------為人類的解放而鬥爭.”"; System.out.println("原文:" + text); KeyPair pair = SecureUtil.generateKeyPair("SM2"); byte[] privateKey = pair.getPrivate().getEncoded(); byte[] publicKey = pair.getPublic().getEncoded(); System.out.println("公鑰:\n" + bytesToBase64(publicKey)); System.out.println("私鑰:\n" + bytesToBase64(privateKey)); SM2 sm2 = SmUtil.sm2(privateKey, publicKey); // 公鑰加密,私鑰解密 String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey); System.out.println("加密後:" + encryptStr); String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey)); System.out.println("解密後:" + decryptStr); //加簽 String sign = sm2.signHex(HexUtil.encodeHexStr(text)); System.out.println("簽名:" + sign); //驗簽 boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(text), sign); System.out.println("驗簽:" + verify); } /** * 位元組數組轉Base64編碼 * * @param bytes 位元組數組 * @return Base64編碼 */ private static String bytesToBase64(byte[] bytes) { byte[] encodedBytes = Base64.getEncoder().encode(bytes); return new String(encodedBytes, StandardCharsets.UTF_8); } /** * Base64編碼轉位元組數組 * * @param base64Str Base64編碼 * @return 位元組數組 */ private static byte[] base64ToBytes(String base64Str) { byte[] bytes = base64Str.getBytes(StandardCharsets.UTF_8); return Base64.getDecoder().decode(bytes); } }
參考文章:
https://www.cnblogs.com/pcheng/p/9629621.html
https://blog.csdn.net/woniu211111/article/details/108114402
https://blog.csdn.net/draven1122/article/details/55212252