qhweb / phpsm
PHP 国密(SM2,SM3,SM4)
1.0.0
2025-11-24 10:01 UTC
Requires
- php: >=7.4
- paragonie/ecc: ^2.5
Requires (Dev)
- phpunit/phpunit: ^11.2
This package is auto-updated.
Last update: 2025-11-24 10:03:15 UTC
README
php版本的国密sm2的签名算法,非对称加解密算法(非对称加密刚上线,目前测试无问题,不能保证兼容其他语言,有问题可以提issues),sm3的hash,hmac, sm4的对称加解密,要求PHP7+,打开gmp支持 目前如果服务器配套的使用的是openssl 1.1.1x, 目前到1.1.1.l(L) ,sm3,sm4都可以直接用openssl_xxx系列函数直接实现,不必大量的代码,但不支持sm2的签名
使用(how to use)
- composer require qhweb/phpsm
- please make sure you upgrade to Composer 2+
- 测试是在php 8.2.9下做的
SM2
- 签名验签算法主体基于PHPECC算法架构,添加了sm2的椭圆参数,
- 参考了 https://github.com/ToAnyWhere/phpsm2 童鞋的sm2验签算法,密钥生成算法
- 添加了签名算法, 支持sm2的16进制,base64公私钥的签名,验签算法
- 支持从文件中读取pem文件的签名,验签算法
- 添加了sm2的非对称加密的算法,但速度一般,有待优化,不能保证兼容所有语言进行加解密,目前测试了js, python的相互加解密
- sm2的加密解密算法在openssl 1.1.1的版本下自带的函数中暂无sm2的公钥私钥的加密函数,得自己实现,建议使用C,C++的算法,打包成PHP扩展的方式
- 由于 openssl没有实现sm2withsm3算法,用系统函数无法实现签名及证书的自签名分发
SM3
- 该算法直接使用 https://github.com/ToAnyWhere/phpsm2 中sm2签名用到的匹配sm3, 未做修改
- 也可使用 openssl的函数, 详见openssl_tsm3.php
SM4
- 该算法直接封装使用 https://github.com/lizhichao/sm 的sm4算法, 同时该项目支持 sm3,sm4 ,可以composer安装
- 由于sm4-ecb, sm4-cbc加密需要补齐,项目lizhichao/sm项目未做补齐操作,这里封装的时候,针对这两个算法做了补齐操作, 其他如sm4-ctr,sm4-cfb,sm4-ofb等,可以直接用
- 在openssl 1.1.1下可使用系统的函数,已支持sm4-cbc,sm4-cfb,sm4-ctr,sm4-ecb,sm4-ofb, 详见openssl_tsm4.php
总结
- 这里封装的测试函数已与相关的js, python, java都可以互签互认
- js: https://github.com/JuneAndGreen/sm-crypto
- python: https://github.com/duanhongyi/gmssl
- java: https://github.com/ZZMarquis/gmhelper
- openssl: 升到1.1.1以后,支持sm3,sm4的加解密,还不支持sm2的公私钥加解密,也不支持sm2的签名,得使用原生代码实现,签名中需要实现sm2withsm3, openssl1.1.1只实现了sm2whithsha256
测试实例代码
<?php
require '../vendor/autoload.php';
use qhwwb\phpsm\sm\SM2;
use qhwwb\phpsm\sm\SM3;
use qhwwb\phpsm\sm\SM4;
// 使用示例
try {
$key = '1234567890abcdef'; // 16 字节
$iv = 'abcdefghijklmnop'; // 16 字节
$sm2 = new SM2();
echo "=== SM3 ===\n\n";
echo '普通加密:'.SM3::hash('admin123'). "\n";
echo '填充加密:'.SM3::hmac('admin123','12345'). "\n";
// 1. 生成密钥对
echo "=== SM2密钥生成 ===\n\n";
// [$privateKey, $publicKey] = $sm2->generatekey();
$privateKey = '2bc313584c9aece21b1c3cf7b8b7fa5561612d82c2b823067452ff396edcaec9';
$publicKey = '0423652d9339b008e8861eec73633ea0c50726d5f478f6db9699cbe3f1a9052f4bfbc345600b53dfd6bac616cb9909e89bdfd094163669d7ed191b51626baa26a0';
echo "私钥:\n" . $privateKey . "\n";
echo "公钥:\n" . $publicKey . "\n";
// 2. 测试数据
$testData = "hello world!";
echo "原始数据: " . $testData . "\n";
// 3. 加密解密测试
echo "=== SM2加密解密测试 ===\n\n";
$encryptResult = $sm2->doEncrypt($testData, $publicKey);
echo "加密结果: " . $encryptResult. "\n";
$decryptResult = $sm2->doDecrypt($encryptResult,$privateKey);
echo "解密结果: " . $decryptResult . "\n";
// 4. 数字签名测试
echo "=== SM2数字签名测试 ===\n\n";
$signResult = $sm2->doSign($testData,$privateKey);
echo "签名结果: " . $signResult . "\n";
// 5. 签名验证测试
$verifyResult = $sm2->verifySign($testData, $signResult,$publicKey);
echo "签名验证: " . ($verifyResult ? "成功" : "失败") . "\n";
echo "=== SM4测试 ===\n\n";
$sm4 = new SM4($key);
$types = ['cbc','ecb','ctr','ofb','cfb'];
$data = json_encode(["a"=>1,"b"=>2]);
foreach ($types as $type) {
$encryptData = $sm4->encrypt($data,'sm4-'.$type,$iv);
$decryptData = $sm4->decrypt($encryptData,'sm4-'.$type,$iv);
echo "=== [$type] ===\n";
echo $type."加密结果: " . $encryptData . "\n";
echo $type."解密结果: " . $decryptData . "\n";
}
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
快捷函数
- SM2随机密钥(hex): createSm2Key()
- SM2随机密钥(PEM): createSm2KeyPem()
- SM2公钥加密: sm2Encrypt($str,$privateKey)
- SM2私钥解密: sm2Decrypt($str,$privateKey)
- SM2签名: sm2Sgin($str,$privateKey, $userId = null)
- SM2验签: sm2VerifySign($str,$sign, $publicKey, $userId = null)
- SM2签名: sm2SignOut($str, $privateKeyFile, $userId = null) 私钥文件签名
- SM2验签: sm2VerifySignOut($str, $sgin, $publickeyFile, $userId = null) 公钥文件验签
- SM3加密: sm3($str,$key='') $key为加密盐
- SM4加密: sm4Encrypt($str, $type = 'sm4', $iv = '', $key='1234567890abcdef',$formatOut = 'hex') $type加密模式'sm4-*'(cbc,ecb,ctr,ofb,cfb)
- SM4解密: sm4Decrypt(string $str, $type = 'sm4', $iv = '', $key='1234567890abcdef',$formatInput = 'hex') $type加密模式'sm4-*'(cbc,ecb,ctr,ofb,cfb)