背景
对外服务的接口为了安全起见,往往需要进行相应的安全处理:数据加密传输和身份认证。数据加密传输有对称加密和非对称加密两种,为了更加安全起见采用非对称加密比较好些,身份认证则采用数字签名可以实现。
非对称加密缺点:加解密速度慢、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
来源:https://www.cnblogs.com/hdwang/p/16310314.html
本站部分图文来源于网络,如有侵权请联系删除。