fix: 修复支付宝 RSA2 签名问题

- 支持 base64 编码的 PEM 格式私钥(systemd 环境变量)
- 使用 Digest + Pkcs1v15Sign::new() 进行签名(自动哈希)
- 修复 rsa2_sign 和 rsa2_verify 函数实现
This commit is contained in:
2026-04-27 14:27:14 +08:00
parent 00fea53a4c
commit ef246db31c
2 changed files with 29 additions and 31 deletions

View File

@@ -1,8 +1,9 @@
// src/alipay.rs — 支付宝 RSA2 签名与请求封装 // src/alipay.rs — 支付宝 RSA2 签名与请求封装
use rsa::pkcs1v15::{Pkcs1v15Sign, SigningKey, VerifyingKey};
use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, LineEnding}; use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, LineEnding};
use rsa::signature::Signer; use rsa::signature::{Signer, Verifier};
use rsa::RsaPrivateKey;
use rsa::RsaPublicKey; use rsa::RsaPublicKey;
use rsa::{Pkcs1v15Sign, RsaPrivateKey};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::env; use std::env;
@@ -85,33 +86,28 @@ pub fn rsa2_sign(content: &str, private_key_pem: &str) -> Result<String, String>
} }
}; };
let signing_input = Sha256::digest(content.as_bytes()); let signing_key = SigningKey::<Sha256>::new(private_key);
let signature = private_key let signature = signing_key.sign(content.as_bytes());
.sign(Pkcs1v15Sign::new::<Sha256>(), &signing_input)
.map_err(|e| format!("签名失败: {}", e))?;
Ok(base64::Engine::encode( Ok(base64::Engine::encode(
&base64::engine::general_purpose::STANDARD, &base64::engine::general_purpose::STANDARD,
&signature, signature.to_bytes().as_ref(),
)) ))
} }
/// 验证 RSA2 签名 /// 验证 RSA2 签名
pub fn rsa2_verify(content: &str, sign: &str, public_key_pem: &str) -> Result<bool, String> { pub fn rsa2_verify(content: &str, sign: &str, public_key_pem: &str) -> Result<bool, String> {
use sha2::Digest;
let public_key = RsaPublicKey::from_public_key_pem(public_key_pem) let public_key = RsaPublicKey::from_public_key_pem(public_key_pem)
.map_err(|e| format!("支付宝公钥解析失败: {}", e))?; .map_err(|e| format!("支付宝公钥解析失败: {}", e))?;
let sig_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, sign) let sig_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, sign)
.map_err(|e| format!("签名 Base64 解码失败: {}", e))?; .map_err(|e| format!("签名 Base64 解码失败: {}", e))?;
let hashed_content = Sha256::digest(content.as_bytes()); let hashed = Sha256::digest(content.as_bytes());
Ok(public_key public_key
.verify( .verify(rsa::Pkcs1v15Sign::new::<Sha256>(), &hashed, &sig_bytes)
Pkcs1v15Sign::new::<Sha256>(), .map_err(|e| format!("签名验证失败: {}", e))?;
&hashed_content, Ok(true)
&sig_bytes,
)
.is_ok())
} }
/// 构建支付宝请求 URL含签名 /// 构建支付宝请求 URL含签名

View File

@@ -1,9 +1,10 @@
// handlers/payment.rs — 支付相关处理器(接入支付宝) // handlers/payment.rs — 支付相关处理器(接入支付宝)
use actix_web::{get, post, web, HttpRequest, HttpResponse}; use actix_web::{get, post, web, HttpRequest, HttpResponse};
use chrono::Utc; use chrono::Utc;
use rsa::pkcs1v15::{Pkcs1v15Sign, SigningKey, VerifyingKey};
use rsa::pkcs8::DecodePrivateKey; use rsa::pkcs8::DecodePrivateKey;
use rsa::signature::{Signer, Verifier}; use rsa::signature::{SignatureEncoding, Signer, Verifier};
use rsa::{Pkcs1v15Sign, RsaPrivateKey}; use rsa::RsaPrivateKey;
use serde::Deserialize; use serde::Deserialize;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use sqlx::postgres::PgPool; use sqlx::postgres::PgPool;
@@ -101,29 +102,30 @@ fn rsa2_sign(content: &str, private_key_pem: &str) -> Result<String, String> {
.map_err(|e| format!("私钥解析失败: {}", e))? .map_err(|e| format!("私钥解析失败: {}", e))?
} }
}; };
let hashed_content = Sha256::digest(content.as_bytes()); let signing_key = SigningKey::<Sha256>::new(private_key);
let signature = private_key let signature = signing_key.sign(content.as_bytes());
.sign(Pkcs1v15Sign::new::<Sha256>(), &hashed_content)
.map_err(|e| format!("签名失败: {}", e))?;
Ok(base64::Engine::encode( Ok(base64::Engine::encode(
&base64::engine::general_purpose::STANDARD, &base64::engine::general_purpose::STANDARD,
&signature, signature.to_bytes().as_ref(),
)) ))
} }
/// 验证 RSA2 签名 /// 验证 RSA2 签名
#[allow(dead_code)]
fn rsa2_verify(content: &str, sign: &str, public_key_pem: &str) -> Result<bool, String> { fn rsa2_verify(content: &str, sign: &str, public_key_pem: &str) -> Result<bool, String> {
use rsa::pkcs8::DecodePublicKey; use rsa::pkcs8::DecodePublicKey;
use rsa::signature::Verifier; use rsa::RsaPublicKey;
use sha2::Digest;
let public_key = let public_key =
rsa::RsaPublicKey::from_public_key_pem(public_key_pem) RsaPublicKey::from_public_key_pem(public_key_pem)
.map_err(|e| format!("支付宝公钥解析失败: {}", e))?; .map_err(|e| format!("支付宝公钥解析失败: {}", e))?;
let sig_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, sign) let sig_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, sign)
.map_err(|e| format!("签名 Base64 解码失败: {}", e))?; .map_err(|e| format!("签名 Base64 解码失败: {}", e))?;
let hashed_content = Sha256::digest(content.as_bytes()); let hashed = Sha256::digest(content.as_bytes());
Ok(public_key public_key
.verify(Pkcs1v15Sign::new::<Sha256>(), &hashed_content, &sig_bytes) .verify(rsa::Pkcs1v15Sign::new::<Sha256>(), &hashed, &sig_bytes)
.is_ok()) .map_err(|e| format!("签名验证失败: {}", e))?;
Ok(true)
} }
// ===== Helper: 调用 alipay.trade.page.pay ===== // ===== Helper: 调用 alipay.trade.page.pay =====