使用RSA算法对接口参数签名及验签
在不同的服务器或系统之间通过API接口进行交互时,两个系统之间必须进行身份的验证,以满足安全上的防抵赖和防篡改。
通常情况下为了达到以上所描述的目的,我们首先会想到使用非对称加密算法对传输的数据进行签名以验证发送方的身份,而RSA加密算法是目前比较通用的非对称加密算法,经常被用于数字签名及数据加密,且很多编程语言的标准库中都自带有RSA算法的库,所以实现起来也是相对简单的。
本文将使用Java标准库来实现RSA密钥对的生成及数字签名和验签,密钥对中的私钥由请求方系统妥善保管,不能泄漏;而公钥则交由系统的响应方用于验证签名。
RSA使用私钥对数据签名,使用公钥进行验签,生成RSA密钥对的代码如下:
package com.springboot.RSAdemo; import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64; /**
* @description 生成RSA公/私钥对
*
* @author admin
*
*/
public class GeneratorRSAKey { public static void main(String[] args) {
jdkRSA();
} public static void jdkRSA() {
GeneratorRSAKey generatorRSAKey = new GeneratorRSAKey(); try {
// 初始化密钥,产生公钥私钥对
Object[] keyPairArr = generatorRSAKey.initSecretkey();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPairArr[0];
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPairArr[1]; System.out.println("------------------PublicKey------------------");
System.out.println(Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded())); System.out.println("\n------------------PrivateKey------------------");
System.out.println(Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} } /**
* 初始化密钥,生成公钥私钥对
*
* @return Object[]
* @throws NoSuchAlgorithmException
*/
private Object[] initSecretkey() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); Object[] keyPairArr = new Object[2];
keyPairArr[0] = rsaPublicKey;
keyPairArr[1] = rsaPrivateKey; return keyPairArr;
}
}
package com.springboot.RSAdemo; import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64; /**
* @description RSA签名工具类
*
* @author admin
*
*/
public class JdkSignatureUtil { private final static String RSA = "RSA"; private final static String MD5_WITH_RSA = "MD5withRSA"; /**
* 执行签名
*
* @param rsaPrivateKey 私钥
* @param toSignStr 参数内容
* @return 签名后的内容,base64后的字符串
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws InvalidKeyException
* @throws SignatureException
*/
public static String executeSignature(String rsaPrivateKey, String toSignStr) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
// base64解码私钥
byte[] decodePrivateKey = Base64.getDecoder().decode(rsaPrivateKey.replace("\r\n", "")); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodePrivateKey);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance(MD5_WITH_RSA);
signature.initSign(privateKey);
signature.update(toSignStr.getBytes()); // 生成签名
byte[] result = signature.sign(); // base64编码签名为字符串
return Base64.getEncoder().encodeToString(result);
} /**
* 验证签名
*
* @param rsaPublicKey 公钥
* @param sign 签名
* @param src 参数内容
* @return 验证结果
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws InvalidKeyException
* @throws SignatureException
*/
public static boolean verifySignature(String rsaPublicKey, String sign, String src) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
// base64解码公钥
byte[] decodePublicKey = Base64.getDecoder().decode(rsaPublicKey.replace("\r\n", "")); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodePublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance(MD5_WITH_RSA);
signature.initVerify(publicKey);
signature.update(src.getBytes());
// base64解码签名为字节数组
byte[] decodeSign = Base64.getDecoder().decode(sign); // 验证签名
return signature.verify(decodeSign);
}
}
package com.springboot.RSAdemo; import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; /**
* @description 发送端
*
* @author admin
*
*/
public class ClientController { //私钥
private final static String PRIVATE_KEY = "MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAnPWR8anPt0YVkJRj+Rdu9gFdkR3leD5LxJFEP6naIYJ5CUon4XJy5uh7uEivyOHJMQqlo4U3zBbn9A1d07MOcwIDAQABAkAZhys0ddztvv1U5X2ZDsGiSziPmKAwvVkPYF0MSbDLkC6ZF6SDosPnBOQGY1YXF88eknAxEmuhWHnRAb05QnUJAiEA03SgC954tZS4OLyRf/4ArNdOBYurKQPbCapDgwAesYUCIQC+BhF3FBGyIlnh7D1iN+uNm1atga5go3widi7dqRnFlwIgEuQldEoA4MAToUX/fb7Ukpx9pPMwbG6iv/9NHsQA+f0CIALRfDjT519I+yRKqK5oPeofv61bGwb75b9tGUzYUTWJAiBLKFNjABVQWu5zYD+IGx3ooJyAFn7rwFGL8Y3h1V1ITg=="; public static String sender() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, UnsupportedEncodingException {
// 请求所需的参数
Map<String, Object> requestParam = new HashMap<>(16);
requestParam.put("userName", "小明");
requestParam.put("phone", "15866552236");
requestParam.put("address", "北京");
requestParam.put("status", 1); // 将需要签名的参数内容按参数名的字典顺序进行排序,并拼接为字符串
StringBuilder sb = new StringBuilder();
requestParam.entrySet().stream().sorted(
Comparator.comparing(Map.Entry::getKey)).forEach(entry ->
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"));
String paramStr = sb.toString().substring(0, sb.length() - 1);
System.out.println("paramStr:" + paramStr); // 使用私钥生成签名字符串
String sign = JdkSignatureUtil.executeSignature(PRIVATE_KEY, paramStr); // 对签名字符串进行url编码
String urlEncodeSign = URLEncoder.encode(sign, "UTF-8");
// String urlEncodeSign = URLEncoder.encode(sign, StandardCharsets.UTF_8.toString()); // 请求参数中需带上签名字符串
requestParam.put("sign", urlEncodeSign); // 发送请求
return postJson("http://localhost:8080/test", requestParam);
} /**
* 发送数据类型为json的post请求
*
* @param <T>
* @param url
* @param param
* @return
*/
public static <T> String postJson(String url, T param) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<T> httpEntity = new HttpEntity<>(param, headers); RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class); return responseEntity.getBody();
} public static void main(String[] args) {
try {
System.out.println(sender());
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.springboot.RSAdemo; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Comparator;
import java.util.Map; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; @RestController
public class ServerController { // 公钥
private final static String PUBLIC_KEY = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJz1kfGpz7dGFZCUY/kXbvYBXZEd5Xg+S8SRRD+p2iGCeQlKJ+Fycuboe7hIr8jhyTEKpaOFN8wW5/QNXdOzDnMCAwEAAQ=="; @PostMapping(value = "/test")
public String server(@RequestBody Map<String, Object> param) throws InvalidKeySpecException,
NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
// 从参数中取出签名字符串并删除,因为sign不参与字符串拼接
String sign = (String) param.remove("sign");
// 对签名字符串进行url解码
String decodeSign = URLDecoder.decode(sign, "UTF-8");
// String decodeSign = URLDecoder.decode(sign, StandardCharsets.UTF_8.toString()); // 将签名的参数内容按参数名的字典顺序进行排序,并拼接为字符串
StringBuilder sb = new StringBuilder();
param.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(entry ->
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&")
);
String paramStr = sb.toString().substring(0, sb.length() - 1); // 使用公钥进行验签
boolean result = JdkSignatureUtil.verifySignature(PUBLIC_KEY, decodeSign, paramStr);
if (result) {
return "签名验证成功";
} return "签名验证失败,非法请求";
}
}
出处:https://blog.51cto.com/zero01/2331063
使用RSA算法对接口参数签名及验签的更多相关文章
- Delphi支付宝支付【支持SHA1WithRSA(RSA)和SHA256WithRSA(RSA2)签名与验签】
作者QQ:(648437169) 点击下载➨Delphi支付宝支付 支付宝支付api文档 [Delphi支付宝支付]支持条码支付.扫码支付.交易查询.交易退款.退款查询.交易撤 ...
- Java & PHP RSA 互通密钥、签名、验签、加密、解密
RSA加密算法是一种非对称加密算法.在公开密钥加密和电子商业中RSA被广泛使用.RSA是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Le ...
- Java RSA (SHA1withRSA)签名和验签
static { try { SIGNATURE = Signature.getInstance("SHA1withRSA", "BC"); } catch ( ...
- erlang的RSA签名与验签
1.RSA介绍 RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对 其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而 ...
- RSA后台签名前台验签的应用(前台采用jsrsasign库)
写在前面 安全测试需要, 为防止后台响应数据返给前台过程中被篡改前台再拿被篡改后的数据进行接下来的操作影响正常业务, 决定采用RSA对响应数据进行签名和验签, 于是有了这篇<RSA后台签名前台验 ...
- java/php/c#版rsa签名以及验签实现
本文为转载,请转载请注明地址: 原文地址为 http://xw-z1985.iteye.com/blog/1837376 在开放平台领域,需要给isv提供sdk,签名是Sdk中需要提供的 ...
- 密码基础知识(2)以RSA为例说明加密、解密、签名、验签
密码基础知识(1)https://www.cnblogs.com/xdyixia/p/11528572.html 一.RSA加密简介 RSA加密是一种非对称加密.是由一对密钥来进行加解密的过程,分别称 ...
- RSA/RSA2 进行签名和验签
package com.byttersoft.hibernate.erp.szmy.util; import java.io.ByteArrayInputStream; import java.io. ...
- .NET RSA解密、签名、验签
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Sec ...
随机推荐
- luoguP1034 矩形覆盖 x
P1034 矩形覆盖 题目描述 在平面上有 n 个点(n <= 50),每个点用一对整数坐标表示.例如:当 n=4 时,4个点的坐标分另为:p1(1,1),p2(2,2),p3(3,6),P4( ...
- vue路由 routers的写法:require用与不用
vue路由的写法有很多种,这里我只说routers的写法,一种是compcomponent后面直接写路径,另一种是用require的方式,来看代码 import Vue from 'vue' impo ...
- python 中字符串转 二进制 /ASCII码
- 我不熟悉的set
同样的我着重介绍那些我不怎么用到的系列,同时,常用的我就点一下. 我们都知道set底层是用红黑树实现的,红黑树是一种已排序的树,所以我们通过迭代器来访问节点元素的时候,并不可以改变它,如果随意改变,那 ...
- swift模式和模式匹配
模式和模式匹配 模式: 代表单个或者复合值得结构,也就是说模式不是一个特定的值,它是一种抽象的结构,[一句话,不是特指,是泛指].这样就可以用模式来匹配各种各样的值. 例如:(x,y)可以匹配元祖(1 ...
- DP练习题——洛谷P1970花匠
目录 题目描述: 输入输出格式: 输入格式: 输出格式: 输入输出样例: 输入样例: 输出样例: 题目分析: 解法一: 解法二: 结语: 题目描述: 洛谷\(P1970\) 花匠栋栋种了一排花,每株花 ...
- OpenCV学习笔记(9)——形态学转换
不同的形态学操作,例如腐蚀,膨胀,开运算,闭运算等 形态学操作是根据图像形状进行的简单操作.一般情况下对二值化图像进行操作.需要的参数有两个,一个是原始图像,第二个被称为结构化元素或核,它是用来决定操 ...
- AOP 代理类的创建
AOP 代理类的创建 入口:AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 和 AnnotationAwar ...
- docker(常见调试技巧):docker打包镜像调试技巧
写Dockerfile可以先不指定CMD.ENTRYPOINT等启动命令,只要拷贝就好了 如下: # Dockerfile for basic-app-client # Build with: # d ...
- linux配置多个ip
linux配置多个ip /sbin/ifconfig eth0:1 172.19.121.180 broadcast 172.19.121.255 netmask 255.255.255.0 up ...