前因
在业务上需要暴露一些接口提供给第三方,经过讨论决定使用RSA
1签名来做接口认证。
RSA 算法简介
RSA(Rivest-Shamir-Adleman)是一种非对称加密算法,广泛用于数据加密和数字签名。它的安全性基于大整数因子分解的困难性。
RSA 的基本原理可以用以下数学公式表示:
加密:
解密:
其中:
- 是明文消息
- 是密文
- 和 组成公钥
- 和 组成私钥
性能比较
下面是一个简单的RSA签名性能分析图表,展示了不同密钥长度对签名和验证时间的影响:
签名认证流程
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
pip install pycryptodome
实现代码
import base64import http.clientimport jsonimport timeimport Cryptofrom Crypto.PublicKey import RSAfrom Crypto.Signature import pkcs1_15from 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 基础库来实现,以确保跨语言兼容性。