lpilp / phpsm2sm3sm4

php版本,支持国密SM2的签名算法,非对称加解密,SM3的hash, SM4的对称加解密
329 stars 76 forks source link

PHP SM2 对接JAVA问题 #90

Closed yhhhhh closed 1 month ago

yhhhhh commented 1 month ago

数据进行SM2加密、加签后请求JAVA那边不成功 image 报错:验签失败

JAVA的参考demo

package com.psbc.pay.demo.service.impl;

import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.psbc.pay.demo.service.HttpService; import com.psbc.pay.demo.service.PaymentService; import com.psbc.pay.demo.util.BCECUtils; import com.psbc.pay.demo.util.FileUtils; import com.psbc.pay.demo.util.SM2Utils; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import org.bouncycastle.util.encoders.Base64; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;

@Service @Slf4j public class PaymentServiceImpl implements PaymentService { // 渠道单位私钥 private final static ConcurrentMap<String, BCECPrivateKey> PRI_KEY_PARAMS = new ConcurrentHashMap<>(); // 缴费平台公钥 private final static ConcurrentMap<String, BCECPublicKey> PUB_KEY_PARAMS = new ConcurrentHashMap<>();

@Resource
private HttpService httpService;

/** 缴费平台公钥 */
@Value("${openpay.publickey}")
private String openPayPublicKey;
/** 渠道单位私钥 */
@Value("${openpay.privatekey}")
private String openPayPrivateKey;
/** 缴费平台联机交易地址,该demo配置的是测试环境的地址,如需生产环境地址请在接口文档中获取 */
@Value("${openpay.uri.onlinetrans}")
private String openPayUri;
/** 缴费平台对账文件下载地址,该demo配置的是测试环境的地址,如需生产环境地址请在接口文档中获取 */
@Value("${openpay.uri.payfile}")
private String openPayFileUri;

@Override
public String paymentEncrypt(@RequestBody String request) {
    log.info("开始构造订单支付报文");
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }

    JSONObject requestJO = JSON.parseObject(request);
    log.info("请求报文为:{}", requestJO.toJSONString());
    // 渠道商代号,由开放式缴费平台分配
    final String channelId = requestJO.getString("channelId");
    // 缴费项目编号,由开放式缴费平台提供,渠道商线下维护到其系统
    final String payItemNo = requestJO.getString("payItemNo");
    // 交易金额,单位为分
    final String txnAmt = requestJO.getString("txnAmt");
    // 渠道商用户标识id
    final String openId = requestJO.getString("openId");
    final String orderNo = UUID.randomUUID().toString().replace("-", "");
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "pay");
    head.put("msgId", new DateTime().toString("yyyyMMddHHmmssSSS"));
    head.put("channelId", channelId);
    message.put("head", head);
    body.put("payItemNo", payItemNo);
    body.put("orderNo", orderNo);
    body.put("orderDesc", "test");
    body.put("txnAmt", String.valueOf(new BigDecimal(txnAmt).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP)));
    body.put("backUrl", "https://test.pay.com/notice");//后台通知渠道商支付结果地址,请根据实际地址填写
    body.put("frontUrl", "http://wxtest.smeia.cn/h5/#/paySuccess");//收银台支付完成返回商户跳转地址,请根据实际地址填写
    body.put("txnType", "1");
    body.put("txnTime", new DateTime().toString("yyyyMMddHHmmss"));
    body.put("openId", openId);
    message.put("body", body);
    final String plaintext = message.toJSONString();
    log.info("下单明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        final String cipherInfo = signature + "|+|" + ciphertext;
        final String uri = openPayUri + channelId;
        // 调用缴费平台接口
        String response = httpService.sendPost(uri, cipherInfo);
        // 解密返回报文
        return decrypt(response, channelId);
    } catch (Exception e) {
        log.error("支付缴费下单失败", e);
        throw new RuntimeException("系统错误");
    }
}

@Override
public String queryEncrypt(String request) {
    log.info("开始构造订单状态确认报文");
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }
    JSONObject requestJO = JSON.parseObject(request);
    log.info("请求报文为:{}", requestJO.toJSONString());
    // 渠道商代号,由开放式缴费平台分配
    final String channelId = requestJO.getString("channelId");
    // 缴费项目编号,由开放式缴费平台提供,渠道商线下维护到其系统
    final String payItemNo = requestJO.getString("payItemNo");
    // 渠道商生成的订单号,保证永久唯一
    final String orderNo = requestJO.getString("orderNo");
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "confirm");
    head.put("msgId", new DateTime().toString("yyyyMMddHHmmssSSS"));
    head.put("channelId", channelId);
    message.put("head", head);
    body.put("payItemNo", payItemNo);
    body.put("orderNo", orderNo);
    message.put("body", body);
    final String plaintext = message.toJSONString();
    log.info("查询明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        final String cipherInfo = signature + "|+|" + ciphertext;
        final String uri = openPayUri + channelId;
        // 调用缴费平台接口
        String response = httpService.sendPost(uri, cipherInfo);
        // 解密返回报文
        return decrypt(response, channelId);
    } catch (Exception e) {
        log.error("订单支付确认查询失败", e);
        throw new RuntimeException("系统错误");
    }
}

@Override
public String refundEncrypt(String request) {
    log.info("开始构造退款报文");
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }
    JSONObject requestJO = JSON.parseObject(request);
    log.info("请求报文为:{}", requestJO.toJSONString());
    // 渠道商代号,由开放式缴费平台分配
    final String channelId = requestJO.getString("channelId");
    // 缴费项目编号,由开放式缴费平台提供,渠道商线下维护到其系统
    final String payItemNo = requestJO.getString("payItemNo");
    // 原支付成功订单号,保证永久唯一
    final String orderNo = requestJO.getString("orderNo");
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "refund");
    head.put("msgId", new DateTime().toString("yyyyMMddHHmmssSSS"));
    head.put("channelId", channelId);
    message.put("head", head);
    body.put("payItemNo", payItemNo);
    body.put("orderNo", orderNo);
    body.put("refundReason", "退款测试");
    message.put("body", body);
    final String plaintext = message.toJSONString();
    log.info("查询明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        final String cipherInfo = signature + "|+|" + ciphertext;
        final String uri = openPayUri + channelId;
        // 调用缴费平台接口
        String response = httpService.sendPost(uri, cipherInfo);
        // 解密返回报文
        return decrypt(response, channelId);
    } catch (Exception e) {
        log.error("支付缴费订单退款失败", e);
        throw new RuntimeException("系统错误");
    }
}

@Override
public String callbackEncrypt(String request) {
    // 渠道商代号,由开放式缴费平台分配,请根据实际情况填写
    final String channelId = "10000226526546";
    log.info("收到支付结果通知,报文为:{}", request);
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }
    // 验签并解密报文
    String decryptRequest = decrypt(request, channelId);
    JSONObject requestJO = JSON.parseObject(decryptRequest);
    JSONObject headJO = requestJO.getJSONObject("head");
    // 根据请求报文结果做对应处理,此样例仅做打印报文处理
    log.info("收到支付结果通知,报文为:{}", requestJO.toJSONString());

    //构造响应报文
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "paycallback");
    head.put("msgId", headJO.getString("msgId"));
    head.put("channelId", headJO.getString("channelId"));
    // 返回处理结果:000000-接收成功,其他则接收失败
    head.put("respCode", "000000");
    head.put("respMsg", "success");
    message.put("head", head);
    message.put("body", body);

    final String plaintext = message.toJSONString();
    log.info("支付结果通知响应报文明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        return signature + "|+|" + ciphertext;
    } catch (Exception e) {
        log.error("支付结果通知处理失败", e);
        throw new RuntimeException("系统错误");
    }

}

@Override
public String payFileDownloadEncrypt(String request) {
    log.info("开始构造下载对账文件报文");
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }
    JSONObject requestJO = JSON.parseObject(request);
    log.info("请求报文为:{}", requestJO.toJSONString());
    // 渠道商代号,由开放式缴费平台分配
    final String channelId = requestJO.getString("channelId");
    // 交易日期
    final String txnDate = requestJO.getString("txnDate");
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "reconcile");
    head.put("msgId", new DateTime().toString("yyyyMMddHHmmssSSS"));
    head.put("channelId", channelId);
    message.put("head", head);
    body.put("txnDate", txnDate);
    message.put("body", body);
    final String plaintext = message.toJSONString();
    log.info("下载对账文件明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        final String cipherInfo = signature + "|+|" + ciphertext;
        final String uri = openPayFileUri + channelId;
        // 调用缴费平台接口
        String response = httpService.sendPost(uri, cipherInfo);
        // 解密返回报文
        String decryptResponse = decrypt(response, channelId);
        JSONObject responseJO = JSON.parseObject(decryptResponse);
        log.info("下载对账文件响应报文:{}", responseJO.toJSONString());
        // 文件内容密文
        String fileContent = responseJO.getJSONObject("body").getString("fileContent");
        // 解析密文生成文件,解压文件
        final String zipFilePath = "F:\\paydemo\\" + channelId + "_" + txnDate + ".zip";
        final String unzipDir = "F:\\paydemo\\" + channelId;
        FileUtils.decodeFileContent(zipFilePath, fileContent);
        FileUtils.unzip(zipFilePath, unzipDir);
        // 文件下载解压成功,需解析文件内容等操作,请参考接口文档对账文件格式。此样例仅返回文件路径
        return unzipDir;
    } catch (Exception e) {
        log.error("下载对账文件失败", e);
        throw new RuntimeException("系统错误");
    }
}

@Override
public String repeatRefundEncrypt(String request) {
    log.info("开始构造部分退款报文");
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }
    JSONObject requestJO = JSON.parseObject(request);
    log.info("请求报文为:{}", requestJO.toJSONString());
    // 渠道商代号,由开放式缴费平台分配
    final String channelId = requestJO.getString("channelId");
    // 缴费项目编号,由开放式缴费平台提供,渠道商线下维护到其系统
    final String payItemNo = requestJO.getString("payItemNo");
    // 原支付成功订单号,保证永久唯一
    final String orderNo = requestJO.getString("orderNo");
    // 部分退款金额,单位为分
    final String refundAmt = requestJO.getString("refundAmt");
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "repeatRefund");
    head.put("msgId", new DateTime().toString("yyyyMMddHHmmssSSS"));
    head.put("channelId", channelId);
    message.put("head", head);
    body.put("payItemNo", payItemNo);
    body.put("orderNo", orderNo);
    body.put("outerRefundNo", UUID.randomUUID().toString().replace("-", ""));
    body.put("refundReason", "部分退款测试");
    body.put("refundAmt", refundAmt);
    body.put("extends1", "");
    body.put("extends2", "");
    body.put("extends3", "");
    body.put("extends4", "");
    message.put("body", body);
    final String plaintext = message.toJSONString();
    log.info("查询明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        final String cipherInfo = signature + "|+|" + ciphertext;
        final String uri = openPayUri + channelId;
        // 调用缴费平台接口
        String response = httpService.sendPost(uri, cipherInfo);
        // 解密返回报文
        return decrypt(response, channelId);
    } catch (Exception e) {
        log.error("部分退款失败", e);
        throw new RuntimeException("系统错误");
    }
}

@Override
public String refundQueryEncrypt(String request) {
    log.info("开始构造退款状态查询报文");
    if (null == request || request.isEmpty()) {
        throw new RuntimeException("请求报文不能为空!");
    }
    JSONObject requestJO = JSON.parseObject(request);
    log.info("请求报文为:{}", requestJO.toJSONString());
    // 渠道商代号,由开放式缴费平台分配
    final String channelId = requestJO.getString("channelId");
    // 缴费项目编号,由开放式缴费平台提供,渠道商线下维护到其系统
    final String payItemNo = requestJO.getString("payItemNo");
    // 原支付订单号
    final String orderNo = requestJO.getString("orderNo");
    // 退款单号
    final String outerRefundNo = requestJO.getString("outerRefundNo");
    JSONObject message = new JSONObject();
    JSONObject head = new JSONObject();
    JSONObject body = new JSONObject();
    head.put("version", "1.0.0");
    head.put("trancode", "refundQuery");
    head.put("msgId", new DateTime().toString("yyyyMMddHHmmssSSS"));
    head.put("channelId", channelId);
    message.put("head", head);
    body.put("payItemNo", payItemNo);
    body.put("orderNo", orderNo);
    body.put("outerRefundNo", outerRefundNo);
    body.put("extends1", "");
    body.put("extends2", "");
    body.put("extends3", "");
    body.put("extends4", "");
    message.put("body", body);
    final String plaintext = message.toJSONString();
    log.info("退款状态查询明文为:{}", plaintext);
    try {
        // 加密
        final String ciphertext = encrypt(channelId, plaintext);
        // 加签
        final String signature = sign(channelId, ciphertext);
        final String cipherInfo = signature + "|+|" + ciphertext;
        final String uri = openPayUri + channelId;
        // 调用缴费平台接口
        String response = httpService.sendPost(uri, cipherInfo);
        // 解密返回报文
        return decrypt(response, channelId);
    } catch (Exception e) {
        log.error("退款状态查询失败", e);
        throw new RuntimeException("系统错误");
    }
}

private String decrypt(String ciphertextInfo, String channelId) {
    final int index = ciphertextInfo.indexOf("|+|");
    final String signature = ciphertextInfo.substring(0, index);
    final String ciphertext = ciphertextInfo.substring(index + "|+|".length());
    try {
        // 验签
        final boolean  =verifyResult verify(channelId, signature, ciphertext);
        log.info("密文验签结果为:{}", verifyResult);
        // 解密
        BCECPrivateKey privateKey = PRI_KEY_PARAMS.get(channelId);
        if (privateKey == null) {
            // 支付缴费热加载证书
            reloadPaymentCert(channelId);
            privateKey = PRI_KEY_PARAMS.get(channelId);
        }
        final byte[] ciphertextBytes = Base64.decode(ciphertext);
        final byte[] decryptedCiphertext = SM2Utils.decrypt(privateKey, ciphertextBytes);
        final byte[] plaintextBytes = Base64.decode(decryptedCiphertext);
        final String plaintext =1123 new String(plaintextBytes, StandardCharsets.UTF_8);

        log.info("结束支付缴费密文验签解密,明文为: {}", plaintext);
        return plaintext;
    } catch (Exception e) {
        log.error("验签解密失败", e);
        throw new RuntimeException("验签解密失败");
    }
}

private String sign(String channelId, String ciphertext) throws CryptoException {
    BCECPrivateKey privateKey = PRI_KEY_PARAMS.get(channelId);
    if (privateKey == null) {
        // 支付缴费热加载证书
        reloadPaymentCert(channelId);
        privateKey = PRI_KEY_PARAMS.get(channelId);
    }
    final byte[] signCiphertextBytes = ciphertext.getBytes(StandardCharsets.UTF_8);
    final byte[] sign = SM2Utils.sign(privateKey, signCiphertextBytes);
    return ByteUtils.toHexString(sign);
}

private boolean verify(String channelId, String signature, String ciphertext) {
    BCECPublicKey publicKey = PUB_KEY_PARAMS.get(channelId);
    if (publicKey == null) {
        // 支付缴费热加载证书
        reloadPaymentCert(channelId);
        publicKey = PUB_KEY_PARAMS.get(channelId);
    }
    final byte[] sign = ByteUtils.fromHexString(signature);
    final byte[] bytes = ciphertext.getBytes(StandardCharsets.UTF_8);
    return SM2Utils.verify(publicKey, bytes, sign);
}

private String encrypt(String channelId, String plaintext) throws InvalidCipherTextException {
    BCECPublicKey publicKey = PUB_KEY_PARAMS.get(channelId);
    if (publicKey == null) {
        // 支付缴费热加载证书
        reloadPaymentCert(channelId);
        publicKey = PUB_KEY_PARAMS.get(channelId);
    }
    final byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
    final byte[] encodedPlaintext = Base64.encode(plaintextBytes);
    final byte[] ciphertextBytes = SM2Utils.encrypt(publicKey, encodedPlaintext);
    return Base64.toBase64String(ciphertextBytes);
}

private void reloadPaymentCert(final String channelId) {
    //支付缴费证书管理加载
    try {
        // 缴费平台公钥
        final byte[] publicKeyBytes = Base64.decode(openPayPublicKey);
        final BCECPublicKey publicKey = BCECUtils.convertX509ToECPublicKey(publicKeyBytes);
        // 渠道单位私钥
        final byte[] privateKeyBytes = Base64.decode(openPayPrivateKey);
        final BCECPrivateKey privateKey = BCECUtils.convertPKCS8ToECPrivateKey(privateKeyBytes);
        PUB_KEY_PARAMS.put(channelId, publicKey);
        PRI_KEY_PARAMS.put(channelId, privateKey);
    } catch (final Exception e) {
        throw new RuntimeException("获取密钥出错", e);
    }
}

}

SM2Utils代码

package com.psbc.pay.demo.util;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.engines.SM2Engine.Mode; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithID; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;

import java.math.BigInteger; import java.security.SecureRandom;

/**

lpilp commented 1 month ago

格式的问题,看代码签名的是 hex, 加密的用base64 PHP代码生成的 签名是 base64的 ; 加密返回的是 hex, java中前面有个04, 请加个04 , bin2hex(base64_decode($sm2->doSign( $document, $privateKey, $userId))) .'|+|' base64_encode(hex2bin('04'. $sm2->doEncrypt($document1, $publicKey))) , 至于参数是什么格式请按你们相关的文档操作

yhhhhh commented 1 month ago

@lpilp 改了一下加密的格式, 签名是生成的hex格式,还是验签失败 $encrypt = base64_encode(hex2bin('04'. $sm2->doEncrypt(base64_encode($data), $this->public_key))); $sign = $sm2->doSign( $encrypt, $this->private_key);

对方给的demo种的加密后内容: BNavgmldTe4IgFufjkGY4vpR8lexkeqjcvPRPeapr2jDMcSx1d+fWIvsC+QCMQvqhP4mXfl8RTOiCJQortbEKjEkX4bBJKYMUHbN2CYf3oKWPaOp54WiFG/WUzme/IPAMrhlirw4ytgGZTv3i3/hJBMxeXaXFttDMajK9cimfc462qOA+zk7Jv9azzxbtwRdcVZfYw78KbdNl/u9307pBKlqN9w3vUt7bB0q4/dIhxVllu3l+pljgRn+be0tIuoZFW6b6HMt3YssVegh7qPzLYNFEOHXdAQN/3IMGm0cIqL3SnJ0iBMmT7reAOnqe+UC/pRHjWEvFdrcGzVHw8Ce7GOrEcZV2dmODTHxbvPyBqdnZLJ9+ghK1FWSi9TVX5H8xjLVuDor+TTC+ZAmZeV6INivjpqL64SYm5Bs82vH8MMPfwNmTaNaf8Qm/kwRMGDdYX216f0GEXJVyGyL9129rdwnEN6JtIMEz7HN2hHPUcl0lzJXH3Ds6xyj/2LcbajWBisugtjgsX5fm2pR/KAhYj6Ft1UYfIsAuFnDHSnZmRfmbeQl+QLZdZMP9ioYi/cl/TkO90L8VykGK/A15+3Xpy6vpekW5BMFGLx1wLp1IRhhvwT7HGSXpa5NEkP1uopQNzNYXIpgbN4F3ErgvIExsOqquit+ 尝试了下是先hex2bin后base64_encode

加密: BOJ8N4DnBpvacIKiOkidd1h84wlYPtmSU/ZuHZgz7RodC1zobcZxTpl0zyWFiROdexhV6Mn6LywRde4SOpWiPptUtYBzyrQG2/3ipb/CJJemWM4HEhfQ4+7BKQdNU9N+OA8w85NI8/E42b17CWuG1twiz/56z+riE80PGMzM7ZG+3QaJDIbRdnkGGjtgMOm+keFIVtJAVMxYE3ff/9g10m5C++TWvq2ryimNiGL4lRJQSOEFt/BhwYaSlRxm9t4thbo2iASQxdrtSoCeSQraGophDTcmwiHQvN596FYCUFsObJANzCdS2HqBzmDN6sgG/ZIIyguqqcOi7f2xylVk4M+ByNV0upvaHucotVOQpAArFC6N/C0WhvOxJKqNBIs2ynMhWaQc8BFQ4Nktm809J6DZaEEOLVAtSuyJqq9aSAlH4r5eHdIC5UNgZ5OxvFgoE4pdHEy+B/kn8EIYV+0gsqSWxCdM5szf6eZy5TchEMgvV2jJI2nesz/R1TzSRjZ4pa4aL5Orfa+Co3rhpYjFO2OWJ4ZshTz/dgsE4iGFuxhBQSo64uYd4wK8bS2nCBUBy76p0MY67/u2GlohRwImFqflPz8200JzXQYnFKCbbzRfc0+2EdULvNRUmPGoeC6EJn4Y5gtrs+pISI834yEnR7eC2eDG2XPVa9tSX/sfL/LjgM3DxZt2+/3X/EqwynTFHPE0LXz9eeC8k0Nk0MvI8cQ4h30MQIHF93yVW6iIqpitCvn7iwvWyn+OcwONyCZw0pD8dnE8DDgy5HAGnw==

签名: 3045022100bd925f7c8169379bcb8abb45b41f456376efa8731f1d9dc9f14a0ab20bfda740022011e0d77f8b4b0a5c2e0b8f6e929aed0d5259254eb75827bcded79d0bfd6e5791

私钥:94CF3533121E7EC817xxxxxxxxxxxxxxxxxxxxxxxxxxFEFE04D9B71EDA5C63663DC 公钥:4EBC4F2A6C00FC265CD29EBA99084742A45085E23577949AFFDD609152F9B8BEE93A4A8F400102DF74452DDDEF478BB9678A6A90A92E6EA859B618A2BB975EDE

lpilp commented 1 month ago

先单独测试下签名吧,用PHP签名,java 验签名, 看了下代码用的 使用 https://github.com/ZZMarquis/gmhelper 封装的 BC库的SM2, 这个库试过与PHP的这个库是可以互相签名的, 1 一样一样的试,先试下签名是否互认 ,就简单的签名如“hello”之类的 2 再试加解密 都可以再结合测试,中间的 要加密与签名的数据有点多,可能这里有点问题

yhhhhh commented 1 month ago

就是那个格式问题,请求成功了,也解密成功了,感谢大佬 @lpilp