切换主题
Java 签名 & 验签 示例
在 API 请求中,为了确保数据的完整性和安全性,我们使用 RSA 签名 机制来对请求进行加密签名,并在接收方进行验签。
本示例提供了一个 SignatureHandler 类,用于 生成签名 和 验证签名
java
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
/**
* RSA 签名 & 验签工具类
* @author 点点易付团队
* - 通过私钥对数据进行签名
* - 通过公钥验证签名的合法性
*/
public class SignatureHandler {
private final Key key; // 存储私钥或公钥对象
private final boolean isPrivate; // 是否为私钥
/**
* 构造方法,初始化私钥或公钥
*
* @param keyStr Base64 编码的私钥或公钥(去掉头尾)
* @param keyType "private" 表示私钥,"public" 表示公钥
* @throws Exception 如果密钥格式错误或解码失败
*/
public SignatureHandler(String keyStr, String keyType) throws Exception {
// 预处理:移除 PEM 头尾标记并去掉所有换行符
keyStr = keyStr.replaceAll("\\n", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
// Base64 解码密钥字符串
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
// 根据 keyType 解析私钥或公钥
if (keyType.equalsIgnoreCase("private")) {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.key = keyFactory.generatePrivate(keySpec);
this.isPrivate = true;
} else if (keyType.equalsIgnoreCase("public")) {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.key = keyFactory.generatePublic(keySpec);
this.isPrivate = false;
} else {
throw new IllegalArgumentException("Invalid keyType. Must be 'private' or 'public'.");
}
}
/**
* 生成 RSA 签名(使用 SHA256 摘要算法)
*
* @param merchantId 商户 ID
* @param timestamp 时间戳
* @param timezone 时区
* @param body JSON 格式的请求体字符串
* @return 生成的 Base64 编码签名字符串
* @throws Exception 如果签名失败
*/
public String generateSignature(String merchantId, String timestamp, String timezone, String body) throws Exception {
if (!isPrivate) {
throw new IllegalStateException("Key must be private to generate signature.");
}
// 组装签名内容(注意:必须保证拼接顺序一致)
String signContent = merchantId + "." + timestamp + "." + timezone + "." + body;
// 使用 RSA 私钥进行签名
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign((PrivateKey) this.key);
signature.update(signContent.getBytes(StandardCharsets.UTF_8));
// 返回 Base64 编码的签名
return Base64.getEncoder().encodeToString(signature.sign());
}
/**
* 验证 RSA 签名的合法性
*
* @param merchantId 商户 ID
* @param timestamp 时间戳
* @param timezone 时区
* @param body JSON 格式的请求体字符串
* @param receivedSignature 接收到的 Base64 编码签名字符串
* @return 验签结果,true 为成功,false 为失败
* @throws Exception 如果验签失败
*/
public boolean verifySignature(String merchantId, String timestamp, String timezone, String body, String receivedSignature) throws Exception {
if (isPrivate) {
throw new IllegalStateException("Key must be public to verify signature.");
}
// 组装签名内容(与 `generateSignature` 方法保持一致)
String signContent = merchantId + "." + timestamp + "." + timezone + "." + body;
// 使用 RSA 公钥进行验签
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify((PublicKey) this.key);
signature.update(signContent.getBytes(StandardCharsets.UTF_8));
// Base64 解码接收到的签名
byte[] decodedSignature = Base64.getDecoder().decode(receivedSignature);
// 验证签名
return signature.verify(decodedSignature);
}
}