import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import sun.misc.BASE64Decoder;
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.TreeMap;
@Slf4j
public class Demo {
private static String publicKey = "xxx";
public static void main(String[] args) {
TreeMap<String, Object> params = new TreeMap<>();
// 放入参数
params.put("agetId", "xx");
params.put("custId", "xx");
params.put("orderNo", "xx");
// ...
params.put("version", "1.0.0");
// 拼接参数,生成sign
StringBuilder sb = new StringBuilder();
for (String key : params.keySet()) {
if (params.get(key) == null) {
continue;
}
String valueStr;
Object value = params.get(key);
// 判断是否为可直接 toString() 的简单类型
if (value instanceof CharSequence // 包含 String、StringBuilder 等
|| value instanceof Number // 所有数值类型包装类
|| value instanceof Boolean
|| value instanceof Character
|| value instanceof Enum) { // 枚举类型,通常使用枚举名即可
valueStr = value.toString();
} else {
// 复杂对象(如 Map、List、数组、自定义 POJO 等)转为 JSON 字符串
valueStr = JSON.toJSONString(value);
}
sb.append(key).append("=").append(valueStr).append("&");
}
String res = sb.substring(0, sb.lastIndexOf("&"));
log.info("拼接的待签名串:{}", res);
String sha256 = SHA256.getSHA256(res);
String sign = RSAUtil.encrypt(publicKey, sha256);
params.put("sign", sign);
String paramJson = JSONObject.toJSONString(params);
String url = "xxx";
// 发送请求
String response = HttpUtil.postData(url, paramJson);
}
public static class SHA256 {
/**
* 利用java原生的类实现SHA256加密
*
* @param str 加密后的报文
* @return
*/
public static String getSHA256(String str) {
MessageDigest messageDigest;
String encodestr = "";
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
encodestr = byte2Hex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return encodestr;
}
/**
* 将byte转为16进制
*
* @param bytes
* @return
*/
private static String byte2Hex(byte[] bytes) {
StringBuilder stringBuffer = new StringBuilder();
String temp;
for (byte aByte : bytes) {
temp = Integer.toHexString(aByte & 0xFF);
if (temp.length() == 1) {
stringBuffer.append("0");
}
stringBuffer.append(temp);
}
return stringBuffer.toString();
}
}
public static class RSAUtil {
/**
* 使用公钥对明文进行签名
*
* @param publicKey 公钥
* @param plainText 明文
* @return
*/
public static String encrypt(String publicKey, String plainText) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
byte[] bytes = plainText.getBytes();
ByteArrayInputStream read = new ByteArrayInputStream(bytes);
ByteArrayOutputStream write = new ByteArrayOutputStream();
byte[] buf = new byte[117];
int len = 0;
while ((len = read.read(buf)) != -1) {
byte[] buf1 = null;
if (buf.length == len) {
buf1 = buf;
} else {
buf1 = new byte[len];
for (int i = 0; i < len; i++) {
buf1[i] = buf[i];
}
}
byte[] bytes1 = cipher.doFinal(buf1);
write.write(bytes1);
}
return Base64Util.encode(write.toByteArray());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 得到公钥
*
* @param key 密钥字符串(经过base64编码)
* @throws Exception
*/
public static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
}
public static class Base64Util {
private static final char[] base64EncodeChars = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
private static byte[] base64DecodeChars = new byte[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 };
private Base64Util() {}
/**
* 将字节数组编码为字符串
*
* @param data
*/
public static String encode(byte[] data) {
StringBuilder sb = new StringBuilder();
int len = data.length;
int i = 0;
int b1, b2, b3;
while (i < len) {
b1 = data[i++] & 0xff;
if (i == len) {
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
sb.append("==");
break;
}
b2 = data[i++] & 0xff;
if (i == len) {
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
sb.append("=");
break;
}
b3 = data[i++] & 0xff;
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
sb.append(base64EncodeChars[b3 & 0x3f]);
}
return sb.toString();
}
/**
* 将base64字符串解码为字节数组
*
* @param str
*/
public static byte[] decode(String str) throws Exception{
byte[] data = str.getBytes("GBK");
int len = data.length;
ByteArrayOutputStream buf = new ByteArrayOutputStream(len);
int i = 0;
int b1, b2, b3, b4;
while (i < len) {
/* b1 */
do {
b1 = base64DecodeChars[data[i++]];
} while (i < len && b1 == -1);
if (b1 == -1) {
break;
}
/* b2 */
do {
b2 = base64DecodeChars[data[i++]];
} while (i < len && b2 == -1);
if (b2 == -1) {
break;
}
buf.write((b1 << 2) | ((b2 & 0x30) >>> 4));
/* b3 */
do {
b3 = data[i++];
if (b3 == 61) {
return buf.toByteArray();
}
b3 = base64DecodeChars[b3];
} while (i < len && b3 == -1);
if (b3 == -1) {
break;
}
buf.write((int) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));
/* b4 */
do {
b4 = data[i++];
if (b4 == 61) {
return buf.toByteArray();
}
b4 = base64DecodeChars[b4];
} while (i < len && b4 == -1);
if (b4 == -1) {
break;
}
buf.write(((b3 & 0x03) << 6) | b4);
}
return buf.toByteArray();
}
}
public static class HttpUtil {
public static String postData(String url, String json) {
return postData(url, json, StandardCharsets.UTF_8);
}
public static String postData(String url, String json, Charset responseCharset) {
log.info("请求地址:" + url);
HttpPost httpPost = new HttpPost(url);
setTimeOut(httpPost);
log.info("连接成功");
StringEntity requestEntity = new StringEntity(json, "UTF-8");
requestEntity.setContentEncoding("UTF-8");
httpPost.setHeader("Content-Type", "application/json");
httpPost.setEntity(requestEntity);
log.info("请求报文:" + json);
return execute(httpPost, responseCharset);
}
private static void setTimeOut(HttpRequestBase httpRequest) {
//设置超时时间 请求超时时间 12s
RequestConfig config = RequestConfig.custom()
// 设置连接超时时间(单位毫秒)
.setConnectTimeout(12000)
// 设置请求超时时间(单位毫秒)
.setConnectionRequestTimeout(2000)
// socket读写超时时间(单位毫秒)
.setSocketTimeout(2000).build();
httpRequest.setConfig(config);
}
private static String execute(HttpRequestBase httpRequest, Charset responseCharset) {
log.info("开始请求————————");
log.info("请求中------------------");
long start = System.currentTimeMillis();
String result = "";
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
CloseableHttpResponse response = httpClient.execute(httpRequest)) {
long end = System.currentTimeMillis();
log.info("请求结束————————");
log.info("响应时间:" + (end - start));
if (response != null) {
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
log.info("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
log.info("响应内容长度为:" + responseEntity.getContentLength());
result = EntityUtils.toString(responseEntity, responseCharset);
log.info("响应内容为:" + result);
}
}
} catch (SocketTimeoutException s) {
log.error("请求超时", s);
} catch (Exception e) {
log.error("请求处理异常", e);
}
return result;
}
}
}using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Newtonsoft.Json.Linq;
namespace Api.XYFPay.Helper.Test
{
public class RSA2Helper
{
//公钥,对接时候使用提供的公钥
private static string pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg+xmmMsM04/q7yP+ykr8cagYhMls1612W5Mpq5WlofErS1ksfAH5GvW3qTwgwmgACOL8EcQE6xgmwCrHLe0jt6PfihZkTuNcBA88DEcSyBJeQhPwi1WeUcZF14AjUdLiLmdDGb7eQmiN/w8lMac8IwbjSiXfw4kju1DuISSIbcYFw+BpFIirTzPiyL1hNludAm15ecnLY79ONiZQP1BrkmV/lvwfkXCQkrrATtiCzCZdiZ2XLBhZm8R7GcVtPk5IB5bYKCg7HH6pdRPdul0futAow2mht4kBVeaZeQdA1xkumDPYaoLez9wQBOT5nGubc6NmgAA1AY70I9csXASnBwIDAQAB";
/// <summary>
/// 公钥加密demo(提交参数sign)
/// </summary>
/// <returns></returns>
public static object PaTest()
{
try
{
var parameters = new Dictionary<string, object>();
var jsonArray = JsonConvert.DeserializeObject("[{\"id\":\"123\" },{\"name\":\"123\"},{\"name12\":\"12333\"},{\"aa\":\"12333\"}]");
parameters.Add("agetId", "FWH000018501");
parameters.Add("orderNo", "test112121121212121");
parameters.Add("orderno2", "方法");
parameters.Add("orderno3", null);
parameters.Add("orderNo4", jsonArray);
// 按键排序字典,包括下划线
var sortedParameters = parameters.OrderBy(p => p.Key, StringComparer.Ordinal)
.Where(x=>x.Value!=null)
.Select(p => p.Value is string ? $"{p.Key}={p.Value}" : $"{p.Key}={JsonConvert.SerializeObject(p.Value)}");
// 拼接排序后的参数
var parameterString = string.Join("&", sortedParameters);
// 计算 SHA256 哈希
string sha256Str = SHA256Encrypt(parameterString);
string sign = EncryptByPublicKey(pubKey, sha256Str);
parameters.Add("sign", sign);
return parameters;
}
catch (Exception ex)
{
return "fail";
}
}
/// <summary>
/// 公钥解密demo,(异步通知 参数需要动态的获取,后面可能会有新增字段情况)
/// </summary>
/// <returns></returns>
public static object ReceiveMessageTest()
{
try
{
//异步通知内容
string content = "{\"REPAYMENT_TRANSACTION_ID\":\"\",\"ORDER_STATUS\":\"1\",\"THREE_ORDER_NO\":\"1718613332163578\",\"T_PAY_NO\":\"4200002164202406174443831052\",\"ORDER_TIME\":\"20240617163542\",\"BUSI_DATA\":\"\",\"MEDIATYPE\":\"\",\"REMARK\":\"\",\"CODE_AGE_AMT\":\"0\",\"POS_PAY_WAY\":\"\",\"USTLDAT\":\"20240617\",\"NETR_AMT\":\"1\",\"DISCOUNT_FLAG\":\"0\",\"DISCOUNT_FEE\":\"0\",\"TXAMT\":\"1\",\"CUST_ID\":\"60000007011491\",\"DEVICE_NO\":\"\",\"SREF_NO\":\"\",\"ORD_TYPE\":\"\",\"INVESTMENT_TYPE\":\"\",\"ORDER_CREATE_TIME\":\"\",\"MERCID\":\"\",\"LIMIT_FEE\":\"0\",\"CARD_TYPE\":\"03\",\"MERCH_CONTRIBUTE\":\"\",\"ORDER_NO\":\"20240617TTTardpq\",\"BAT_NO\":\"\",\"PAY_CHANNEL\":\"2\",\"T_ORDER_NO\":\"A0240617163533442648\",\"TXN_RSV1\":\"\",\"TXN_RSV2\":\"\",\"BANK_NAME\":\"零钱通或零钱\",\"PROMOTION_DETAIL\":\"\",\"OLD_ORDER_NO\":\"\",\"sign\":\"DQGWWVppvT0tn8YZP+N63hA1BFsig3d03Ghab6PUcATd71tLWqzPnQM4KtePeZUWLq7RxgwHmkwYi6EUdFSZbM2+9Y9GlcWGbJsC1oyZtWkIaIoyyO9IqoePutr8/Xk6asidkfFu4DcJQf9LsnFx6sj2wWOCAOMJHRPXMIdlqq+Tqasn8QUHotgtFbFEiHHaq4WmLVKrwpXrXb76hD4fUTpFrHTafD8DXZeOV/uodckxGX7jCyrmVBOi4xwlLsOGMPwqI3vyq01R9Af4ZzW8TgMZ+poWlXwGQFBO4WYaBqXbG2XsCggMlc1JDysUkFqnahYgR2gWO1RfBUnQQS+WBw==\",\"DEBT_STATE\":\"\",\"CUST_FEE\":\"0\",\"TRAN_TYPE_SER\":\"01\",\"PAY_TYPE\":\"\",\"AGET_ID\":\"FWH000018501\",\"BANK_CODE\":\"OTHERS\",\"OPEN_ID\":\"oBY7i5bBtwn9oyFyOjnTxhUcKOzk\",\"PAY_WAY\":\"8\",\"TRADING_IP\":\"120.33.197.108\",\"CUST_AMT\":\"0\"}";
object notify = JsonConvert.DeserializeObject(content);
//var json = JsonConvert.SerializeObject(notify, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var parameters = JsonConvert.DeserializeObject<Dictionary<string, object>>(content);
string sign = parameters.FirstOrDefault(x => x.Key == "sign").Value.ToString();
parameters.Remove("sign");
// 按字典排序,包括下划线
var sortedParameters = parameters.OrderBy(p => p.Key, StringComparer.Ordinal)
.Select(p => p.Value is string ? $"{p.Key}={p.Value}" : $"{p.Key}={JsonConvert.SerializeObject(p.Value)}");
// 拼接排序后的参数
var parameterString = string.Join("&", sortedParameters);
// 计算 SHA256 哈希
string sha256Str = SHA256Encrypt(parameterString);
string strRst = Encoding.UTF8.GetString(DecryptByPublicKey(sign, pubKey));
if (strRst == sha256Str)
{
return JsonConvert.DeserializeObject<JObject>("{\"rspCod\":\"\",\"rspMsg\":\"success\"}");
}
return JsonConvert.DeserializeObject<JObject>("{\"rspCod\":\"\",\"rspMsg\":\"fail\"}");
}
catch (Exception ex)
{
return JsonConvert.DeserializeObject<JObject>("{\"rspCod\":\"\",\"rspMsg\":\"fail\"}");
}
}
/// <summary>
/// RSA公钥加密
/// </summary>
/// <param name="xmlPublicKey">加密公钥;为空则默认系统公钥</param>
/// <param name="enptStr">需要加密的明文字符串</param>
/// <param name="encoding">编码格式;默认:UTF-8</param>
/// <returns>RSA公钥加密的密文</returns>
public static string EncryptByPublicKey(string publicKey, string enptStr, string encoding = "UTF-8")
{
String pubKey = publicKey.Trim();
String xmlPublicKey = RSAPublicKeyJava2DotNet(pubKey);
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
byte[] cipherbytes;
rsa.FromXmlString(xmlPublicKey);
cipherbytes = rsa.Encrypt(Encoding.GetEncoding(encoding).GetBytes(enptStr), false);
return Convert.ToBase64String(cipherbytes);
}
}
/// <summary>
/// 用公钥解密
/// </summary>
/// <param name="data"></param>
/// <param name="publicKey">公钥</param>
/// <returns></returns>
public static byte[] DecryptByPublicKey(String data, String publicKey)
{
String pubKey = publicKey.Trim();
String xmlPublicKey = RSAPublicKeyJava2DotNet(pubKey);
RSACryptoServiceProvider publicRsa = new RSACryptoServiceProvider();
publicRsa.FromXmlString(xmlPublicKey);
AsymmetricKeyParameter keyPair = DotNetUtilities.GetRsaPublicKey(publicRsa);
//转换密钥
// AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(publicRsa);
IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");// 参数与Java中加密解密的参数一致
c.Init(false, keyPair); //第一个参数为true表示加密,为false表示解密;第二个参数表示密钥
byte[] DataToEncrypt = Convert.FromBase64String(data);
byte[] outBytes = c.DoFinal(DataToEncrypt);//解密
return outBytes;
}
/// <summary>
/// RSA公钥格式转换,java->.net
/// </summary>
/// <param name="publicKey"></param>
/// <returns></returns>
private static string RSAPublicKeyJava2DotNet(string publicKey)
{
RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
}
public static string SHA256Encrypt(string data)
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
byte[] hash = SHA256Managed.Create().ComputeHash(bytes);
string ret = "";
for (int i = 0; i < hash.Length; i++)
{
ret += Convert.ToString(hash[i], 16).PadLeft(2, '0');
}
return ret.PadLeft(32, '0').ToLower();
}
}
}
<?php
namespace pay;
class Demo
{
private $agetId = "61xxxxx"; //机构号
private $custId = "600xxxx"; //商户号
private $APIURL = "https://xyf-server-test.postar.cn"; //测试环境
/**
* 生成签名的通用方法
* @param array $params 需要签名的参数数组
* @return string 签名结果
*/
private function generateSign($params)
{
// 根据key进行字母大小排序
ksort($params);
// 拼接排序后的参数字符串
$str = '';
foreach ($params as $key => $value) {
if ($value !== null) {
$str .= "&$key=$value";
}
}
$str = substr($str, 1);
$sha256 = hash("sha256", $str);
$key_pem =
"-----BEGIN PUBLIC KEY-----
" .
chunk_split(
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxZ933I5F2uwSq3nLlhT7O8wNg8lSpAP4ATP/CHIcvt6QWWk6WLMoJVenJnKaF/nBnnXvb2Bp1GnlxW86a55uQLtbYgbuVZFm8qE3WhXVtEx9dqe3DOS/CqZXbrDEygclBaATZutKYvH3Z7Gl6uRfz63GKVhEJ0BbmrEP7ybQ3wTyXAD37WeiKgkYewKGuYEqnA6uV1/3H9dpiXauj5AcfVJ7ZV1QQHYm4fA4YJTiNB5oKGXiOvMO7lyfisqMrgjsQOwiTUHxxEmMk9HR+vCbVkA4GfynNBTjo7xG93lS0SBNIkf0R8D4PG9NsBVKwaEHyebja1Ak6q8kMBmc/L0azwIDAQAB",
64,
"
"
) .
"-----END PUBLIC KEY-----";
$privateKey = openssl_pkey_get_public($key_pem); //检测是否公钥
openssl_public_encrypt($sha256, $sign, $privateKey); //公钥加密
$sign = base64_encode($sign);
return $sign;
}
/**
* 获取格式化的公钥
* @return string 格式化的公钥
*/
private function getPublicKey()
{
return "-----BEGIN PUBLIC KEY-----
" .
chunk_split(
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxZ933I5F2uwSq3nLlhT7O8wNg8lSpAP4ATP/CHIcvt6QWWk6WLMoJVenJnKaF/nBnnXvb2Bp1GnlxW86a55uQLtbYgbuVZFm8qE3WhXVtEx9dqe3DOS/CqZXbrDEygclBaATZutKYvH3Z7Gl6uRfz63GKVhEJ0BbmrEP7ybQ3wTyXAD37WeiKgkYewKGuYEqnA6uV1/3H9dpiXauj5AcfVJ7ZV1QQHYm4fA4YJTiNB5oKGXiOvMO7lyfisqMrgjsQOwiTUHxxEmMk9HR+vCbVkA4GfynNBTjo7xG93lS0SBNIkf0R8D4PG9NsBVKwaEHyebja1Ak6q8kMBmc/L0azwIDAQAB",
64,
"
"
) .
"-----END PUBLIC KEY-----";
}
//签名demo - 使用通用方法生成签名
public function sign()
{
// 创建参数字典
$params = array(
'agetId' => $this->agetId, //机构号
'custId' => $this->custId, //商户号
'timeStamp' => time(), //时间戳
'version' => '1.0.0' //版本号
);
// 调用通用签名方法
return $this->generateSign($params);
}
//公钥解密参考签名demo
public function decode()
{
$RSA_DECRYPT_BLOCK_SIZE = 256;
$sign =
"k3qi6zUkK2vgj56p2Rvin801AK3JJIgowOVfp2BWyf+XSCAGFImG4X5DiyeFlQ1u02CTdEXdxV2OuaIumJdE7MtkkqYasqir8vFGLVVP1hxNhV0rSuNuhYxjFpjKUAIsGoOc9BgA/EYE3LjWXyt7ubFdkDc9yBJf45TndaylbsdcpPd2Blaq/hm10LAFGqOJhEPPhSOx+XD2R2ZWt6Lh2zNeoSB8Scg5zNaqNeX1xzMFIGhBktfkaehWvZaPGXGKa4Dluw95UJ73ZDib7Pq0c0yZ0PORc1QogZylMkgNFIpqH8W/HVU0ocfm8+UJGDSuEldNlYaY/na1nGRWcdhxuQ=="; // 签名
$sign = base64_decode($sign);
$data = str_split($sign, $RSA_DECRYPT_BLOCK_SIZE);
// 获取公钥
$key_pem = $this->getPublicKey();
$pubKey = openssl_pkey_get_public($key_pem);
$result = "";
foreach ($data as $block) {
openssl_public_decrypt($block, $dataDecrypt, $pubKey);
$result .= $dataDecrypt;
}
return $result;
}
}
// 主函数,模拟main方法执行
function main() {
echo "===== Pay类演示程序开始 =====
";
// 创建Pay类实例
$pay = new payPay();
try {
// 签名生成
echo "1. 执行sign方法(签名生成):
";
$signResult = $pay->sign();
echo "签名结果: " . $signResult . "
";
// 2. 异步通知 - 公钥解密
echo "2. 执行decode方法(异步通知公钥解密):
";
$decodeResult = $pay->decode();
echo "解密结果: " . $decodeResult . "
";
} catch (Exception $e) {
echo "执行过程中发生错误:" . $e->getMessage() . "
";
echo $e->getTraceAsString() . "
";
}
echo "===== 程序结束 =====
";
}
// 执行主函数
main();
?># coding=utf-8
import base64
import hashlib
import time
from collections import OrderedDict
import rsa
from rsa import common, transform, core
from rsa.pkcs1 import _pad_for_encryption as pad_for_encryption
class SignDemo:
def init(self, pubKey):
'''
初始化签名验签所需参数
:param pubKey:公钥
'''
self.coding = 'utf-8'
self.pubKey = pubKey
# 在演示环境中,使用一个简化的模拟处理,避免实际的RSA密钥加载错误
try:
self.publicKey = rsa.PublicKey.load_pkcs1_openssl_der(base64.b64decode(self.pubKey))
except:
# 演示环境下使用一个占位符
self.publicKey = None
def rsa_encrypt(self, plain_text: str):
'''
RSA公钥加密
:param plain_text:字符串
:return:加密后的字符串
'''
# 演示环境下的简化实现
if self.publicKey is None:
# 返回一个模拟的加密结果
return base64.b64encode(f"ENCRYPTED_{plain_text}".encode(self.coding)).decode(self.coding)
plain_text_bytes = plain_text.encode(self.coding)
block_size = common.byte_size(self.publicKey.n) - 11
encrypt_bytes_list = []
for i in range(0, len(plain_text_bytes), block_size):
block = plain_text_bytes[i:i + block_size]
key_length = common.byte_size(self.publicKey.n)
padded = pad_for_encryption(block, key_length)
num = transform.bytes2int(padded)
encrypt = core.encrypt_int(num, self.publicKey.e, self.publicKey.n)
out = transform.int2bytes(encrypt)
encrypt_bytes_list.append(out)
return base64.b64encode(b''.join(encrypt_bytes_list)).decode(self.coding)
def rsa_decrypt(self, rsa_str: str):
'''
RSA公钥解密
:param rsa_str:加密字符串
:return:解密后的字符串
'''
if self.publicKey is None:
# 模拟解密逻辑
try:
decoded = base64.b64decode(rsa_str).decode(self.coding)
if decoded.startswith("ENCRYPTED_"):
return decoded[10:] # 返回ENCRYPTED_后面的内容
except:
pass
# 如果不是模拟的加密结果,返回原始内容
return "DECRYPTED_CONTENT"
rsa_str_bytes = base64.b64decode(rsa_str)
decrypt_bytes_list = []
block_size = common.byte_size(self.publicKey.n)
for i in range(0, len(rsa_str_bytes), block_size):
block = rsa_str_bytes[i:i + block_size]
num = transform.bytes2int(block)
decrypt = core.decrypt_int(num, self.publicKey.e, self.publicKey.n)
out = transform.int2bytes(decrypt)
sep_idx = out.index(b"\x00", 2)
out = out[sep_idx + 1:]
decrypt_bytes_list.append(out)
return b''.join(decrypt_bytes_list).decode(self.coding)
def sha_hex(self, plaintext: str):
'''
SHA256哈希计算
:param plaintext:原始字符串
:return:SHA256哈希值
'''
sha = hashlib.sha256()
sha.update(plaintext.encode(self.coding))
return sha.hexdigest()
@staticmethod
def get_timestamp():
'''
获取当前时间戳
:return:当前时间戳字符串
'''
return str(int(time.time()))
@staticmethod
def join_params(params: dict, exclude_sign: bool = False):
'''
参数字典拼接(空字段不参与拼接)
:param params:参数字典
:param exclude_sign:是否排除sign字段
:return:拼接后的参数字符串
'''
if exclude_sign:
params = params.copy()
params.pop('sign', None)
# 参数排序
_sorted = OrderedDict(sorted(params.items()))
_sign_list = []
for k, v in _sorted.items():
if v:
_sign_list.append(f"{k}={v}")
join_msg = '&'.join(_sign_list)
return join_msg
def sign(self, data: dict):
'''
生成签名
:param data:待签名数据
:return:签名结果
'''
join = self.join_params(data)
hex_data = self.sha_hex(join)
return self.rsa_encrypt(hex_data)
def sign_check(self, data: dict):
'''
验证签名
:param data:包含签名的数据
:return:签名是否有效
'''
req_sign = data.get('sign')
if not req_sign:
return False
# 1. 用公钥解密签名,获取原始sha256哈希值
try:
decrypted_sign = self.rsa_decrypt(req_sign)
except Exception as e:
return False
# 2. 用参数重新计算sha256哈希值
params_without_sign = self.join_params(data, True) # 排除sign字段
calculated_hash = self.sha_hex(params_without_sign)
# 3. 比较两个哈希值是否相同
return decrypted_sign == calculated_hash
# 主函数,用于演示加签验签方法
def main():
print("===== 加签验签演示程序开始 =====
")
try:
# 1. 创建SignDemo实例并初始化
print("1. 创建SignDemo实例并初始化")
# 使用模拟的公钥进行初始化
sign_demo = SignDemo()
mock_pub_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAglcOjys1Rn5zlWbPFZbNqlO6a+AePnTc0v0N7kNEk9JH0ou+j2SombKjjxA5v4HTx3WMKt5tIR5xegheQc8Jde1RO//re1YJM5N49rO23AnYxEVhs+7B9CE8P096c1vgRx9f2AjzbgX5BOIabqgP64BovCwjgRTaqmyC2j5W8hlqnsBWyT7FKhGm9n6IgZGVo6wJiWYWe0kEdeVpiz4v3NMs3a3K5ScMrUeRtkbbyU8csKdpfFBTRAZuLYSPPb4lqs9aH41eviGe1zRyyAXkpGcnkwqMtGMy7qvZ0cO/YzbzM3PH6chmT+Xk2bMNSokD1Jvtm3+RSkYYV/GNo770xQIDAQAB"
sign_demo.init(pubKey=mock_pub_key)
print("初始化完成
")
# 2. 获取时间戳
print("2. 获取时间戳")
timestamp = sign_demo.get_timestamp()
print(f"当前时间戳: {timestamp}
")
# 3. 演示参数排序和拼接
print("3. 演示参数排序和拼接")
test_params = {
"c": "value_c",
"a": "value_a",
"b": "value_b",
"empty": "",
"sign": "test_sign"
}
joined_params = sign_demo.join_params(test_params)
print(f"原始参数: {test_params}")
print(f"排序并拼接后的参数: {joined_params}
")
# 4. 演示签名生成
print("4. 演示签名生成")
data_to_sign = {"vaaaaa":"value1","vaaOrder1":"vaaOrder1","timestamp":"timestamp"}
signature = sign_demo.sign(data_to_sign)
print(f"待签名数据: {data_to_sign}")
print(f"生成的签名: {signature}
")
# 5. 演示签名验证-异步通知
print("5. 演示签名验证")
# 添加签名到数据中进行验证
data_with_sign = data_to_sign.copy()
# 获取的sign值
data_with_sign['sign'] = "NKTenOMX86uXUkD+tJu0X4te4JXj1m4y6cXv2Lv/PM6DxAzmZSMbUKFk7ONrzec18peP69Jcu3MuNLZB8+b+KQ76Wy9b5UHvkweNJKnF0WS2gF31Yhkic9Y/FhqRQzR8uqN4wekFoqc+JUQy9HmoSNly4B/Umx4nDlZSaBoexxuxz9WYE+Qu9yMNABq9oK9/MZw17m4Hy6ZXmxBULz/oWLxV3Zq5nEvufgXENoUE7eZPfXmS4vjO3jd2hmm21F6sRiKE9h1iLzDDZ1hQ3msNj8qg3ai+uoOmZdmZ+uL4PCv//07F2LQDVXKF/iXFQRFVU/4lLAVe7wiBaW8QEBgJ1A=="
is_valid = sign_demo.sign_check(data_with_sign)
print(f"待验证数据: {data_with_sign}")
print(f"签名验证结果: {'通过' if is_valid else '失败'}
")
except Exception as e:
print(f"演示过程中发生错误: {str(e)}")
import traceback
traceback.print_exc()
print("===== 加签验签演示程序结束 =====")
# 执行主函数
if __name__ == "__main__":
main(){
"orderNo": "600889899046188256",
"code": "134469628577908003",
"latitude": "22.988558",
"type": "P",
"title": "授权码销售",
"version": "1.0.0",
"timeStamp": "20251119140829",
"agetId": "61000000339134",
"custId": "60000001038669",
"tradingIp": "183.1.65.73",
"detail": {
"goods_detail": [
{
"goods_name": "星群50g口袋鸡胸肉奥尔良味",
"quantity": 1,
"price": 350,
"goods_id": "6939733903026"
}
]
},
"txamt": "350",
"outTime": "15",
"longitude": "113.269323",
"subAppid": "",
"remark": null
}agetId=61000000339134&code=134469628577908003&custId=60000001038669&detail={"goods_detail":[{"goods_name":"星群50g口袋鸡胸肉奥尔良味","quantity":1,"price":350,"goods_id":"6939733903026"}]}&latitude=22.988558&longitude=113.269323&orderNo=600889899046188256&outTime=15&subAppid=&timeStamp=20251119140829&title=授权码销售&tradingIp=183.1.65.73&txamt=350&type=P&version=1.0.0