skip to content
Wentao Zhang

RSA 接口签名认证实现

/ 4 min read

前因

在业务上需要暴露一些接口提供给第三方,经过讨论决定使用RSA1签名来做接口认证。

RSA 算法简介

RSA(Rivest-Shamir-Adleman)是一种非对称加密算法,广泛用于数据加密和数字签名。它的安全性基于大整数因子分解的困难性。

RSA 的基本原理可以用以下数学公式表示:

加密: C=Memod  nC = M^e \mod n

解密: M=Cdmod  nM = C^d \mod n

其中:

  • MM 是明文消息
  • CC 是密文
  • ee 和 nn 组成公钥
  • dd 和 nn 组成私钥

性能比较

下面是一个简单的RSA签名性能分析图表,展示了不同密钥长度对签名和验证时间的影响:

image

签名认证流程

是
否
1. 开始
2. 生成RSA密钥对
3. 分发公钥给客户端
4. 客户端准备请求数据
5. 客户端使用私钥对数据签名
6. 客户端发送请求数据和签名
7. 服务端接收请求
8. 服务端使用公钥验证签名
9. 验证是否通过
10. 接受请求并处理
11. 拒绝请求
12. 返回处理结果
13. 返回错误信息
14. 结束

Java实现

生成签名

String appSecret = "...private_key...";
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(Base64.decode(appSecret));
PrivateKey privateKey = keyFactory.generatePrivate(privateSpec);
// 创建签名对象
Signature sign = Signature.getInstance("SHA256withRSA");
// 签名
sign.initSign(privateKey);
sign.update("Hello, World!".getBytes("UTF-8"));
byte[] signature = sign.sign();
System.out.println("签名: " + Base64.encode(signature));

验证签名

String appKey = "...public_key..."
// 解码密钥
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(Base64.decode(appKey));
PublicKey publicKey = keyFactory.generatePublic(publicSpec);
// 验证
Signature verifySig = Signature.getInstance("SHA256withRSA");
verifySig.initVerify(publicKey);
verifySig.update("Hello, World!".getBytes("UTF-8"));
boolean isCorrect = verifySig.verify(signature);
System.out.println("签名正确: " + isCorrect);

完整的工具方法

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* 描述: RSA 工具
*/
public class RSAUtil {
/**
* 验证签名
* @param publicKey 公钥
* @param sign 签名
* @param data 数据
* @return 是否验证通过
* @throws Exception 异常
*/
public static boolean verify(String publicKey, String sign, String data) {
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(Base64.decode(publicKey));
PublicKey key = SecureUtil.generatePublicKey("RSA", publicSpec);
// 验证
try {
Signature verifySig = Signature.getInstance("SHA256withRSA");
verifySig.initVerify(key);
verifySig.update(data.getBytes(StandardCharsets.UTF_8));
return verifySig.verify(Base64.decode(sign));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 生成签名
* @param privateKey 私钥
* @param data 数据
* @return 签名
*/
public static String sign(String privateKey, String data) throws Exception {
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
PrivateKey key = SecureUtil.generatePrivateKey("RSA", privateSpec);
// 创建签名对象
Signature sign = Signature.getInstance("SHA256withRSA");
// 签名
sign.initSign(key);
sign.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signature = sign.sign();
return Base64.encode(signature);
}
}

Python生成签名、验证签名 (使用pycryptodome)

安装 pycryptodome

Terminal window
pip install pycryptodome

实现代码

import base64
import http.client
import json
import time
import Crypto
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
appKey = "...public_key..."
app_secret = """-----BEGIN RSA PRIVATE KEY-----
...private_key...
-----END RSA PRIVATE KEY-----"""
def sign(data, private_key):
key = RSA.import_key(private_key)
h = SHA256.new(data.encode('utf-8'))
signature = pkcs1_15.new(key).sign(h)
return base64.b64encode(signature).decode('utf-8')
def verify(public_key, sign, data):
public_key = RSA.import_key(public_key)
sign_bytes = base64.b64decode(sign)
hash = SHA256.new(data.encode('utf-8'))
try:
pkcs1_15.new(public_key).verify(hash, sign_bytes)
return True
except (ValueError, TypeError):
return False

注意事项

注意: 最初使用 hutool 工具实现签名认证时,发现 Python 中生成的签名无法在 Java 端验证通过。因此改用了 Java 基础库来实现,以确保跨语言兼容性。

Footnotes

  1. RSA维基百科 ↩