技术饭
php的sm2加密、签名、验签,sm4加密
php的sm2加密、签名、验签,sm4加密,为了保障商用密码的安全性,国家密码局制定了一系列密码标准,包括:SM1(SCB2)、SM2、SM3、SM4、SM7、SM9、祖冲之密码算法(ZUC) 等。其中SM1、SM4、SM7、祖冲之密码(ZUC)是对称算法。SM2、SM9是非对称算法。SM3是哈希算法,其中SM1、SM7算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
php版本正常可以使用开源的lpilp/guomi包来做签名、验签、加密等操作,但是开源的存在风险,建议是直接拉去下来自己打包,这里就不在讲解可以看开源库的文档
gitcode:https://gitcode.net/mirrors/lpilp/phpsm2sm3sm4
github:https://github.com/lpilp/phpsm2sm3sm4
如果是直接使用openssl的话可以参考:php的openssl加密扩展实现
如果是自行开发需要编译封装一个gmssl库或者其他库成为php的扩展,php -m 加载gmp、gmssl或者是gmkit
GmSSL:https://github.com/GmSSL/GmSSL-PHP
sop/asn1:https://github.com/sop/asn1
签名、验签:
<?php
namespace SmSign{
use Sop\ASN1\Type\Constructed\Sequence;
use Sop\ASN1\Type\Primitive\Integer;
use Sop\ASN1\Type\UnspecifiedType;
// use Rtgm\sm\RtSm4;
// use Rtgm\sm\RtSm2;
class SmSign
{
/**
* 生成签名
* @param $document
* @param $prikey
* @return string
*/
public function doSign($document, $prikey){
//按64位截取
$privateKey = str_split($prikey, 64);
$key = [
'd' => hex2bin($privateKey[2]),
'P' => [
hex2bin($privateKey[0]),
hex2bin($privateKey[1])
]
];
[
'R'=> $r,
'S'=> $s
] = gmkit_sm2_sign($key, $document);
$sequence = new Sequence(
new Integer(gmp_init(bin2hex($r), 16)),
new Integer(gmp_init(bin2hex($s), 16))
);
return bin2hex($sequence->toDER());
//sm2
//$sm2 = new RtSm2('hex', false);
//return $sm2->doSign($document, $prikey);
}
/**
* 验证签名
* @param $document
* @param $sign
* @param $pubkey
* @return mixed
*/
public function verifySign($document, $sign, $pubkey){
//公钥:去除开头04
$P = [
hex2bin(substr($pubkey, 2, 64)),
hex2bin(substr($pubkey,-64))
];
//解码asn1编码得到原始的r、s再验签
$der = hex2bin($sign);
$seq = UnspecifiedType::fromDER($der)->asSequence();
$si = new Sequence(
new Integer(gmp_init($seq->at(0)->asInteger()->number(), 10)), // 十进制
new Integer(gmp_init($seq->at(1)->asInteger()->number(), 10))
);
$signature = [
"R" => hex2bin(substr(bin2hex($si->at(0)->toDER()), -64)), // 取出后64位
"S" => hex2bin(substr(bin2hex($si->at(1)->toDER()), -64))
];
return gmkit_sm2_verify($P, $signature, $document);
//sm2
//$sm2 = new RtSm2('hex', false);
//return $sm2->verifySign($document, $sign, $pubkey);
}
}
}
sm2加密、sm4加密:
<?php
namespace Sm {
// use Rtgm\sm\RtSm4;
// use Rtgm\sm\RtSm2;
class Sm
{
/**
* 补位
* @param $lack
* @return string
*/
private function pad($lack) {
return str_repeat(chr($lack), $lack);
}
/**
* sm4加密
* @param $content
* @return string
*/
public function sm4_encrypt($content = "", $aesKey = ""){
if(empty($content) || empty($aesKey)){
return "";
}
//加密信息
$content = $content.$this->pad(16 - (strlen($content) % 16));
$c = '';
foreach (str_split($content, 16) as $chunk) {
$c .= gmkit_sm4($aesKey, $chunk);
}
return base64_encode($c);
//sm4
//$sm4 = new RtSm4($aesKey);
//return $sm4->encrypt($content, "sm4-ecb", "", "base64");
}
/**
* 密码sm2加密
* @param $publicKey
* @return string
*/
public function sm2_encrypt($document = "", $publicKey = ""){
if(empty($document) || empty($publicKey)){
return "";
}
//公钥
$P = [
hex2bin(substr($publicKey,2,64)),
hex2bin(substr($publicKey,-64))
];
[
'C1'=> [$x, $y],
'C2'=> $C2,
'C3'=> $C3
] = gmkit_sm2_encrypt($P, $document);
return "04".bin2hex($x).bin2hex($y).bin2hex($C2).bin2hex($C3);
//密码加密
//$sm2 = new RtSm2('hex', false);
//$passwd = $sm2->doEncrypt($document, $publicKey, C1C2C3);
//return "04" . $passwd; // doEncrypt做了substr($foreignPubKey, -128); 需要补一个04
}
}
}
文明上网理性发言!