php 与java对接国密sm2遇到的一些问题(招商银行接口)

最近在对接招行的云直联接口,使用国密算法,加签加密逻辑为:使用用户SM私钥签名->用户的对称(SM4)密钥加密;由于招行只提供了C#和java的对接示例所以需要找一个可用的PHP国密的包,在github上找到几个但是都无法顺利验签成功,特地来问下各位有什么办法(SM4加解密已验证通过,就是SM2签名一致提示无效)。
先说下第一个包:
github.com/lpilp/phpsm2sm3sm4
招行提供的用户私钥(SM2):

NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=

我方调用代码:

use Rtgm\sm\RtSm2;

$key='NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=';
$key=bin2hex($key);//转为16进制
$sm2 = new RtSm2('base64');
$sign = $sm2->doSign($data, $key);
$sign=trim($sign);

这里遇到第一个问题是,$sm2->doSign($data, $key)方法只支持16进制的密码

php 与java 对接国密sm2遇到的一些问题
如果不将私钥转为16进制的话调用就会报错:

php 与java 对接国密sm2遇到的一些问题
附上_dosign()截图

php 与java 对接国密sm2遇到的一些问题
如果将私钥转为16进制的话调用的话签名正常,但是java验签不通过。
第二个包:
github.com/lat751608899/sm2
调用示例

$key='NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=';
$key=base64_decode($key);
$pubKey = new \Lat\Ecc\PublicKey();
$pubKey->parse($key);
$sm2 = new \Lat\Ecc\Sm2();
$res = $sm2->pubEncrypt($pubKey, $data);
dd($res);

出现以下异常

php 与java 对接国密sm2遇到的一些问题

不知各位有什么建议或者有没有和招行那边对接国密的PHP demo?
附上招行给的java示例:gitee.com/zhong-xiaokai/cmbjavasm

==============================================================================
最终在latzou(github.com/lat751608899) 的帮助下完成了对接,非常感谢!
具体demo如下,后面有对接需要的小伙伴可以参考下:

<?php

use Mdanter\Ecc\Crypto\Key\PrivateKey;
use Mdanter\Ecc\Crypto\Signature\Signature;
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
use Rtgm\ecc\RtEccFactory;
use Rtgm\sm\RtSm2;

require 'vendor/autoload.php';
//sm2签名
$data = '{"request":{"body":{"TEST":"中文","TEST2":"!@#$%^&*()","TEST3":12345,"TEST4":[{"arrItem1":"qaz","arrItem2":123,"arrItem3":true,"arrItem4":"中文"}],"buscod":"N02030"},"head":{"funcode":"DCLISMOD","userid":"N003261207"}},"signature":{"sigdat":"__signature_sigdat__"}}';
$key = 'NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=';
$key = bin2hex(base64_decode($key));//转为16进制
$sm2 = new RtSm2('base64');
$userid = 'N003261207' . "0000000000000000";
$userid = substr($userid, 0, 16);

$sign = $sm2->doSign($data, $key, $userid);
$sign = base64_decode($sign);
$a = \FG\ASN1\ASNObject::fromBinary($sign)->getChildren();

$aa = formatHex($a[0]->getContent());
$bb = formatHex($a[1]->getContent());
$sign = $aa. $bb;
$sign = base64_encode(hex2bin($sign));
var_dump($sign);
//sm4加密
$iv = substr($userid.'0000000000000000', 0,16) ;
$sm4 = new RtSm4($key);
$sign = $sm4->encrypt($data,'sm4',$iv,'base64');

//sm2验签
$signHex = bin2hex(base64_decode($sign));
var_dump($signHex);
$r = substr($signHex, 0, 64);
$s = substr($signHex, 64, 64);
var_dump($r, $s);
$r = gmp_init($r, 16);
$s = gmp_init($s, 16);
/*$r = gmp_init('90416529259334433398865842692135340273188180784859666141339740103133164395295', 10);
$s = gmp_init('51927610271972364114244381230895889971736075490328811928131691394657016568041', 10);*/
$signature = new Signature( $r, $s );
$serializer = new DerSignatureSerializer();
$serializedSig = $serializer->serialize($signature);

$sign = base64_encode($serializedSig);
var_dump($sign);
$adapter = RtEccFactory::getAdapter();
$generator = RtEccFactory::getSmCurves()->generatorSm2();
$secret = gmp_init($key, 16);
$key = new PrivateKey($adapter, $generator, $secret);
$pubkey = $key->getPublicKey()->getPoint();
$x = $pubkey->getX();
$y = $pubkey->getY();
$pub = gmp_strval($x, 16);
$pub .= gmp_strval($y, 16);
var_dump($pub);
$b = $sm2->verifySign($data, $sign, $pub, $userid);
var_dump($b);


function formatHex($dec)
{

    $hex = gmp_strval(gmp_init($dec, 10), 16);
    $len = strlen($hex);
    if ($len == 64) {
        return $hex;
    }
    if ($len < 64){
        $hex = str_pad($hex, 64, "0", STR_PAD_LEFT);
    }else {
        $hex = substr($hex, $len - 64, 64);
    }

    return $hex;
}
最佳答案

问题已解决,非常感谢latzou,具体方案已附到原文

1个月前 评论
hon-陈烁临 1个月前
讨论数量: 32
use Rtgm\sm\RtSm2;

$key=base64_decode('NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=');
$key=bin2hex($key);//转为16进制
$sm2 = new RtSm2('base64');
$sign = $sm2->doSign($data, $key);
$sign=trim($sign);

这样试下吧

1个月前 评论
昵称过于个性导致系统无法显示 (楼主) 1个月前
昵称过于个性导致系统无法显示 (楼主) 1个月前
deatil (作者) 1个月前
deatil (作者) 1个月前
use Rtgm\sm\RtSm2;

$key=base64_decode('NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=');

$key=bin2hex($key);//转为16进制
$sm2 = new RtSm2('base64');
$sign = $sm2->doSign($data, $key);
$sign=trim($sign);

改了的这个就该是对的

1个月前 评论
昵称过于个性导致系统无法显示 (楼主) 1个月前
deatil (作者) 1个月前
deatil (作者) 1个月前
昵称过于个性导致系统无法显示 (楼主) 1个月前
deatil (作者) 1个月前
昵称过于个性导致系统无法显示 (楼主) 1个月前
deatil (作者) 1个月前

file
这个包应该没问题,只不过里面的代码逻辑可能出了点问题.方便截个_dosign函数的图吗?年前我对接银行支付的时候好像遇到过这种问题,怎样处理的记不太清了

1个月前 评论
昵称过于个性导致系统无法显示 (楼主) 1个月前

前段时间处理过招行国密的问题,用go实现的,然后供php调用

1个月前 评论

半年前处理过一个邮储的国密问题,直接用java的demo做成java的webserver供php调用,你用的这个php包我也试过了,签名出来与java结果不一致,我还试了其它能找到的php包,都不行

1个月前 评论
renxiaotu (作者) 1个月前

file
找到调用的这个方法,
这句
$dec = gmp_init($dec, 10);
改成
if (is_string($dec)){
$dec = gmp_init($dec, 10);
}

原来测试demo示例

PHP
加密的数据不需要转化16进制, json_encode一下就可以,咱们报错是一样的

1个月前 评论

问题已解决,非常感谢latzou,具体方案已附到原文

1个月前 评论
hon-陈烁临 1个月前

您好,我也遇到了招行这个国密的问题,按照您的demo还是失败,具体截图如下:

file

file

还有这个提示

PHP

3周前 评论
昵称过于个性导致系统无法显示 (楼主) 3周前
昵称过于个性导致系统无法显示 (楼主) 3周前
wotainanle (作者) 3周前
昵称过于个性导致系统无法显示 (楼主) 3周前
wotainanle (作者) 3周前

周末这么快回复,真的感谢!!!
用了demo中的数据,也对KEY做了ASSIIC码排序,请求结果还是不行

PHP

3周前 评论

楼主你好,我在跟你之前对接同样的业务 我按照你的方法 调用 但是出了点小问题 不知道怎么弄 求解答一下谢谢了

file

1周前 评论

@昵称过于个性导致系统无法显示 楼主你好, 请求完后的数据 验签使用银行给的公钥么, 我看代码中没有使用公钥 $pub = gmp_strval($x, 16); $pub .= gmp_strval($y, 16); var_dump($pub); $b = $sm2->verifySign($data, $sign, $pub, $userid);

6天前 评论
hashxb 2天前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!