first commit

This commit is contained in:
2025-11-28 10:08:12 +08:00
commit 09a14bf0a3
1088 changed files with 145132 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
vendor2/
vendor/
composer.lock
+71
View File
@@ -0,0 +1,71 @@
# php sm2 sm3 sm4 国密算法整理
* 本项目支持php版本的国密sm2的签名算法,非对称加解密算法,sm3的hash, sm4的对称加解密,要求PHP7,打开gmp支持
* 目前如果服务器配套的使用的是openssl 1.1.1x 目前到1.1.1.l(w) sm3sm4都可以直接用openssl_xxx系列函数直接实现,不必大量的代码,不支持sm2的签名,sm2的加解密
### 使用
* composer require lpilp/guomi
* please make sure you upgrade to Composer 2+
* PHP >=7.2,打开gmp组件支持
* 如需要使用php5.6 请使用wzhih童鞋fork修改的 https://github.com/wzhih/guomi ; composer require wzhih/guomi 或是使用该项目的简化版本 https://github.com/lpilp/simplesm2
### SM2
* 签名验签算法主体基于PHPECC算法架构,添加了sm2的椭圆参数,
* 参考了 https://github.com/ToAnyWhere/phpsm2 童鞋的sm2验签算法,密钥生成算法
* 添加了签名算法, 支持sm2的16进制,base64公私钥的签名,验签算法
* 支持从文件中读取pem文件的签名,验签算法
* 由于 openssl没有实现sm2withsm3算法,用系统函数无法实现签名及证书的自签名分发
### SM2非对称加密
* 添加了sm2的非对称加密的算法,但速度一般,有待优化,不能保证兼容所有语言进行加解密,目前测试了js, python的相互加解密
* sm2的加密解密算法在openssl 1.1.1的版本下自带的函数中暂无sm2的公钥私钥的加密函数,得自己实现,建议使用C,C++的算法,打包成PHP扩展的方式
* SM2的非对称加密缺省的是c1c3c2, 请使用的时候注意下,对方返回的是c1c3c2还是c1c2c3,进行相应的修改更新,还有一点就是本项目中c1前面没有04, 视对接方的需求,看是否添加\x04, v1.0.6版已对c1c3c2还是c1c2c3做了兼容,缺省是c1c3c2,添加相应的modetype后可以兼容两种模式,使用方法见 test/tsm2_encrypt.php
* 如对方sm2非对称加密生成的不是c1c3c2 而是 asn1(c1x,c1y,c3,c2), 目前本项目不支持这种样式的,请先asn1解开后,拼接成 C1C3C2的形式后再调用解密函数,否则会报椭圆不匹配错误, 请自行处理
### 关于数据格式
* sm2的缺省返回是asn1(r,s)的base64字符串
* sm2的非对称加密返回的是 c1c3c2的hex字符串
* sm3缺省hex的字符串
* sm4缺省也是hex的字符串
* 在于其他语言互通的时候请自行统一格式,以免因为格式的问题而造成运算不成功
### SM3
* 该算法直接使用 https://github.com/ToAnyWhere/phpsm2 中sm2签名用到的匹配sm3, 未做修改
* 也可使用 openssl的函数, 详见openssl_tsm3.php
* hmac-sm3,这个算法与hmac-sha256在hmac的算法是一样的,只是hash的算法不一样,一个是sm3,一个sha256, 没有什么特殊的注意的地方
### SM4
* 该算法直接封装使用 https://github.com/lizhichao/sm 的sm4算法, 同时该项目支持 sm3,sm4 ,可以composer安装
* 由于sm4-ecb sm4-cbc加密需要补齐,项目lizhichao/sm项目未做补齐操作,这里封装的时候,针对这两个算法做了补齐操作, 其他如sm4-ctr,sm4-cfbsm4-ofb等,可以直接用
* 在openssl 1.1.1下可使用系统的函数,已支持sm4-cbcsm4-cfbsm4-ctr,sm4-ecbsm4-ofb 详见openssl_tsm4.php ,有一点很诡异,用yum/dnf安装的openssl只支持sm3, 如果是自己编译安装的就支持sm3,sm4
### SM2各语言总结
* 这里封装的测试函数已与相关的js,python,java,go等都可以互签互认
* js: https://github.com/JuneAndGreen/sm-crypto 一个注意点就是: js的中文字符转成byte[]时,缺省的是unicode编码两字节,需要转成utf8的三字节编码,一个简单的方案 unescape(encodeURIComponent(str)) 然后再一个字节一个字节读就行了
* python: https://github.com/duanhongyi/gmssl 使用 pip install gmssl 安装就可
* java: https://github.com/ZZMarquis/gmhelper 注意下java中文的转码问题,getBytes("UTF-8") 要加上编码类型, 因为 getBytes()函数的缺省编码是随操作系统的,如果是在中文版的windows中使用,缺省是GBK编码,就会出现中文的编码的问题,而造成签名无法通过
* openssl: 升到1.1.1以后,支持sm3,sm4的加解密,还不支持sm2的公私钥加解密,也不支持sm2的签名
+ go: https://github.com/tjfoc/gmsm 一家做区块链的公司开源的项目,在go方面可以说是最早开源的了,https://github.com/deatil/go-cryptobin 这个是个go的宝藏项目,有各种的加解密,签名
+ C#: 项目也比较少,基本是基于https://www.bouncycastle.org/ 的BC加密库(java也是基于该库),该库1.8.4后版本支持sm2,sm3,sm4,考察搜索到的几个项目,https://github.com/hz281529512/SecretTest 完整性算比较好
+ C: https://github.com/guanzhi/GmSSL 北大计算机的开源项目,fork多,star也多。
+ php-openssl: php7 好像支持了sm3, 在openssl1.1.1以上,可用编译的方式加入sm3,sm4的支持。 xampp套件下的php7以上的版本支持sm3, sm4的openssl_系列函数, openssl_get_md_methods() 查看是否支持sm3, openssl_get_cipher_methods() 查看是否支持sm4
### SM2签名常见问题
* 提供的私钥是base64的短串,一般直接 bin2hex(base64_decode(str)) 就是明文的密钥了
* 文件格式的密钥一般有pkcs1与pkcs8两个格式,本项目只支持pkcs1格式的密钥,使用前请先进行相关的转换,一般 pkcs8是四行,pkcs1是三行,区别见 https://www.jianshu.com/p/a428e183e72e
* 关于签名的字符串的问题,有些项目会将原始字符串哈稀后,再对哈稀值进行签名,有些对这哈稀值又进行了hex2bin操作后再签名,请双方按约定的标准确定最后签名的数据值,双方保持一致即可
* 签名的结果是asn1(r,s),个别的项目签名出来的只是 r+s的字符串组合,验证签名的时候注意下。 base64的签名如果以MEU开头的,这个是asn1的,解开后是64字节是r + s 的 在src/util/SmSignFormatRS.php 有相关的转换函数,请按需使用
### 特别注意
* sm2的构造函数中缺省是固定了中间椭圆,目前发现个别的接入方(目前发现是招行金融平台)将这个中间椭圆私钥随机算法给加黑了, 请使用的时候 $randFixed 设为false 以及重新生成一个中间椭圆的密钥对替换原有程序的数据
```
function __construct($formatSign='hex', $randFixed = true) {
// 注意: 这个randFixed尽量取false, 如需要固定,请重新生成$foreignkey密码对
$this->adapter = RtEccFactory::getAdapter();
$this->generator = RtEccFactory::getSmCurves()->generatorSm2();
if(in_array($formatSign,$this->arrFormat)){
$this->formatSign = $formatSign;
} else {
$this->formatSign = 'hex';
}
if(!$randFixed){
$this->useDerandomizedSignatures = false;
$this->useDerandomizedEncrypt = false;
}
}
```
+32
View File
@@ -0,0 +1,32 @@
{
"name": "lpilp/guomi",
"description": "国密sm2",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "recent",
"email": "lpilp@126.com",
"wx": "xiaopz129"
}
],
"minimum-stability": "stable",
"require": {
"php": ">=7.2",
"mdanter/ecc": "^1.0"
},
"autoload": {
"psr-4": {
"Rtgm\\": "src"
},
"files": ["src/overwrite.php"]
},
"repositories": {
"packagist": {
"type": "composer",
"url": "https://mirrors.aliyun.com/composer/"
}
}
}
@@ -0,0 +1,125 @@
<?php
/**
* 覆盖ecc里的椭圆类,添加sm2
*/
declare(strict_types=1);
namespace Mdanter\Ecc\Curves;
// use Mdanter\Ecc\Exception\UnknownCurveException;
use Mdanter\Ecc\Exception\UnsupportedCurveException;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Math\MathAdapterFactory;
use Mdanter\Ecc\Primitives\GeneratorPoint;
use Rtgm\ecc\Sm2Curve;
class CurveFactory
{
/**
* @param string $name
* @return NamedCurveFp
*/
public static function getCurveByName(string $name): NamedCurveFp
{
$adapter = MathAdapterFactory::getAdapter();
if($name == Sm2Curve::NAME_PSM2){
return self::getSm2Factory($adapter)->curveSm2();
}
$nistFactory = self::getNistFactory($adapter);
$secpFactory = self::getSecpFactory($adapter);
switch ($name) {
case NistCurve::NAME_P192:
return $nistFactory->curve192();
case NistCurve::NAME_P224:
return $nistFactory->curve224();
case NistCurve::NAME_P256:
return $nistFactory->curve256();
case NistCurve::NAME_P384:
return $nistFactory->curve384();
case NistCurve::NAME_P521:
return $nistFactory->curve521();
case SecgCurve::NAME_SECP_112R1:
return $secpFactory->curve112r1();
case SecgCurve::NAME_SECP_192K1:
return $secpFactory->curve192k1();
case SecgCurve::NAME_SECP_256K1:
return $secpFactory->curve256k1();
case SecgCurve::NAME_SECP_256R1:
return $secpFactory->curve256r1();
case SecgCurve::NAME_SECP_384R1:
return $secpFactory->curve384r1();
default:
$error = new UnsupportedCurveException('Unknown curve.');
$error->setCurveName($name);
throw $error;
}
}
/**
* @param string $name
* @return GeneratorPoint
*/
public static function getGeneratorByName(string $name): GeneratorPoint
{
$adapter = MathAdapterFactory::getAdapter();
if($name == Sm2Curve::NAME_PSM2){
return self::getSm2Factory($adapter)->generatorSm2();
}
$nistFactory = self::getNistFactory($adapter);
$secpFactory = self::getSecpFactory($adapter);
switch ($name) {
case NistCurve::NAME_P192:
return $nistFactory->generator192();
case NistCurve::NAME_P224:
return $nistFactory->generator224();
case NistCurve::NAME_P256:
return $nistFactory->generator256();
case NistCurve::NAME_P384:
return $nistFactory->generator384();
case NistCurve::NAME_P521:
return $nistFactory->generator521();
case SecgCurve::NAME_SECP_112R1:
return $secpFactory->generator112r1();
case SecgCurve::NAME_SECP_192K1:
return $secpFactory->generator192k1();
case SecgCurve::NAME_SECP_256K1:
return $secpFactory->generator256k1();
case SecgCurve::NAME_SECP_256R1:
return $secpFactory->generator256r1();
case SecgCurve::NAME_SECP_384R1:
return $secpFactory->generator384r1();
default:
$error = new UnsupportedCurveException('Unknown generator.');
$error->setCurveName($name);
throw $error;
}
}
/**
* @param GmpMathInterface $math
* @return NistCurve
*/
private static function getNistFactory(GmpMathInterface $math): NistCurve
{
return new NistCurve($math);
}
/**
* @param GmpMathInterface $math
* @return SecgCurve
*/
private static function getSecpFactory(GmpMathInterface $math): SecgCurve
{
return new SecgCurve($math);
}
/**
* @param GmpMathInterface $math
* @return Sm2
*/
private static function getSm2Factory(GmpMathInterface $math): Sm2Curve
{
return new Sm2Curve($math);
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace Rtgm\ecc;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Math\MathAdapterFactory;
use Mdanter\Ecc\EccFactory;
// use Rtgm\sm\ecc\NistCurve;
use Rtgm\ecc\Sm2Curve;
/**
* 添加sm2的工厂,采用类的继承方式,这里也可以采用如 \Curves\CurveFctory的覆盖的方式
*/
class RtEccFactory extends EccFactory{
/**
* Selects and creates the most appropriate adapter for the running environment.
*
* @param bool $debug [optional] Set to true to get a trace of all mathematical operations
*
* @throws \RuntimeException
* @return GmpMathInterface
*/
public static function getAdapter(bool $debug = false): GmpMathInterface
{
$adapter = MathAdapterFactory::getAdapter($debug);
return $adapter;
}
/**
* Returns a factory to create NIST Recommended curves and generators.
*
* @param GmpMathInterface $adapter [optional] Defaults to the return value of EccFactory::getAdapter().
* @return Sm2Curve
*/
public static function getSmCurves(GmpMathInterface $adapter = null): Sm2Curve
{
$adapter = $adapter ?: self::getAdapter();
// var_dump($adapter);
return new Sm2Curve($adapter);
}
}
@@ -0,0 +1,148 @@
<?php
/**
* 覆盖ecc里的oid与椭圆的对照数组,添加sm2
*/
declare(strict_types=1);
namespace Mdanter\Ecc\Serializer\Util;
use FG\ASN1\Universal\ObjectIdentifier;
use Mdanter\Ecc\Curves\NamedCurveFp;
use Mdanter\Ecc\Curves\CurveFactory;
use Mdanter\Ecc\Curves\NistCurve;
use Mdanter\Ecc\Curves\SecgCurve;
use Rtgm\ecc\Sm2Curve;
use Mdanter\Ecc\Exception\UnsupportedCurveException;
use Mdanter\Ecc\Primitives\CurveFpInterface;
use Mdanter\Ecc\Primitives\GeneratorPoint;
class CurveOidMapper
{
const NIST_P192_OID = '1.2.840.10045.3.1.1';
const NIST_P224_OID = '1.3.132.0.33';
const NIST_P256_OID = '1.2.840.10045.3.1.7';
const NIST_P384_OID = '1.3.132.0.34';
const NIST_P521_OID = '1.3.132.0.35';
const SECP_112R1_OID = '1.3.132.0.6';
const SECP_192K1_OID = '1.3.132.0.31';
const SECP_256K1_OID = '1.3.132.0.10';
const SECP_256R1_OID = '1.2.840.10045.3.1.7';
const SECP_384R1_OID = '1.3.132.0.34';
const SM2_OID = '1.2.156.10197.1.301';
/**
* @var array
*/
private static $oidMap = array(
NistCurve::NAME_P192 => self::NIST_P192_OID,
NistCurve::NAME_P224 => self::NIST_P224_OID,
NistCurve::NAME_P256 => self::NIST_P256_OID,
NistCurve::NAME_P384 => self::NIST_P384_OID,
NistCurve::NAME_P521 => self::NIST_P521_OID,
SecgCurve::NAME_SECP_112R1 => self::SECP_112R1_OID,
SecgCurve::NAME_SECP_192K1 => self::SECP_192K1_OID,
SecgCurve::NAME_SECP_256K1 => self::SECP_256K1_OID,
SecgCurve::NAME_SECP_256R1 => self::SECP_256R1_OID,
SecgCurve::NAME_SECP_384R1 => self::SECP_384R1_OID,
Sm2Curve::NAME_PSM2 => self::SM2_OID,
);
/**
* @var array
*/
private static $sizeMap = array(
NistCurve::NAME_P192 => 24,
NistCurve::NAME_P224 => 28,
NistCurve::NAME_P256 => 32,
NistCurve::NAME_P384 => 48,
NistCurve::NAME_P521 => 66,
SecgCurve::NAME_SECP_112R1 => 14,
SecgCurve::NAME_SECP_192K1 => 24,
SecgCurve::NAME_SECP_256K1 => 32,
SecgCurve::NAME_SECP_256R1 => 32,
SecgCurve::NAME_SECP_384R1 => 48,
Sm2Curve::NAME_PSM2 =>32, //sm2 也是256位的,32个字节的密码
);
/**
* @return array
*/
public static function getNames(): array
{
return array_keys(self::$oidMap);
}
/**
* @param CurveFpInterface $curve
* @return int
*/
public static function getByteSize(CurveFpInterface $curve): int
{
if ($curve instanceof NamedCurveFp && array_key_exists($curve->getName(), self::$sizeMap)) {
return self::$sizeMap[$curve->getName()];
}
throw new UnsupportedCurveException('Unsupported curve type');
}
/**
* @param NamedCurveFp $curve
* @return ObjectIdentifier
*/
public static function getCurveOid(NamedCurveFp $curve): ObjectIdentifier
{
if (array_key_exists($curve->getName(), self::$oidMap)) {
$oidString = self::$oidMap[$curve->getName()];
return new ObjectIdentifier($oidString);
}
throw new UnsupportedCurveException('Unsupported curve type');
}
/**
* @param ObjectIdentifier $oid
* @return NamedCurveFp
*/
public static function getCurveFromOid(ObjectIdentifier $oid): NamedCurveFp
{
$oidString = $oid->getContent();
$invertedMap = array_flip(self::$oidMap);
if (array_key_exists($oidString, $invertedMap)) {
return CurveFactory::getCurveByName($invertedMap[$oidString]);
}
$error = new UnsupportedCurveException('Invalid data: unsupported curve.');
$error->setOid($oidString);
throw $error;
}
/**
* @param ObjectIdentifier $oid
* @return GeneratorPoint
*/
public static function getGeneratorFromOid(ObjectIdentifier $oid): GeneratorPoint
{
$oidString = $oid->getContent();
$invertedMap = array_flip(self::$oidMap);
if (array_key_exists($oidString, $invertedMap)) {
return CurveFactory::getGeneratorByName($invertedMap[$oidString]);
}
$error = new UnsupportedCurveException('Invalid data: unsupported generator.');
$error->setOid($oidString);
throw $error;
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace Rtgm\ecc;
use Mdanter\Ecc\Curves\NamedCurveFp;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Primitives\CurveParameters;
use Mdanter\Ecc\Primitives\GeneratorPoint;
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
/**
* 国密sm2椭圆
*/
class Sm2Curve
{
const NAME_PSM2 = 'SM2';
/**
* @var GmpMathInterface
*/
private $adapter;
/**
* @param GmpMathInterface $adapter
*/
public function __construct(GmpMathInterface $adapter)
{
$this->adapter = $adapter;
// echo "I am sm ecc\n";
}
/**
* Returns an sm2国密 curve.
*
* @return NamedCurveFp
*/
public function curveSm2(): NamedCurveFp
{
$p = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16);
$b = gmp_init('0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16);
$a = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16);
$parameters = new CurveParameters(256, $p, $a, $b);
return new NamedCurveFp(self::NAME_PSM2, $parameters, $this->adapter);
}
/**
* Returns an sm2 generator.
*
* @param RandomNumberGeneratorInterface $randomGenerator
* @return GeneratorPoint
*/
public function generatorSm2(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
{
$curve = $this->curveSm2();
$order = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16);
$x = gmp_init('0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7', 16);
$y = gmp_init('0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0', 16);
return $curve->getGenerator($x, $y, $order, $randomGenerator);
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
declare( strict_types = 1 );
// namespace Mdanter\Ecc\Crypto\Signature;
namespace Rtgm\ecc;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
use Mdanter\Ecc\Util\BinaryString;
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
use Mdanter\Ecc\Crypto\Signature\Signature;
/**
* sm2签名算法
*/
class Sm2Signer {
/**
*
* @var GmpMathInterface
*/
private $adapter;
/**
*
* @param GmpMathInterface $adapter
*/
public function __construct( GmpMathInterface $adapter ) {
$this->adapter = $adapter;
}
/**
* @param PrivateKeyInterface $key
* @param \GMP $truncatedHash - hash truncated for use in ECDSA hash算法然后truncated by 相关的椭圆字节
* @param \GMP $randomK
* @return SignatureInterface
*/
public function sign( PrivateKeyInterface $key, \GMP $truncatedHash, \GMP $randomK ): SignatureInterface {
$math = $this->adapter;
$generator = $key->getPoint();
// var_dump($generator);die();
$n = $generator->getOrder();
$modMath = $math->getModularArithmetic( $n );
$prikey = $key->getSecret();
//第一二步是userid, msg 生成 trucatedhash ,
$count = 0;
while (true) {
$count++;
// echo "count: $count\n";
if($count >5){
throw new \RuntimeException( 'Error: sign R or S = 0' );
}
// 第三步生成随机数
$k = $math->mod( $randomK, $n );
// 第四步 计算pt1(x1,y1) = [K]G这个点
// 生成一个新的点P = kG
$p1 = $generator->mul( $k );
// var_dump($p1);die();
// 第五步 计算 r = (truncatedHash + x1) mod n
$r = $modMath->add($truncatedHash,$p1->getX());
// var_dump(gmp_strval($r,16));die();
$zero = gmp_init( 0, 10 );
if ( $math->equals( $r, $zero ) ) {
// @todo 如报错,重来
// continue; //报错重来一次
// @todo
throw new \RuntimeException( 'Error: random number R = 0' );
}
// 第六步 计算 s = ((1 + d)^-1 * (k - rd)) mod n
$one = gmp_init(1,10);
$s1 = $math->inverseMod($math->add($one, $prikey),$n );
// print_r(gmp_strval($s1,16));die();
$s2 = $math->sub($k,$math->mul($r,$prikey));
// print_r(gmp_strval($s2,16));die();
$s = $modMath->mul($s1,$s2);
// var_dump($generator->mul($s));die();
if ( $math->equals( $s, $zero ) ) {
// continue;
throw new \RuntimeException( 'Error: random number S = 0' );
}
return new Signature( $r, $s );
}
}
/**
* @param PublicKeyInterface $key
* @param SignatureInterface $signature
* @param \GMP $hash
* @return bool
*/
public function verify( PublicKeyInterface $key, SignatureInterface $signature, \GMP $hash ): bool {
$generator = $key->getGenerator();
// var_dump($generator);die();
$n = $generator->getOrder();
$r = $signature->getR();
$s = $signature->getS();
$math = $this->adapter;
$one = gmp_init( 1, 10 );
if ( $math->cmp( $r, $one ) < 0 || $math->cmp( $r, $math->sub( $n, $one ) ) > 0 ) {
return false;
}
if ( $math->cmp( $s, $one ) < 0 || $math->cmp( $s, $math->sub( $n, $one ) ) > 0 ) {
return false;
}
// 1.2.3.4 sm3 取msg,userid的 hash值,这里直接就传过来了,
$modMath = $math->getModularArithmetic( $n );
// 第五步 计算t=(r'+s')mod n
$t = $modMath->add($r,$s);
// // 第六步 计算(x1,y1) = [s]G + [t]PA
$p1 = $generator->mul($s); // p1 = sG 是OK的与签名生成的sG一样
$p2 = $key->getPoint()->mul($t);
$xy = $p1->add($p2);
// // 第七步 R=(e' + x1') 验证R==r'?
$v = $modMath->add($hash, $xy->getX());
return BinaryString::constantTimeCompare( $math->toString( $v ), $math->toString( $r ) );
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
spl_autoload_register(function ($cls) {
$map = array(
'Mdanter\Ecc\Serializer\Util\CurveOidMapper' => __DIR__ .'/ecc/Serializer/Util/CurveOidMapper.php',
'Mdanter\Ecc\Curves\CurveFactory'=>__DIR__ .'/ecc/Curves/CurveFactory.php',
);
if (isset($map[$cls])) {
// echo $cls . ' loaded abc '. $map[$cls]. PHP_EOL;
include_once $map[$cls];
return true;
}
}, true, true);
+403
View File
@@ -0,0 +1,403 @@
<?php
namespace Rtgm\sm;
define("C1C3C2", 1);
define("C1C2C3", 0);
use Rtgm\ecc\RtEccFactory;
use Rtgm\ecc\Sm2Signer;
use Mdanter\Ecc\Crypto\Key\PrivateKey;
use Mdanter\Ecc\Crypto\Key\PublicKey;
use Mdanter\Ecc\Primitives\Point;
use Mdanter\Ecc\Serializer\PrivateKey\PemPrivateKeySerializer;
use Mdanter\Ecc\Serializer\PrivateKey\DerPrivateKeySerializer;
use Mdanter\Ecc\Serializer\PublicKey\PemPublicKeySerializer;
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
use Rtgm\smecc\SM2\Sm2WithSm3;
use Rtgm\smecc\SM2\Hex2ByteBuf;
class RtSm2
{
protected $adapter = null;
protected $generator = null;
protected $userId = '1234567812345678';
// 是否固定签名不随机,好处是同一段参数的签名固定,增大别人的猜测的难度,
// 同样的key + document每次签名是一样的,如果为false则每次不一样
protected $useDerandomizedSignatures = true;
// 是否固定加密不随机,算法中的是否每次都用不同的中间椭圆,如果固定的话,
// 同样的文本加密后的数据是一样的,但速度会更快一些,随机的话,每次加密出来的数据不一样
protected $useDerandomizedEncrypt = true;
// 输入输出的签名方式 16进制的还是base64
protected $formatSign = 'hex';
// 可扩展自定义多种返回签名方式
protected $arrFormat = ['hex', 'base64'];
// 加密时的中间椭圆,取任意的sm2中间椭圆都可以,useDerandomizedEncrypt = true时使用,为false时每次加密,重新生成一个
protected $foreignKey = [
'21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225',
'04e27c3780e7069bda7082a23a489d77587ce309583ed99253f66e1d9833ed1a1d0b5ce86dc6714e9974cf258589139d7b1855e8c9fa2f2c1175ee123a95a23e9b'
];
protected $cipher = null;
/**
* Undocumented function
*
* @param string $formatSign
* @param boolean $randFixed 是否使用中间椭圆,使用中间椭圆的话,速度会快一些,但同样的数据的签名或加密的值就固定了
*/
function __construct($formatSign = 'hex', $randFixed = true)
{
$this->adapter = RtEccFactory::getAdapter();
$this->generator = RtEccFactory::getSmCurves()->generatorSm2();
if (in_array($formatSign, $this->arrFormat)) {
$this->formatSign = $formatSign;
} else {
$this->formatSign = 'hex';
}
if (!$randFixed) {
$this->useDerandomizedSignatures = false;
$this->useDerandomizedEncrypt = false;
}
}
/**
* 随机生成一对16进制明文公私钥
*/
public function generatekey()
{
// $adapter = $this->adapter;
$generator = $this->generator;
//随机生成一个私钥类
$private = $generator->createPrivateKey();
//取出私钥16进制表示出来
$privateKey = $this->decHex($private->getSecret());
//取出公钥的椭圆点
$pubPoint = $private->getPublicKey()->getPoint();
//公钥上的点x, y
$pubX = $this->decHex($pubPoint->getX());
$pubY = $this->decHex($pubPoint->getY());
$publicKey = '04' . $pubX . $pubY;
return [$privateKey, $publicKey];
}
/**
* 随机生成一对PEM编码公私钥
*/
public function generatePemkey()
{
$adapter = $this->adapter;
$generator = $this->generator;
$private = $generator->createPrivateKey();
$derSerializer = new DerPrivateKeySerializer($adapter);
// der包 ans1编码 1 版本号 2私钥 3 oid 4 公钥 四组数据
// $der = $derSerializer->serialize( $private );
$pemSerializer = new PemPrivateKeySerializer($derSerializer);
$privateKeyPem = $pemSerializer->serialize($private);
$derPubSerializer = new DerPublicKeySerializer($adapter);
$pemPubSerializer = new PemPublicKeySerializer($derPubSerializer);
$publicKeyPem = $pemPubSerializer->serialize($private->getPublicKey());
return [$privateKeyPem, $publicKeyPem];
}
/**
* SM2 公钥加密算法
*
* @param string $document
* @param string $publicKey 如提供的base64的,可使用 bin2hex(base64_decode($publicKey))
* @return string
*/
public function doEncrypt($document, $publicKey, $model = C1C3C2)
{
$adapter = $this->adapter;
$generator = $this->generator;
$this->cipher = new \Rtgm\smecc\SM2\Cipher();
$arrMsg = Hex2ByteBuf::HexStringToByteArray2(bin2hex($document));
list($pubKeyX, $pubKeyY) = $this->_getKeyXY($publicKey);
// $key = $this->_getPubKeyObject( $pubKeyX, $pubKeyY );
$point = new Point($adapter, $generator->getCurve(), gmp_init($pubKeyX, 16), gmp_init($pubKeyY, 16));
// 是否使用固定的中间椭圆加密,
if ($this->useDerandomizedEncrypt) {
$c1 = $this->cipher->initEncipher($point, $this->foreignKey);
} else {
$c1 = $this->cipher->initEncipher($point, null);
}
// print_r($c1);
$arrMsg = $this->cipher->encryptBlock($arrMsg);
$c2 = strtolower(Hex2ByteBuf::ByteArrayToHexString($arrMsg));
// print_R($c2);echo "\n";
$c3 = strtolower(Hex2ByteBuf::ByteArrayToHexString($this->cipher->Dofinal()));
// print_r($c1.$c3.$c2);
if ($model == C1C3C2) {
return $c1 . $c3 . $c2;
} else {
return $c1 . $c2 . $c3;
}
}
/**
* SM2 私钥解密算法,
*
* @param string $document
* @param string $privateKey 如提供的base64的,可使用 bin2hex(base64_decode($privateKey))
* @param bool $trim 是否做04开头的去除,看业务返回
* @return string
*/
public function doDecrypt($encryptData, $privateKey, $trim = true, $model = C1C3C2)
{
// $encryptData = $c1.$c3.$c2
if (substr($encryptData, 0, 2) == '04' && $trim) {
$encryptData = substr($encryptData, 2);
}
if (strlen($privateKey) == 66 && substr($privateKey, 0, 2) == '00') {
$privateKey = substr($privateKey, 2); // 个别的key 前面带着00
}
$adapter = $this->adapter;
$generator = $this->generator;
$this->cipher = new \Rtgm\smecc\SM2\Cipher();
$c1X = substr($encryptData, 0, 64);
$c1Y = substr($encryptData, strlen($c1X), 64);
$c1Length = strlen($c1X) + strlen($c1Y);
if ($model == C1C3C2) {
$c3 = substr($encryptData, $c1Length, 64);
$c2 = substr($encryptData, $c1Length + strlen($c3));
} else {
$c3 = substr($encryptData, -64);
$c2 = substr($encryptData, $c1Length, strlen($encryptData) - $c1Length - 64);
}
$p1 = new Point($adapter, $generator->getCurve(), gmp_init($c1X, 16), gmp_init($c1Y, 16));
$this->cipher->initDecipher($p1, $privateKey);
$arrMsg = Hex2ByteBuf::HexStringToByteArray2($c2);
$arrMsg = $this->cipher->decryptBlock($arrMsg);
$document = hex2bin(Hex2ByteBuf::ByteArrayToHexString($arrMsg));
$c3_ = strtolower(Hex2ByteBuf::ByteArrayToHexString($this->cipher->Dofinal()));
$c3 = strtolower($c3);
if ($c3 == $c3_) { //hash签名相同,
return $document;
} else {
return '';
}
}
/**
* SM2 签名明文16进制密码, 如提供的base64的,可使用 bin2hex(base64_decode($privateKey))
*
*/
public function doSign($document, $privateKey, $userId = null)
{
if (empty($userId)) {
$userId = $this->userId;
}
$adapter = $this->adapter;
$generator = $this->generator;
$algorithm = 'sha256';
$secret = gmp_init($privateKey, 16);
$key = new PrivateKey($adapter, $generator, $secret);
return $this->_dosign($document, $key, $adapter, $generator, $userId, $algorithm);
}
/**
* SM2 签名pem密码
*
*/
public function doSignOutKey($document, $privateKeyFile, $userId = null)
{
if (empty($userId)) {
$userId = $this->userId;
}
if (!file_exists($privateKeyFile)) {
throw new \Exception('privatekey file not exists');
}
$adapter = $this->adapter;
$generator = $this->generator;
//这个sha256 只是生成随机数时用到,和主体算法无关
$algorithm = 'sha256';
$pemSerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer($adapter));
$keyData = file_get_contents($privateKeyFile);
$key = $pemSerializer->parse($keyData);
return $this->_dosign($document, $key, $adapter, $generator, $userId, $algorithm);
}
protected function _dosign($document, $key, $adapter, $generator, $userId, $algorithm = 'sha256')
{
// $publickey = $key->getPublicKey();
$obPoint = $key->getPublicKey()->getPoint();
$pubKeyX = $adapter->decHex($obPoint->getX());
$pubKeyY = $adapter->decHex($obPoint->getY());
$hash = $this->_doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId);
# Derandomized signatures are not necessary, but is avoids
# the risk of a low entropy RNG, causing accidental reuse
# of a k value for a different message, which leaks the
# private key.
if ($this->useDerandomizedSignatures) {
$random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getHmacRandomGenerator($key, $hash, $algorithm);
} else {
$random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getRandomGenerator();
}
$randomK = $random->generate($generator->getOrder());
$signer = new Sm2Signer($adapter);
$signature = $signer->sign($key, $hash, $randomK);
$serializer = new DerSignatureSerializer();
$serializedSig = $serializer->serialize($signature);
if ($this->formatSign == 'hex') {
return bin2hex($serializedSig);
} else if ($this->formatSign == 'base64') {
return base64_encode($serializedSig) . PHP_EOL;
}
//缺省 hex
return bin2hex($serializedSig);
}
public function verifySign($document, $sign, $publicKey, $userId = null)
{
$adapter = $this->adapter;
$generator = $this->generator;
if (empty($userId)) {
$userId = $this->userId;
}
if ($this->formatSign == 'hex') {
$sigData = hex2bin($sign);
} else if ($this->formatSign == 'base64') {
$sigData = base64_decode($sign);
} else {
$sigData = hex2bin($sign);
}
// Parse signature
$sigSerializer = new DerSignatureSerializer();
$sig = $sigSerializer->parse($sigData);
// get hash
list($pubKeyX, $pubKeyY) = $this->_getKeyXY($publicKey);
$hash = $this->_doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId);
// get pubkey parse
$key = $this->_getPubKeyObject($pubKeyX, $pubKeyY);
$signer = new Sm2Signer($adapter);
return $signer->verify($key, $sig, $hash);
}
public function verifySignOutKey($document, $sign, $publickeyFile, $userId = null)
{
if (empty($userId)) {
$userId = $this->userId;
}
if (!file_exists($publickeyFile)) {
throw new \Exception('publickey file not exists');
}
$adapter = $this->adapter;
$generator = $this->generator;
if ($this->formatSign == 'hex') {
$sigData = hex2bin($sign);
} else if ($this->formatSign == 'base64') {
$sigData = base64_decode($sign);
} else {
$sigData = hex2bin($sign);
}
// Parse signature
$sigSerializer = new DerSignatureSerializer();
$sig = $sigSerializer->parse($sigData);
// Parse public key
$keyData = file_get_contents($publickeyFile);
$derSerializer = new DerPublicKeySerializer($adapter);
$pemSerializer = new PemPublicKeySerializer($derSerializer);
$key = $pemSerializer->parse($keyData);
$pubKeyX = $this->decHex($key->getPoint()->getX());
$pubKeyY = $this->decHex($key->getPoint()->getY());
$hash = $this->_doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId);
$signer = new Sm2Signer($adapter);
return $signer->verify($key, $sig, $hash);
}
/**
*
*/
protected function _doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId)
{
$hasher = new Sm2WithSm3();
$hash = $hasher->getSm3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId);
return gmp_init(Hex2ByteBuf::ByteArrayToHexString($hash), 16);
}
protected function _getKeyXY($publicKey)
{
if (strlen($publicKey) == 128) {
$pubKeyX = substr($publicKey, 0, 64);
$pubKeyY = substr($publicKey, -64);
} else if (strlen($publicKey) == 130 && substr($publicKey, 0, 2) == '04') {
$pubKeyX = substr($publicKey, 2, 64);
$pubKeyY = substr($publicKey, -64);
} else {
throw new \Exception('publickey format error');
}
return [$pubKeyX, $pubKeyY];
}
protected function _getPubKeyObject($pubKeyX, $pubKeyY)
{
$generator = $this->generator;
// __construct( GmpMathInterface $adapter, CurveFpInterface $curve, \GMP $x, \GMP $y, \GMP $order = null, bool $infinity = false )
$x = gmp_init($pubKeyX, 16);
$y = gmp_init($pubKeyY, 16);
$point = new Point($this->adapter, $generator->getCurve(), $x, $y);
// __construct( GmpMathInterface $adapter, GeneratorPoint $generator, PointInterface $point )
return new PublicKey($this->adapter, $this->generator, $point);
}
protected function _str2hex($str)
{
$res = array();
for ($i = 0; $i < strlen($str); $i++) {
$res[$i] = sprintf("%02x", ord($str[$i]));
}
return implode("", $res);
}
private function decHex($dec, $len = 64): string
{
if (gettype($dec) == 'string') {
$dec = gmp_init($dec, 10);
}
if (gmp_cmp($dec, 0) < 0) {
throw new \InvalidArgumentException('Unable to convert negative integer to string');
}
$hex = gmp_strval($dec, 16);
/* if (strlen($hex) % 2 != 0) {
$hex = '0'.$hex;
} */
$left = $len - strlen($hex);
if ($left > 0) {
$hex = str_repeat('0', $left) . $hex;
}
return $hex;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Rtgm\sm;
use Rtgm\smecc\SM3\SM3Digest;
class RtSm3 {
public function digest($msg,$format=1){
$md = array();
$sm3 = new SM3Digest();
$msgArray = unpack("C*",$msg);
$sm3->BlockUpdate($msgArray, 1, sizeof($msgArray));
$sm3->DoFinal($md, 0);
if($format){
return $this->_dec2hex($md);
} else {
return $md;
}
}
protected function _dec2hex($md){
$res = array();
for($i=0; $i<count($md);$i++){
$res[$i] = sprintf("%02x",$md[$i]);
}
return implode("",$res);
}
}
+113
View File
@@ -0,0 +1,113 @@
<?php
namespace Rtgm\sm;
/**
* Sm4 from https://github.com/lizhichao/sm
*/
use Exception;
use Rtgm\smecc\SM4\Sm4;
class RtSm4
{
protected $sm4;
protected $keyLen = 16;
protected $ivLen = 16;
function __construct($key)
{
$this->sm4 = new Sm4($key);
}
public function encrypt($data, $type = 'sm4', $iv = '', $formatOut = 'hex')
{
if ($type != 'sm4-ecb') {
$this->check_iv($iv);
}
$ret = '';
switch ($type) {
case 'sm4':
case 'sm4-cbc':
$data = $this->mystr_pad($data, $this->keyLen); //需要补齐
$ret = $this->sm4->enDataCbc($data, $iv);
break;
case 'sm4-ecb':
$data = $this->mystr_pad($data, $this->keyLen); //需要补齐
$ret = $this->sm4->enDataEcb($data);
break;
case 'sm4-ctr':
$ret = $this->sm4->enDataCtr($data, $iv);
break;
case 'sm4-ofb':
$ret = $this->sm4->enDataOfb($data, $iv);
break;
case 'sm4-cfb':
$ret = $this->sm4->enDataCfb($data, $iv);
break;
default:
throw new Exception('bad type');
}
if ($formatOut == 'hex') {
return bin2hex($ret);
} else if ($formatOut == 'base64') {
return base64_encode($ret);
}
return $ret;
}
public function decrypt($data, $type = 'sm4', $iv = '', $formatInput = 'hex')
{
if ($type != 'sm4-ecb') {
$this->check_iv($iv);
}
if ($formatInput == 'hex') {
$data = hex2bin($data);
} else if ($formatInput == 'base64') {
$data = base64_decode($data);
}
//else is raw
switch ($type) {
case 'sm4':
case 'sm4-cbc':
$ret = $this->sm4->deDataCbc($data, $iv);
$ret = $this->mystr_unpad($ret);
break;
case 'sm4-ecb':
$ret = $this->sm4->deDataEcb($data);
$ret = $this->mystr_unpad($ret);
break;
case 'sm4-ctr':
$ret = $this->sm4->deDataCtr($data, $iv);
break;
case 'sm4-ofb':
$ret = $this->sm4->deDataOfb($data, $iv);
break;
case 'sm4-cfb':
$ret = $this->sm4->deDataCfb($data, $iv);
break;
default:
throw new Exception('bad type');
}
return $ret;
}
//加密前补齐
protected function mystr_pad($data, $len = 16)
{
$n = $len - strlen($data) % $len;
return $data . str_repeat(chr($n), $n);
}
// 解密后去掉补齐
protected function mystr_unpad($data)
{
$n = ord(substr($data, -1));
return substr($data, 0, -$n);
}
protected function check_iv($iv)
{
if (strlen($iv) != $this->ivLen) {
throw new Exception('bad iv');
}
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
//
namespace Rtgm\smecc\SM2;
use Rtgm\smecc\SM3\SM3Digest;
use Rtgm\sm\RtSm2;
class Cipher
{
private $ct = 1;
private $p2;
/**
* @var SM3Digest
*/
private $sm3keybase;
/**
* @var SM3Digest
*/
private $sm3c3;
private $key = array();
private $keyOff = 0;
private function Reset() //注意,加密使用无符号的数组转换,以便与硬件相一致
{
$this->sm3keybase = new SM3Digest();
$this->sm3c3 = new SM3Digest();
$p = array();
$gmp_x = $this->p2->GetX();
$x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
$this->sm3keybase->BlockUpdate($x, 0, sizeof($x));
$this->sm3c3->BlockUpdate($x, 0, sizeof($x));
$gmp_y = $this->p2->GetY();
$y = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y);
$this->sm3keybase->BlockUpdate($y, 0, sizeof($y));
$this->ct = 1;
$this->NextKey();
}
public function initEncipher($userPoint, $foreignKey = null)
{
if (empty($foreignKey)) {
$sm2 = new RtSm2();
$foreignKey = $sm2->generatekey();
}
$foreignPriKey = $foreignKey[0];
$foreignPubKey = $foreignKey[1];
$this->p2 = $userPoint->mul(gmp_init($foreignPriKey, 16));
$this->reset();
return substr($foreignPubKey, -128);
}
public function initDecipher($userPoint, $privateKey)
{
$this->p2 = $userPoint->mul(gmp_init($privateKey, 16));
$this->reset();
}
private function NextKey()
{
$sm3keycur = new SM3Digest();
$sm3keycur->setSM3Digest($this->sm3keybase);
$sm3keycur->Update(($this->ct >> 24 & 0x00ff));
$sm3keycur->Update(($this->ct >> 16 & 0x00ff));
$sm3keycur->Update(($this->ct >> 8 & 0x00ff));
$sm3keycur->Update(($this->ct & 0x00ff));
$sm3keycur->DoFinal($this->key, 0);
$this->keyOff = 0;
$this->ct++;
}
public function encryptBlock($data)
{
$len = count($data);
$this->sm3c3->BlockUpdate($data, 0, $len);
// print_r($data);die();
for ($i = 0; $i < $len; $i++) {
if ($this->keyOff == sizeof($this->key)) {
$this->NextKey();
}
$data[$i] ^= $this->key[$this->keyOff++];
}
return $data;
}
public function decryptBlock($data)
{
$len = count($data);
for ($i = 0; $i < $len; $i++) {
if ($this->keyOff == sizeof($this->key))
$this->NextKey();
$data[$i] ^= $this->key[$this->keyOff++];
}
$this->sm3c3->BlockUpdate($data, 0, $len);
return $data;
}
public function Dofinal()
{
$c3 = array();
$gmp_p = $this->p2->GetY();
$p = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_p);
$this->sm3c3->BlockUpdate($p, 0, sizeof($p));
$this->sm3c3->DoFinal($c3, 0);
$this->Reset();
return $c3;
}
}
@@ -0,0 +1,107 @@
<?php
//
namespace Rtgm\smecc\SM2;
class Hex2ByteBuf
{
public static function ConvertGmp2ByteArray($GmpData)
{
$HexStr = gmp_strval($GmpData, 16);
$OutBuf = array();
$OutBuf = Hex2ByteBuf::HexStringToByteArray($HexStr);
return $OutBuf;
}
public static function ByteArrayToHexString($b, $nlen = null)
{
$outstring = array();
$nlen = $nlen == null ? count($b) : 0;
// var_dump($nlen);
for ($n = 0; $n < $nlen; $n++) {
$outstring[] = Hex2ByteBuf::myhex($b[$n]);
}
// echo (implode("x",$outstring));
return implode("", $outstring);
}
private static function myhex($indata)
{
$temp_1 = intval($indata / 16);
if ($temp_1 < 10)
$temp_1 = $temp_1 + 0x30;
else
$temp_1 = $temp_1 + 0x41 - 10;
$temp_2 = $indata % 16;
if ($temp_2 < 10)
$temp_2 = $temp_2 + 0x30;
else
$temp_2 = $temp_2 + 0x41 - 10;
return chr($temp_1) . chr($temp_2);
}
public static function HexStringToByteArray($InString)
{
for (
$m = strlen($InString);
$m < 64;
$m++
) {
$InString = '0' . $InString;
}
$g_len = 0;
$nlen = 0;
$n = 0;
$i = 0;
$temp = '';
$nlen = strlen($InString);
if ($nlen < 16) $g_len = 16;
$g_len = $nlen / 2;
for (
$n = 0;
$n < $nlen;
$n = $n + 2
) {
$temp = substr($InString, $n, 2);
$temp = '0x' . $temp;
$b[$i] = hexdec($temp);
$i = $i + 1;
}
return $b;
}
public static function HexStringToByteArray2($InString)
{
// for (
// $m = strlen($InString);
// $m < 64;
// $m++
// ) {
// $InString = '0' . $InString;
// }
$g_len = 0;
$nlen = 0;
$n = 0;
$i = 0;
$temp = '';
$nlen = strlen($InString);
if ($nlen < 16) $g_len = 16;
$g_len = $nlen / 2;
for (
$n = 0;
$n < $nlen;
$n = $n + 2
) {
$temp = substr($InString, $n, 2);
$temp = '0x' . $temp;
$b[$i] = hexdec($temp);
$i = $i + 1;
}
return $b;
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
namespace Rtgm\smecc\SM2;
use Rtgm\smecc\SM3\SM3Digest;
class SM2Enc
{
private const SM2_ADDBYTE = 97;//加密后的数据会增加的长度
private const MAX_ENCLEN = 128; //最大的加密长度分组
private const MAX_DECLEN = (self::MAX_ENCLEN + self::SM2_ADDBYTE); //最大的解密长度分组
/* public function SM2_EncStringBySoft($InString, $PubKeyX, $PubKeyY, $generator)
{
$Kx = gmp_init($PubKeyX, 16);
$Ky = gmp_init($PubKeyY, 16);
$userKey = $generator->getPublicKeyFrom($Kx,$Ky,null);
$n = 0;
$incount = 0;
$outcount = 0;
$temp_InBuf = array();
$temp_OutBuf = array();
$inlen = strlen($InString) + 1;
$outlen = ($inlen / $this::MAX_ENCLEN + 1) * $this::SM2_ADDBYTE + $inlen;
$OutBuf = array();
$InBuf = array();
$InBuf=unpack("C*",$InString);
$InBuf[$inlen]=0;//这样是为了保挂与其它开发语言一致
$ret = 0;
$temp_inlen = 0;
while ( $inlen > 0)
{
if ( $inlen > $this::MAX_ENCLEN)
$temp_inlen = $this::MAX_ENCLEN;
else
$temp_inlen = $inlen;
for ( $n = 0; $n < $temp_inlen; $n++)
{
$temp_InBuf[$n] = $InBuf[$incount + $n + 1];//注意,这里要加1,因为UNPACK后是从1开始
}
$temp_OutBuf=$this->sub_EncBufBySoft($temp_InBuf, $temp_inlen, $userKey);
for ( $n = 0; $n < ($temp_inlen + $this::SM2_ADDBYTE); $n++)
{
$OutBuf[ $outcount + $n] = $temp_OutBuf[$n];
}
$inlen = $inlen - $this::MAX_ENCLEN;
$incount = $incount + $this::MAX_ENCLEN;
$outcount = $outcount + $this::MAX_DECLEN;
}
return Hex2ByteBuf::ByteArrayToHexString( $OutBuf, sizeof($OutBuf));
}
private function sub_EncBufBySoft($InBuf, $InBuflen, $userKey)
{
$n = 0 ;
$data = array();
$data = $InBuf;
$cipher = new Cipher();
$c1 = $cipher->Init_enc( $userKey);
$bc1[0]=4;
$gmp_x = $c1->getPoint()->GetX();
$x=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
SM3Digest::arraycopy($x,0,$bc1,1,sizeof($x));
$gmp_y = $c1->getPoint()->GetY();
$y=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y);
SM3Digest::arraycopy($y,0,$bc1,1+32,sizeof($y));
$c1_len = sizeof($bc1);
$data=$cipher->Encrypt( $data ,$InBuflen);
$c3 = array();
$c3=$cipher->Dofinal( );
$OutBuf=array();
for ( $n = 0; $n < $c1_len; $n++)
{
$OutBuf[$n] = $bc1[ $n];
}
for ( $n = 0; $n < $InBuflen; $n++)
{
$OutBuf[ $n + $c1_len] = $data[ $n];
}
for ( $n = 0; $n < 32; $n++)
{
$OutBuf[ $n + $c1_len + $InBuflen] = $c3[ $n];
}
return $OutBuf;
} */
}
+172
View File
@@ -0,0 +1,172 @@
<?php
//
namespace Rtgm\smecc\SM2;
use Rtgm\smecc\SM3\SM3Digest;
class Sm2WithSm3
{
private function GetZ($userId, $HexPubKeyX,$HexPubKeyY,$generator)
{
//$PubKeyX_gmp = gmp_init($HexPubKeyX, 16);
// $PubKeyY_gmp = gmp_init($HexPubKeyY, 16);
$md = array();
$sm3 = new SM3Digest();
// userId length
$id=unpack("C*",$userId);
$len = sizeof($id)*8;
$sm3->Update( ($len >> 8 & 0x00ff));
$sm3->Update( ($len & 0x00ff));
// userId
$sm3->BlockUpdate($id, 1, sizeof($id));
// a,b
$gmp_a = $generator->getCurve()->GetA();
$a=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_a);
$sm3->BlockUpdate($a, 0, sizeof($a));
$gmp_b = $generator->getCurve()->GetB();
$b=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_b);
$sm3->BlockUpdate($b, 0, sizeof($b));
// gx,gy
$gmp_gx = $generator->GetX();
$gx=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_gx);
$sm3->BlockUpdate($gx, 0, sizeof($gx));
$gmp_gy = $generator->GetY();
$gy=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_gy);
$sm3->BlockUpdate($gy, 0, sizeof($gy));
// x,y
$bPubKeyX=array();
$bPubKeyX=Hex2ByteBuf::HexStringToByteArray($HexPubKeyX);
$sm3->BlockUpdate($bPubKeyX, 0, sizeof($bPubKeyX));
$bPubKeyY=array();
$bPubKeyY=Hex2ByteBuf::HexStringToByteArray($HexPubKeyY);
$sm3->BlockUpdate($bPubKeyY, 0, sizeof($bPubKeyY));
$sm3->DoFinal($md, 0);
return $md;
}
private function GetE($z, $HashMsgValue)
{
$md = array();
$sm3 = new SM3Digest();
$sm3->BlockUpdate($z, 0, sizeof($z));
// byte[] p = Encoding.Default.GetBytes(msg);
// sm3.BlockUpdate(p, 0, p.Length);
$sm3->BlockUpdate($HashMsgValue, 0, 32);
$sm3->DoFinal($md, 0);
return $md;
}
public function GetMsgHash( $msg)
{
$md = array();
$sm3 = new SM3Digest();
$msgArray=unpack("C*",$msg);
$sm3->BlockUpdate($msgArray, 1, sizeof($msgArray));
$sm3->DoFinal($md, 0);
return $md;
}
private function Sm2Verify($md, $PubKeyX, $PubKeyY, $VerfiySign,$generator)
{
//SM2Result sm2Ret = new SM2Result();
$InSignBuf = array();
$InSignBuf=Hex2ByteBuf::HexStringToByteArray($VerfiySign);
$Kx = gmp_init($PubKeyX, 16);
$Ky = gmp_init($PubKeyY, 16);
$PubKey = $generator->getPublicKeyFrom($Kx,$Ky,null);
$r = gmp_init(substr($VerfiySign,0, 64), 16);
$s = gmp_init(substr($VerfiySign,64, 64), 16);
$ecc_point_g=$generator->getCurve()->getPoint($generator->GetX(), $generator->GetY());
$Sm2Ret=$this->sub_Sm2Verify($md, $PubKey , $r, $s,$ecc_point_g,$generator);
if (gmp_cmp($r,$Sm2Ret)==0)
{
return true;
}
else
return false;
}
private function sub_Sm2Verify($md, $userKey,$r, $s,$ecc_point_g,$generator)
{
$generator = $userKey->getGenerator();
$ecc_n = $generator->getOrder();
// e_
$md_gmp=Hex2ByteBuf::ByteArrayToHexString($md,sizeof($md));
$e =gmp_init($md_gmp,16);
// t
$t=gmp_add($r,$s);
$t=gmp_mod($t,$ecc_n);
$zero = gmp_init(0, 10);
if (gmp_cmp($t,$zero)==0)
return null;
// x1y1
$x1y1 = $ecc_point_g->mul($s);
$x1y1 = $x1y1->add($userKey->getPoint()->mul($t));
// R
return gmp_mod(gmp_add($e,$x1y1->GetX()),$ecc_n);
}
public function YtVerfiyBySoft($id, $msg, $PubKeyX, $PubKeyY, $VerfiySign,$generator)
{
$Z = array();
$E = array();
$MsgHashValue = array();
$Z = $this->GetZ($id, $PubKeyX, $PubKeyY,$generator);
$MsgHashValue = $this->GetMsgHash($msg);
$E = $this->GetE($Z, $MsgHashValue);
$IsVailSign = $this->Sm2Verify($E, $PubKeyX, $PubKeyY, $VerfiySign,$generator);
return $IsVailSign;
}
public function getSm3Hash($msg,$PubKeyX, $PubKeyY, $generator, $userId="1234567812345678") {
// sm3(z+msg)
$z = $this->GetZ($userId, $PubKeyX, $PubKeyY,$generator);
$md = array();
$sm3 = new SM3Digest();
$sm3->BlockUpdate($z, 0, sizeof($z));
$msgArray=unpack("C*",$msg);
$sm3->BlockUpdate($msgArray, 1, sizeof($msgArray));
$sm3->DoFinal($md, 0);
return $md;
}
}
@@ -0,0 +1,112 @@
<?php
//
namespace Rtgm\smecc\SM3;
class GeneralDigest
{
private const BYTE_LENGTH = 64;
private $xBuf=array();
private $xBufOff;
private $byteCount;
public function setGeneralDigest($t)
{
$this->arraycopy($t->xBuf, 0, $this->xBuf, 0, sizeof($t->xBuf));
$this->xBufOff = $t->xBufOff;
$this->byteCount = $t->byteCount;
}
public function __construct( )
{
$this->xBuf[0]=0;
$this->xBuf[1]=0;
$this->xBuf[2]=0; $this->xBuf[3]=0;
}
public function Update($input)
{
$this->xBuf[$this->xBufOff++] = $input;
if ($this->xBufOff == sizeof($this->xBuf))
{
$this->ProcessWord($this->xBuf, 0);
$this->xBufOff = 0;
}
$this->byteCount++;
}
public function BlockUpdate(
$input,
$inOff,
$length)
{
//
// fill the current word
//
while (($this->xBufOff != 0) && ($length > 0))
{
$this->Update($input[$inOff]);
$inOff++;
$length--;
}
//
// process whole words.
//
while ($length > sizeof($this->xBuf))
{
$this->ProcessWord($input, $inOff);
$inOff += sizeof($this->xBuf);
$length -= sizeof($this->xBuf);
$this->byteCount += sizeof($this->xBuf);
}
//
// load in the remainder.
//
while ($length > 0)
{
$this->Update($input[$inOff]);
$inOff++;
$length--;
}
}
public function Finish()
{
// $bitLength = ($this->byteCount << 3);
$bitLength = $this->LeftRotateLong($this->byteCount , 3);
//
// add the pad bytes.
//
$this->Update(128);
while ($this->xBufOff != 0) $this->Update(0);
$this->ProcessLength($bitLength);
$this->ProcessBlock();
}
public function Reset()
{
$this->byteCount = 0;
$this->xBufOff = 0;
$this->xBuf[0]=0;
$this->xBuf[1]=0;
$this->xBuf[2]=0; $this->xBuf[3]=0;
}
public function GetByteLength():int
{
return $this::BYTE_LENGTH;
}
}
+348
View File
@@ -0,0 +1,348 @@
<?php
//declare(strict_types=1);
namespace Rtgm\smecc\SM3;
use Rtgm\smecc\SM3\GeneralDigest;
class SM3Digest extends GeneralDigest
{
public static $AlgorithmName="SM3";
private const DIGEST_LENGTH = 32;
public function GetDigestSize():int
{
return $this::DIGEST_LENGTH;
}
private static $v0 = array(0x7380166f, 0x4914b2b9, 0x172442d7, -628488704, -1452330820, 0x163138aa,-477237683, -1325724082);
private $v = array(0, 0, 0, 0, 0, 0, 0, 0);
private $v_ = array(0, 0, 0, 0, 0, 0, 0, 0);
private static $X0 = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
private $X = array();
private $xOff;
private $T_00_15 = 0x79cc4519;
private $T_16_63 = 0x7a879d8a;
public function __construct()
{
parent::__construct();
// parent::$gSM3Digest=$this;
$this->Reset();
}
public function setSM3Digest($t)
{
parent::setGeneralDigest($t);
$this->arraycopy($t->X, 0, $this->X, 0, sizeof($t->X));
$this->xOff = $t->xOff;
$this->arraycopy($t->v, 0, $this->v, 0, sizeof($t->v));
}
public function Reset()
{
parent::Reset();
$this->arraycopy(SM3Digest::$v0, 0, $this->v, 0, sizeof(SM3Digest::$v0));
$this->xOff = 0;
$this->arraycopy(SM3Digest::$X0, 0, $this->X, 0, sizeof(SM3Digest::$X0));
}
public static function arraycopy($InBuf,$InBufPos,&$OutBuf,$OutBufPos,$Len)
{
for( $n = 0 ;$n< $Len;$n++)
{
$OutBuf[$n + $OutBufPos] =$InBuf[$n+$InBufPos];
}
}
public function ProcessBlock()
{
$ww = $this->X;
$ww_ = array();
for ($i = 16; $i < 68; $i++)
{
$ww[$i] = $this->P1( $ww[ $i - 16] ^ $ww[$i - 9] ^ ($this->ROTATE($ww[$i - 3], 15))) ^ ($this->ROTATE($ww[$i - 13], 7)) ^ $ww[$i - 6];
}
for ($i = 0; $i < 64; $i++)
{
$ww_[$i] = $ww[$i] ^ $ww[$i + 4];
}
$vv = $this->v;
$vv_ = $this->v_;
$this->arraycopy($vv, 0, $vv_, 0, sizeof($vv));
// int SS1, SS2, TT1, TT2, aaa;
for ($i = 0; $i < 16; $i++)
{
$aaa = $this->ROTATE($vv_[0], 12);
$SS1 = $this->MyAdd($aaa , $vv_[4]);
$SS1 = $this->MyAdd($SS1, $this->ROTATE($this->T_00_15, $i));
$SS1 = $this->ROTATE($SS1, 7);
$SS2 = $SS1 ^ $aaa;
$TT1=$this->FF_00_15($vv_[0], $vv_[1], $vv_[2]);
$TT1=$this->MyAdd($TT1, $vv_[3]);
$TT1=$this->MyAdd($TT1, $SS2);
$TT1=$this->MyAdd($TT1, $ww_[$i]);
$TT2=$this->GG_00_15($vv_[4], $vv_[5], $vv_[6]);
$TT2=$this->MyAdd($TT2, $vv_[7]);
$TT2=$this->MyAdd($TT2, $SS1);
$TT2=$this->MyAdd($TT2, $ww[$i]);
$vv_[3] = $vv_[2];
$vv_[2] = $this->ROTATE($vv_[1], 9);
$vv_[1] = $vv_[0];
$vv_[0] = $TT1;
$vv_[7] = $vv_[6];
$vv_[6] = $this->ROTATE($vv_[5], 19);
$vv_[5] = $vv_[4];
$vv_[4] = $this->P0($TT2);
}
for ($i = 16; $i < 64; $i++)
{
$aaa = $this->ROTATE($vv_[0], 12);
$SS1 = $this->MyAdd($aaa , $vv_[4]);
$z= $this->ROTATE($this->T_16_63, $i);
$SS1 = $this->MyAdd( $SS1, $this->ROTATE($this->T_16_63, $i));
$SS1 = $this->ROTATE($SS1, 7);
$SS2 = $SS1 ^ $aaa;
$TT1 = $this->MyAdd($this->FF_16_63($vv_[0], $vv_[1], $vv_[2]) , $vv_[3]);
$TT1 = $this->MyAdd( $TT1, $SS2);
$TT1 = $this->MyAdd( $TT1, $ww_[$i]);
$TT2 = $this->MyAdd($this->GG_16_63($vv_[4], $vv_[5], $vv_[6]) , $vv_[7]);
$TT2 = $this->MyAdd( $TT2, $SS1);
$TT2 = $this->MyAdd($TT2 , $ww[$i]);
$vv_[3] = $vv_[2];
$vv_[2] = $this->ROTATE($vv_[1], 9);
$vv_[1] = $vv_[0];
$vv_[0] = $TT1;
$vv_[7] = $vv_[6];
$vv_[6] = $this->ROTATE($vv_[5], 19);
$vv_[5] = $vv_[4];
$vv_[4] = $this->P0($TT2);
}
for ($i = 0; $i < 8; $i++)
{
$vv[$i] ^= $vv_[$i];
}
$this->v=$vv;
$this->v_=$vv_;
// Reset
$this->xOff = 0;
$this->arraycopy(SM3Digest::$X0, 0, $this->X, 0, sizeof(SM3Digest::$X0));
}
public function ProcessWord($in_Renamed, $inOff)
{
$n = $this->LeftRotateLong($in_Renamed[$inOff] , 24);
$n |= $this->LeftRotateLong(($in_Renamed[++$inOff] & 0xff) , 16);
$n |= $this->LeftRotateLong(($in_Renamed[++$inOff] & 0xff) , 8);
$n |= ($in_Renamed[++$inOff] & 0xff);
$this->X[$this->xOff] = $n;
if (++$this->xOff == 16)
{
$this->ProcessBlock();
}
}
public function ProcessLength($bitLength)
{
if ($this->xOff > 14)
{
$this->ProcessBlock();
}
$this->X[14] = ($this->RightRotateLong($bitLength, 32));
$this->X[15] = ($bitLength & 0xffffffff);
}
public function IntToBigEndian($n, &$bs, $off)
{
$bs[$off] = ($this->RightRotateLong($n, 24)) & 0xff;
$bs[++$off] = ($this->RightRotateLong($n, 16)) & 0xff;
$bs[++$off] = ($this->RightRotateLong($n, 8)) & 0xff;
$bs[++$off] = ($n) & 0xff ;
}
public function DoFinal(&$out_Renamed, $outOff):int
{
$this->Finish();
for ($i = 0; $i < 8; $i++)
{
$this->IntToBigEndian($this->v[$i], $out_Renamed, $outOff + $i * 4);
}
$this->Reset();
return $this::DIGEST_LENGTH;
}
private function HandleSign($lValue)
{
$lValue = $lValue & 0xFFFFFFFF;
if($lValue>=0x80000000)
{
$lValue=$lValue-(0xffffffff+1);
}
return $lValue;
}
private function MyAdd($A,$B)
{
$lValue=$this->HandleSign($A) + $this->HandleSign($B);
return $this->HandleSign($lValue);
}
public function LeftRotateLong($lValue, $lBits )
{
$lBits = $lBits % 32;
$lValue=$lValue<< $lBits;
$lValue=$lValue & 0xffffffff;
if($lValue>=0x80000000)
{
$lValue=$lValue-(0xffffffff+1);
}
return $lValue;
/* $lngSign=0; $intI=0;
$mValue=0;
$lBits = $lBits % 32;
$mValue = $lValue;
if($lBits == 0) return $mValue;
For ($intI = 1 ;$intI<= $lBits;$intI++)
{
$lngSign = $mValue & 0x40000000;
$mValue = ($mValue & 0x3FFFFFFF) * 2;
if($lngSign & 0x40000000)
$mValue = $mValue | 0x80000000;
}
return $mValue;*/
}
private function RightRotateLong($lValue , $lBits)
{
$lngSign=0;$intI=0;
$mValue =0;
$mValue = $lValue;
// $lBits = $lBits % 32;
if( $lBits == 0 )
{
return $mValue ;
}
if ($lValue >= 0)
{
if($lBits<0) $lBits= 32+ $lBits;
$r = $lValue >> $lBits;
}
else
{
$t=~$lBits;
if($t<0) $t= 32+ $t;
// $t = $t % 32;
$r= ($lValue >> $lBits) + (2 << $t);
}
return $r;
/* For ($intI = 1 ;$intI<= $lBits;$intI++)
{
$lngSign = $mValue & 0x80000000;
$mValue = ($mValue & 0x7FFFFFFF) / 2;
if ($lngSign)
$mValue = $mValue | 0x40000000;
}
return $mValue;*/
}
private function FFj($X, $Y, $Z, $j) :int
{
if($j>=0 && $j<=15) {
return $this->FF_00_15($X, $Y, $Z);
} else {
return $this->FF_16_63($X, $Y, $Z);
}
}
private function GGj($X, $Y, $Z, $j) :int
{
if($j>=0 && $j<=15) {
return $this->GG_00_15($X, $Y, $Z);
} else {
return $this->GG_16_63($X, $Y, $Z);
}
}
private function ROTATE($X, $n):int
{
// $r=($this->RightRotateLong($X, (32 - $n)));
//$r1=$this->LeftRotateLong($X , $n);
// $r2=($X << $n);
return $this->LeftRotateLong($X , $n) | ($this->RightRotateLong($X, (32 - $n)));
}
private function P0($X):int
{
$a=$this->ROTATE(($X), 9);
$b= $this->ROTATE(($X), 17);
return (($X) ^ $this->ROTATE(($X), 9) ^ $this->ROTATE(($X), 17));
}
private function P1($X):int
{
return (($X) ^ $this->ROTATE(($X), 15) ^ $this->ROTATE(($X), 23));
}
private static function FF_00_15($X, $Y, $Z):int
{
return ($X ^ $Y ^ $Z);
}
private static function FF_16_63($X, $Y, $Z):int
{
return (($X & $Y) | ($X & $Z) | ($Y & $Z));
}
private static function GG_00_15($X, $Y, $Z):int
{
return ($X ^ $Y ^ $Z);
}
private static function GG_16_63($X, $Y, $Z):int
{
return (($X & $Y) | (~ $X & $Z));
}
}
+349
View File
@@ -0,0 +1,349 @@
<?php
/**
* source: https://github.com/lizhichao/sm
*/
namespace Rtgm\smecc\SM4;
class Sm4
{
private $ck = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
];
private $Sbox = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
];
private $fk = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
private $rk = [];
private $b = '';
private $len = 16;
/**
* Sm4 constructor.
* @param string $key 秘钥长度16位
* @param string $b 不是16的倍数 需要的补码
* @throws \Exception
*/
public function __construct($key, $b = ' ')
{
$this->ck16($key);
$this->crk($key);
}
private function dd(&$data)
{
$n = strlen($data) % $this->len;
$data = $data . str_repeat($this->b, $n);
}
private function ck16($str)
{
if (strlen($str) !== $this->len) {
throw new \Exception('秘钥长度为16位');
}
}
private function add($v)
{
$arr = unpack('N*', $v);
$max = 0xffffffff;
$j = 1;
for ($i = 4; $i > 0; $i--) {
if ($arr[$i] > $max - $j) {
$j = 1;
$arr[$i] = 0;
} else {
$arr[$i] += $j;
break;
}
}
return pack('N*', ...$arr);
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataCtr($str, $iv)
{
return $this->enDataCtr($str, $iv);
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataCtr($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$s1 = pack('N*', ...$tr);
$s1 = $s1 ^ $s;
$iv = $this->add($iv);
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataOfb($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$iv = pack('N*', ...$tr);
$s1 = $s ^ $iv;
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataOfb($str, $iv)
{
return $this->enDataOfb($str, $iv);
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataCfb($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$s1 = pack('N*', ...$tr);
$s1 = $s ^ $s1;
$iv = $s;
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataCfb($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$s1 = pack('N*', ...$tr);
$iv = $s ^ $s1;
$r .= $iv;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataCbc($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$s = $iv ^ $s;
$tr = [];
$this->encode(array_values(unpack('N*', $s)), $tr);
$iv = pack('N*', ...$tr);
$r .= $iv;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataCbc($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->decode(array_values(unpack('N*', $s)), $tr);
$s1 = pack('N*', ...$tr);
$s1 = $iv ^ $s1;
$iv = $s;
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @return string
*/
public function enDataEcb($str)
{
$r = [];
$this->dd($str);
$ar = unpack('N*', $str);
do {
$this->encode([current($ar), next($ar), next($ar), next($ar)], $r);
} while (next($ar));
return pack('N*', ...$r);
}
/**
* @param string $str 解密字符串
* @return string
*/
public function deDataEcb($str)
{
$r = [];
$this->dd($str);
$ar = unpack('N*', $str);
do {
$this->decode([current($ar), next($ar), next($ar), next($ar)], $r);
} while (next($ar));
return pack('N*', ...$r);
}
private function encode($ar, &$r)
{
for ($i = 0; $i < 32; $i++) {
$ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[$i]);
}
$r[] = $ar[35];
$r[] = $ar[34];
$r[] = $ar[33];
$r[] = $ar[32];
}
private function decode($ar, &$r)
{
for ($i = 0; $i < 32; $i++) {
$ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[31 - $i]);
}
$r[] = $ar[35];
$r[] = $ar[34];
$r[] = $ar[33];
$r[] = $ar[32];
}
private function crk($key)
{
$keys = array_values(unpack('N*', $key));
$keys = [
$keys[0] ^ $this->fk[0],
$keys[1] ^ $this->fk[1],
$keys[2] ^ $this->fk[2],
$keys[3] ^ $this->fk[3]
];
for ($i = 0; $i < 32; $i++) {
$this->rk[] = $keys[] = $keys[$i] ^ $this->t1($keys[$i + 1] ^ $keys[$i + 2] ^ $keys[$i + 3] ^ $this->ck[$i]);
}
}
private function lm($a, $n)
{
return ($a >> (32 - $n) | (($a << $n) & 0xffffffff));
}
private function f($x0, $x1, $x2, $x3, $r)
{
return $x0 ^ $this->t($x1 ^ $x2 ^ $x3 ^ $r);
}
private function s($n)
{
return $this->Sbox[($n & 0xff)] | $this->Sbox[(($n >> 8) & 0xff)] << 8 | $this->Sbox[(($n >> 16) & 0xff)] << 16 | $this->Sbox[(($n >> 24) & 0xff)] << 24;
}
private function t($n)
{
$b = $this->s($n);
return $b ^ $this->lm($b, 2) ^ $this->lm($b, 10) ^ $this->lm($b, 18) ^ $this->lm($b, 24);
}
private function t1($n)
{
$b = $this->s($n);
return $b ^ $this->lm($b, 13) ^ $this->lm($b, 23);
}
}
@@ -0,0 +1,473 @@
<?php
namespace Rtgm\smecc\SPLSM2;
use Exception;
class SimpleSm2
{
protected $p;
protected $a;
protected $b;
protected $n;
protected $gx;
protected $gy;
protected $size;
protected $userId = '1234567812345678';
//请自行重新生成一对,示例中的foreignKey可能被大量项目使用,而被对方加黑
protected $foreignKey = array(
'21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225',
'04e27c3780e7069bda7082a23a489d77587ce309583ed99253f66e1d9833ed1a1d0b5ce86dc6714e9974cf258589139d7b1855e8c9fa2f2c1175ee123a95a23e9b'
);
protected $privateKey;
protected $publicKey;
protected $sm3;
protected $randSign = true; // true则同一字符串,同样密钥,每次签名不一样,false是每次签名都一样
protected $randEnc = true; // true则同一字符串,同样密钥,每次签名不一样,false是每次签名都一样
protected $fixForeignKey = false; //中间了椭圆固定,在加密时减少生成密码对,性能有所提升,安全性有所下降,
function __construct()
{
$this->sm3 = new Sm3();
$eccParams = Sm2Ecc::get_params();
$this->p = $eccParams['p'];
$this->a = $eccParams['a'];
$this->b = $eccParams['b'];
$this->n = $eccParams['n'];
// 计算基点G
$this->gx = $eccParams['gx'];
$this->gy = $eccParams['gy'];
}
public function general_pair()
{
$pointG = new Sm2Point($this->gx, $this->gy);
$prikeyGmp = $this->rand_prikey();
$publicKey = $this->get_pkey_from_prikey($prikeyGmp, $pointG);
$prikey = $this->decHex($prikeyGmp, 64);
return array(
$prikey, $publicKey
);
}
/**
* 通过私钥算公钥 Pub = pG
*
* @param GMP $prikey
* @return string
*/
public function get_pkey_from_prikey($prikeyGmp, $pointG = null)
{
if (empty($pointG)) {
$pointG = new Sm2Point($this->gx, $this->gy);
}
$kG = $pointG->mul($prikeyGmp, false);
$x1 = $this->decHex($kG->getX(), 64);
$y1 = $this->decHex($kG->getY(), 64);
$publicKey = '04' . $x1 . $y1;
return $publicKey;
}
// 生成标准的 base64 的 asn1(r,s)签名
public function sign($document, $prikey, $publicKey = null, $userId = null){
list($r,$s) = $this->sign_raw($document, $prikey, $publicKey, $userId);
return Sm2Asn1::rs_2_asn1($r,$s);
}
/**
*
*
* @param string $document
* @param string $prikey
* @param string $publicKey //这个值虽然可以从prikey中计算出来,但直接给出来,不用每次计算,性能会好一点点
* @param string $userId
* @return array
*/
public function sign_raw($document, $prikey, $publicKey = null, $userId = null)
{
$gmpPrikey = gmp_init($prikey, 16);
// 如果知道公钥直接填上是好的,减少运算,虽说从私钥可以算出公钥,这不浪费资源不是
if (empty($publicKey)) {
$publicKey = $this->get_pkey_from_prikey($gmpPrikey);
}
$hash = $this->get_sm2withsm3_hash($document, $publicKey, $userId);
$gmpHash = gmp_init($hash, 16);
$count = 0;
while (true) {
$count++;
if ($count > 5) {
//5次都有问题,肯定有问题了
throw new \RuntimeException('Error: sign R or S = 0');
}
//中间椭圆的私钥
// $k = gmp_init('21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225',16);
$k = $this->_get_forign_prikey($document);
// var_dump(gmp_strval($k,16),'21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225');
$gmpP1x = $this->_get_forign_pubkey_x($k);
$r = gmp_mod(gmp_add($gmpHash, $gmpP1x), $this->n);
$zero = gmp_init(0, 10);
if (gmp_cmp($r, $zero) === 0) {
continue; //报错重来一次
}
$one = gmp_init(1, 10);
$s1 = gmp_invert(gmp_add($one, $gmpPrikey), $this->n);
$s2 = gmp_sub($k, gmp_mul($r, $gmpPrikey));
$s = gmp_mod(gmp_mul($s1, $s2), $this->n);
if (gmp_cmp($s, $zero) === 0) {
continue;
// throw new \RuntimeException('Error: random number S = 0');
}
return array(gmp_strval($r, 16), gmp_strval($s, 16));
}
}
// 标准的asn1 base64签名验签
public function verify($document, $publicKey, $sign, $userId = null){
list($hexR,$hexS) = Sm2Asn1::asn1_2_rs($sign);
return $this->verifty_sign_raw($document, $publicKey, $hexR, $hexS, $userId);
}
/**
* Undocumented function
*
* @param string $document bin
* @param string $publicKey hex
* @param string $r hex
* @param string $s hex
* @param string $userId
* @return bool
*/
public function verifty_sign_raw($document, $publicKey, $hexR, $hexS, $userId = null)
{
$plen = strlen($publicKey);
if ($plen == 130 && substr($publicKey, 0, 2) == '04') {
$pubX = substr($publicKey, 2, 64);
$pubY = substr($publicKey, -64);
} else if ($plen == 128) {
$pubX = substr($publicKey, 0, 64);
$pubY = substr($publicKey, -64);
} else {
throw new Exception("bad publickey $publicKey");
}
// 1.2.3.4 sm3 取msg,userid的 hash值,
$hash = gmp_init($this->get_sm2withsm3_hash($document, $publicKey, $userId), 16);
$r = gmp_init($hexR, 16);
$s = gmp_init($hexS, 16);
$n = $this->n;
$one = gmp_init(1, 10);
if (gmp_cmp($r, $one) < 0 || gmp_cmp($r, gmp_sub($n, $one)) > 0) {
return false;
}
if (gmp_cmp($s, $one) < 0 || gmp_cmp($s, gmp_sub($n, $one)) > 0) {
return false;
}
// 第五步 计算t=(r'+s')mod n
$t = gmp_mod(gmp_add($r, $s), $n);
// // 第六步 计算(x1,y1) = [s]G + [t]P
$pointG = new Sm2Point($this->gx, $this->gy); //生成基准点
$p1 = $pointG->mul($s, false); // p1 = sG
$pointPub = new Sm2Point(gmp_init($pubX, 16), gmp_init($pubY, 16)); //生成公钥的基准点
$p2 = $pointPub->mul($t, false); // p2 = tP
$xy = $p1->add($p2);
// // 第七步 vR=(hash' + x1')
$v = gmp_mod(gmp_add($hash, $xy->getX()), $n);
// 最后结果 比较 $v和$r是否一致
// var_dump(gmp_strval($v,16),$hexR);
return gmp_strval($v, 16) == $hexR;
}
public function encrypt_raw($publicKey, $data)
{
list($pubX, $pubY) = $this->_get_pub_xy($publicKey);
$point = new Sm2Point($pubX,$pubY);
$t = '';
$count = 0;
while (!$t) {
$count++;
if($count>5){
throw new Exception('bad kdf '); // 这处一般是生成的$k问题,5次都有问题,这运气差的可以买双色球了
}
if($this->fixForeignKey) { //使用固定的第中间椭圆
list($x1,$y1) = $this->_get_pub_xy($this->foreignKey[1],false);
$k = gmp_init($this->foreignKey[0],16);
$x1 = $this->format_hex($x1,64);// 不足前面补0
$y1 = $this->format_hex($y1,64);// 不足前面补0
} else {
$k = $this->_get_forign_prikey($data.'_'.$count);
//dump($k);
//$k = gmp_init('104953050056413721046883757640585885959005820148174417356964987920496726278110',10);
$kG = $point->mul($k);
$x1 = $this->decHex($kG->getX(), 64);
$y1 = $this->decHex($kG->getY(), 64);
}
$c1 = $x1 . $y1;
$kPb = $point->mul($k, false);
$x2 = gmp_strval($kPb->getX(), 16);
$y2 = gmp_strval($kPb->getY(), 16);
$x2 = pack('H*', str_pad($x2, 64, 0, STR_PAD_LEFT));
$y2 = pack('H*', str_pad($y2, 64, 0, STR_PAD_LEFT));
$t = $this->kdf($x2 . $y2, strlen($data));
}
$c2 = gmp_xor(gmp_init($t, 16), $this->str2gmp($data));
$c2 = $this->decHex($c2, strlen($data) * 2);
$c3 = $this->hash_sm3($x2 . $data . $y2);
return array($c1, $c3, $c2);
}
/**
* sm2非对称解密
*
* @param string $prikey 私钥明文 hex
* @param string $c1 hex
* @param string $c3 hex
* @param string $c2 hex
* @return string decode($c2) 解密结果
*/
public function decrypt_raw($prikey, $c1,$c3,$c2)
{
list($x1, $y1) = $this->_get_pub_xy($c1);
$point = new Sm2Point($x1,$y1);
$dbC1 = $point->mul(gmp_init($prikey,16), false);
$x2 = gmp_strval($dbC1->getX(), 16);
$y2 = gmp_strval($dbC1->getY(), 16);
$x2 = pack('H*', str_pad($x2, 64, 0, STR_PAD_LEFT));
$y2 = pack('H*', str_pad($y2, 64, 0, STR_PAD_LEFT));
$len = strlen($c2);
$t = $this->kdf($x2 . $y2, $len / 2); // 转成16进制后 字符长度要除以2
$m1 = gmp_strval(gmp_xor(gmp_init($t, 16), gmp_init($c2, 16)), 16);
$m1 = pack("H*", $m1);
$u = $this->hash_sm3($x2 . $m1 . $y2);
if (strtoupper($u) != strtoupper($c3)) {
throw new \Exception("error decrypt data");
}
return $m1;
}
protected function kdf($z, $klen)
{
$res = '';
$ct = 1;
$j = ceil($klen / 32);
for ($i = 0; $i < $j; $i++) {
$ctStr = str_pad(chr($ct), 4, chr(0), STR_PAD_LEFT);
$hex = $this->hash_sm3($z . $ctStr);
if ($i + 1 == $j && $klen % 32 != 0) { // 最后一个 且 $klen/$v 不是整数
$res .= substr($hex, 0, ($klen % 32) * 2); // 16进制比byte长度少一半 要乘2
} else {
$res .= $hex;
}
$ct++;
}
return $res;
}
/**
* Undocumented function
*
* @param string $message
* @param boolean $raw
* @return string
*/
public function hash_sm3($message, $raw = false)
{
// return $this->sm3->digest($message, $raw);
// 有些版本的PHP直接支持sm3
return openssl_digest($message,'sm3',$raw);
}
/**
* hex 用0补齐一定的位置
*
* @param string $hex
* @param integer $count
* @return string
*/
public function format_hex($hex, $count = 64)
{
return str_pad($hex, $count, "0", STR_PAD_LEFT);
}
/**
* 采用gmp自带的函数随机生成私钥,gmp_random_bits需要5.6.3才有
* 也可其他随机函数生成
*
* @param integer $numBits
* @return \GMP
*/
public function rand_prikey($numBits = 256)
{
if (!function_exists('gmp_random_bits')) {
return $this->_get_forign_prikey('loveyou');
}
$value = gmp_random_bits($numBits);
$mask = gmp_sub(gmp_pow(2, $numBits), 1);
$integer = gmp_and($value, $mask);
return $integer;
}
/**
* gmp 转 hex,并用0补齐位数
*
* @param GMP|int $dec
* @param integer $len
* @return string
*/
public function decHex($dec, $len = 0)
{
if (!$dec instanceof \GMP) {
$dec = gmp_init($dec, 10);
}
if (gmp_cmp($dec, 0) < 0) {
throw new \Exception('Unable to convert negative integer to string');
}
$hex = gmp_strval($dec, 16);
if (strlen($hex) % 2 != 0) {
$hex = '0' . $hex;
}
if ($len && strlen($hex) < $len) { // point x y 要补齐 64 位
$hex = str_pad($hex, $len, "0", STR_PAD_LEFT);
}
return $hex;
}
/**
* Undocumented function
*
* @param string $document
* @param string $publicKey
* @param string $userId
* @return string
*/
public function get_sm2withsm3_hash($document, $publicKey, $userId)
{
// 置M=ZA∥MZA= Hv(ENTLA||IDA||a||b||Gx||Gy||Ax||Ay) IDA==>userId
// ENTLA为IDA的比特长度,2字节;IDA用户标识默认值见上节;a,b,Gx,Gy见曲线参数;Ax,Ay为公钥坐标
$len = strlen($publicKey);
if ($len == 130) {
$publicKey = substr($publicKey, 2);
} else if ($len == 128) {
//OK
} else {
throw new \Exception('bad pulickey');
}
$px = gmp_init(substr($publicKey, 0, 64), 16);
$py = gmp_init(substr($publicKey, 64, 64), 16);
$zStr = $this->_get_entla($userId);
$zStr .= $userId;
$zStr .= hex2bin(gmp_strval($this->a, 16));
$zStr .= hex2bin(gmp_strval($this->b, 16));
$zStr .= hex2bin(gmp_strval($this->gx, 16));
$zStr .= hex2bin(gmp_strval($this->gy, 16));
$zStr .= hex2bin(gmp_strval($px, 16));
$zStr .= hex2bin(gmp_strval($py, 16));
$hashStr = $this->hash_sm3($zStr);
$hash = $this->hash_sm3(hex2bin($hashStr) . $document);
return $hash;
}
/**
* 生成随机私钥
*
* @param string $document
* @return \GMP
*/
protected function _get_forign_prikey($document = '')
{
// 要支持php5的话,没有什么好函数了,如果是php7或以上或以使用
// $s = random_bytes(64) 或 this->rand_prikey(int bits=256) 代替
if ($this->randSign || $this->randEnc) { // 从document ==>k 变化
$s = substr(openssl_digest('S1' . $document . microtime(), 'sha1'), 1, 32) . md5($document . microtime() . 'S2');
} else {
$s = substr(openssl_digest('S1' . $document, 'sha1'), 1, 32) . md5($document . 'S2');
}
$s = strtolower($s);
if (substr($s, 0, 1) == 'f') { //私钥不要太大了,超过 n值就不好了,
$s = 'e' . substr($s, 1);
}
return gmp_init($s, 16);
}
protected function _get_forign_pubkey_x($k)
{
$pointG = new Sm2Point($this->gx, $this->gy);
$kG = $pointG->mul($k, false);
return $kG->getX();
}
protected function _get_entla($userId)
{
$len = strlen($userId) * 8;
$l1 = $len >> 8 & 0x00ff;
$l2 = $len & 0x00ff;
return chr($l1) . chr($l2);
}
protected function _gmp_to_bin($gmp)
{
return hex2bin(gmp_strval($gmp, 16));
}
public function set_private_key($privateKey)
{
$this->privateKey = $privateKey;
}
public function set_public_key($publicKey)
{
$this->publicKey = $publicKey;
}
public function set_userid($userId)
{
if (empty($userId) || strlen($userId) != 16) {
throw new Exception(" userid 格式不对");
}
$this->userId = $userId;
}
public function set_rand_sign_flag($flag = false)
{
$this->randSign = $flag;
}
public function set_rand_enc_flag($flag = false)
{
$this->randEnc = $flag;
}
public function set_fix_foreignkey_flag($flag = true){
$this->fixForeignKey = $flag;
}
public function str2gmp($string)
{
$hex = unpack('H*', $string);
return gmp_init($hex[1], 16);
}
protected function _get_pub_xy($publicKey,$rtGmp = true){
$plen = strlen($publicKey);
if ($plen == 130 && substr($publicKey, 0, 2) == '04') {
$pubX = substr($publicKey, 2, 64);
$pubY = substr($publicKey, -64);
} else if ($plen == 128) {
$pubX = substr($publicKey, 0, 64);
$pubY = substr($publicKey, -64);
} else {
throw new Exception("bad publickey $publicKey");
}
if($rtGmp){
return array(gmp_init($pubX,16),gmp_init($pubY,16));
}
// var_dump($publicKey,$pubX, $pubY,'==============');
return array($pubX, $pubY);
}
}
+232
View File
@@ -0,0 +1,232 @@
<?php
namespace Rtgm\smecc\SPLSM2;
/**
*
* 针对签名,加解密只有一层 asn1的解析,不做其他类型的解析,
*/
define('MAXLEVEL', 2); // 简单解析,只解析两层就够了
class Sm2Asn1
{
const CLASS_UNIVERSAL = 0;
const CLASS_APPLICATION = 1;
const CLASS_CONTEXT_SPECIFIC = 2;
const CLASS_PRIVATE = 3;
const TYPE_BOOLEAN = 1;
const TYPE_INTEGER = 2;
const TYPE_BIT_STRING = 3;
const TYPE_OCTET_STRING = 4;
const TYPE_NULL = 5;
const TYPE_OBJECT_IDENTIFIER = 6;
const TYPE_OBJECT_DESCRIPTOR = 7;
const TYPE_INSTANCE_OF = 8; // EXTERNAL
const TYPE_REAL = 9;
const TYPE_ENUMERATED = 10;
const TYPE_EMBEDDED = 11;
const TYPE_UTF8_STRING = 12;
const TYPE_RELATIVE_OID = 13;
const TYPE_SEQUENCE = 16; // SEQUENCE OF
const TYPE_SET = 17; // SET OF
const TYPE_NUMERIC_STRING = 18;
const TYPE_PRINTABLE_STRING = 19;
const TYPE_TELETEX_STRING = 20; // T61String
const TYPE_VIDEOTEX_STRING = 21;
const TYPE_IA5_STRING = 22;
const TYPE_UTC_TIME = 23;
const TYPE_GENERALIZED_TIME = 24;
const TYPE_GRAPHIC_STRING = 25;
const TYPE_VISIBLE_STRING = 26; // ISO646String
const TYPE_GENERAL_STRING = 27;
const TYPE_UNIVERSAL_STRING = 28;
const TYPE_CHARACTER_STRING = 29;
const TYPE_BMP_STRING = 30;
const TYPE_BIG_SEQUENCE = 48;
const TYPE_BIG_SET = 49;
const TYPE_CHOICE = -1;
const TYPE_ANY = -2;
const TYPE_ANY_RAW = -3;
const TYPE_ANY_SKIP = -4;
const TYPE_ANY_DER = -5;
/**
* 解析简单的asn1
*
* @param string bin $data
* @param integer $level
* @return array <string>
*/
public static function decode($data, $level = 0)
{
$result = array();
$pos = 0;
while (abs($pos) < strlen($data)) {
$octets = 0;
$length = 0;
$tag = ord($data[$pos]);
$pos++;
if ($tag == 0) {
$result[] = '00';
continue;
}
$temp = ord($data[$pos]);
// echo "temp = $temp\n";
if ($temp == 128) {
$length = 0;
} else if ($temp > 128) {
$octets = $temp & 127;
$length = 0;
// echo $octets."====\n";
for ($i = 0; $i < $octets; $i++) {
$pos++;
$length <<= 8;
$length |= ord($data[$pos]);
}
} else {
$length = $temp;
}
$content = substr($data, ++$pos, $length);
$pos += $length;
$res = self::_do_decode($content, $tag, $level);
$result[] = $res;
}
return $result;
}
protected static function _do_decode($content, $tag, $level)
{
$level++;
if ($level > MAXLEVEL) {
return bin2hex($content);
}
switch ($tag) {
case self::TYPE_BOOLEAN:
return (bool)ord($content[0]);
case self::TYPE_INTEGER:
return bin2hex($content);
case self::TYPE_OCTET_STRING:
return bin2hex($content);
case self::TYPE_BIT_STRING:
$padByte = bin2hex($content[0]);
$contentText = bin2hex(substr($content, 1));
return array($padByte, $contentText);
case self::TYPE_SEQUENCE:
case self::TYPE_BIG_SEQUENCE:
case self::TYPE_BIG_SET:
return self::decode($content, $level);
case self::TYPE_UTF8_STRING:
return $content;
case self::TYPE_BMP_STRING:
return extension_loaded("iconv") ? iconv('UCS-2BE', 'UTF-8', $content) : $content;
case self::TYPE_UNIVERSAL_STRING:
return extension_loaded("iconv") ? iconv('UCS-4BE', 'UTF-8', $content) : $content;
case self::TYPE_NULL:
return null;
default: // 其他复杂的不处理了,如有复杂需要,请使用其他的 asn1库处理
return bin2hex($content);
}
}
/**
*
* @param string hex bigint $r
* @param string hex bigint $s
* @return string base64 一般约定签名用bas64, 加解密用hex
*/
public static function rs_2_asn1($r, $s, $outFormat = 'base64')
{
$binR = self::_format_int_pad(hex2bin($r));
$binS = self::_format_int_pad(hex2bin($s));
$lenR = strlen($binR);
$lenS = strlen($binS);
$result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS;
if ($outFormat == 'base64') {
return base64_encode($result);
} else {
return bin2hex($result);
}
}
public static function asn1_2_rs($asn1Str,$inFormat='base64'){
if($inFormat=='base64'){
$bin = base64_decode($asn1Str);
} else {
$bin = hex2bin($asn1Str);
}
$data = self::decode($bin);
$r = gmp_strval(gmp_init($data[0][0],16),16);
$s = gmp_strval(gmp_init($data[0][1],16),16);
return array($r, $s);
}
/**
*
*
* @param string hex bigint $c1x
* @param string hex bigint $c1y
* @param string hex bin $c3
* @param string hex bin $c2
* @return string hex 一般约定签名用bas64, 加解密用hex
*/
public static function asn1_cccc($c1x, $c1y, $c3, $c2, $outFormat = 'hex')
{
$binc1x = self::_format_int_pad($c1x); // c1为椭圆点,得是bigint
$binc1y = self::_format_int_pad($c1y);
$binc3 = hex2bin($c3);
$binc2 = hex2bin($c2);
$c1xEncoded = chr(2) . strlen($binc1x) . $binc1x; // c1x 64 hex
$c1yEncoded = chr(2) . strlen($binc1y) . $binc1y; // c1y 64 hex
$c3Encoded = chr(4) . self::_encode_length(strlen($binc3)) . $binc3; // c3的长度是固定的,可不用长宽度的方式求宽度,但有些就要c2 放前面呢,兼容下
$c2Encoded = chr(4) . self::_encode_length(strlen($binc2)) . $binc2;
$cccc = $c1xEncoded . $c1yEncoded . $c3Encoded . $c2Encoded;
$lenAll = strlen($cccc);
$result = chr(48) . self::_encode_length($lenAll) . $cccc;
if ($outFormat == 'hex') {
return bin2hex($result);
} else {
return base64_encode($result);
}
}
/**
* rs要固定长度,经测试会有小概率出现长度短的,要补0
*
* @param string $hex
* @return string
*/
protected static function _padding_zero($hex)
{
$len = 64; // r,s都是32字节
$left = $len - strlen($hex);
if ($left > 0) {
$hex = str_repeat('0', $left) . $hex;
}
return hex2bin($hex);
}
/**
* 去掉多余的0, int 的话补上必发
*
* @param string $binStr
* @return string
*/
protected static function _format_int_pad($binStr)
{
//trim 0
while (ord($binStr[0]) == 0) {
$binStr = substr($binStr, 1);
}
// add 0 if necessary
if (ord($binStr[0]) > 127) {
$binStr = chr(0) . $binStr;
}
// echo bin2hex($binStr)."\n";
return $binStr;
}
protected static function _encode_length($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Rtgm\smecc\SPLSM2;
class Sm2Ecc {
static function get_params(){
return array(
'p' => gmp_init("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16),
'a' => gmp_init("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16),
'b' => gmp_init("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16),
'n' => gmp_init("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16),
'gx' => gmp_init("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16),
'gy' => gmp_init("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16),
'size'=>256
);
}
}
@@ -0,0 +1,188 @@
<?php
namespace Rtgm\smecc\SPLSM2;
/**
* from https://github.com/lat751608899/sm2/blob/main/src/Point.php
* @author zlq <751608899@qq.com>
* modify by lpilp
* point 类 ,使用zlq童鞋 修改至 ECC的 point 类 Mdanter\Ecc\Primitives\Point
*/
class Sm2Point
{
protected $eccParams;
protected $x;
protected $y;
public function __construct(\GMP $x, \GMP $y)
{
$this->x = $x;
$this->y = $y;
$this->init();
}
protected function init()
{
$eccParams = Sm2Ecc::get_params();
$this->eccParams= $eccParams;
}
public function mul(\GMP $n, $isBase = true)
{
$zero = gmp_init(0, 10);
$n = gmp_mod($n, $this->eccParams['p']);
if (gmp_cmp($n, $zero) === 0) {
return $this->getInfinity();
}
$p = $isBase ? new self($this->eccParams['gx'], $this->eccParams['gy']) : clone $this;
/** @var Point[] $r */
$r = [
$this->getInfinity(), // Q
$p// P
];
$base = gmp_strval(gmp_init(gmp_strval($n), 10), 2);
$n = strrev(str_pad($base, $this->eccParams['size'], '0', STR_PAD_LEFT));
for ($i = 0; $i < $this->eccParams['size']; $i++) {
$j = $n[$i];
if($j == 1){
$r[0] = $r[0]->add($r[1]); // r0 + r1 => p + 0 = p
}
$r[1] = $r[1]->getDouble();
}
$r[0]->checkOnLine();
return $r[0];
}
public function add(Sm2Point $addend)
{
if ($addend->isInfinity()) {
return clone $this;
}
if ($this->isInfinity()) { // 是否是无穷远点
return clone $addend;
}
// x 相等
if (gmp_cmp($addend->getX(), $this->x) === 0) {
// y 也相等 = 倍点
if (gmp_cmp($addend->getY(), $this->y) === 0) {
return $this->getDouble();
} else { // y 不相等 无穷远点
return $this->getInfinity();
}
}
$slope = $this->divMod(// λ = (y2 - y1) / (x2 - x1) (mod p)
gmp_sub($addend->getY(), $this->y), // y2 - y1
gmp_sub($addend->getX(), $this->x) // x2 - x1
);
// λ² - x1 - x2
$xR = $this->subMod(gmp_sub(gmp_pow($slope, 2), $this->x), $addend->getX());
// (λ(x1 - x3)-y1)
$yR = $this->subMod(gmp_mul($slope, gmp_sub($this->x, $xR)), $this->y);
return new self($xR, $yR);
}
public function getDouble()
{
if ($this->isInfinity()) {
return $this->getInfinity();
}
$threeX2 = gmp_mul(gmp_init(3, 10), gmp_pow($this->x, 2)); // 3x²
$tangent = $this->divMod( // λ = (3x² + a) / 2y (mod p)
gmp_add($threeX2, $this->eccParams['a']), // 3x² + a
gmp_mul(gmp_init(2, 10), $this->y) // 2y
);
$x3 = $this->subMod( // λ² - 2x (mod p)
gmp_pow($tangent, 2), // λ²
gmp_mul(gmp_init(2, 10), $this->x) // 2x
);
$y3 = $this->subMod( // λ(x - x3)-y (mod p)
gmp_mul($tangent, gmp_sub($this->x, $x3)), // λ(x - x3)
$this->y
);
return new self($x3, $y3);
}
public function getInfinity()
{
return new self(gmp_init(0,10), gmp_init(0,10));
}
/**
* @return \GMP
*/
public function getX()
{
return $this->x;
}
/**
* @return \GMP
*/
public function getY()
{
return $this->y;
}
public function isInfinity()
{
return gmp_cmp($this->x, gmp_init(0,10)) === 0
&& gmp_cmp($this->y, gmp_init(0,10)) === 0;
}
/**
* // k ≡ (x/y) (mod n) => ky ≡ x (mod n) => k y/x ≡ 1 (mod n)
* @param $x
* @param $y
* @param null $n
* @return \GMP|resource
*/
protected function divMod($x, $y, $n = null)
{
$n = $n?:$this->eccParams['p'];
// y k ≡ 1 (mod n) => k ≡ 1/y (mod n)
$k = gmp_invert($y, $n);
// kx ≡ x/y (mod n)
$kx = gmp_mul($x, $k);
return gmp_mod($kx, $n);
}
protected function subMod($x, $y, $n = null)
{
return gmp_mod(gmp_sub($x, $y), $n?:$this->eccParams['p']);
}
public function contains(\GMP $x, \GMP $y)
{
$eq_zero = gmp_cmp(
$this->subMod(
gmp_pow($y, 2),
gmp_add(
gmp_add(
gmp_pow($x, 3),
gmp_mul($this->eccParams['a'], $x)
),
$this->eccParams['b']
)
),
gmp_init(0, 10)
);
return $eq_zero;
}
public function checkOnLine()
{
if($this->contains($this->x, $this->y) !== 0){
throw new \Exception('Invalid point');
}
return true;
}
}
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace Rtgm\smecc\SPLSM2;
/**
* 在5.x的版本中不支持openssl系列的sm3 使用lizhichao童鞋的sm3
*
* https://github.com/lizhichao/sm/blob/master/src/Sm3.php
* @auth lizhichao
*/
class Sm3
{
private $IV = '7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e';
private $LEN = 512;
private $STR_LEN = 64;
public function digest($str,$raw = false)
{
$l = strlen($str) * 8;
$k = $this->getK($l);
$bt = $this->getB($k);
$str = $str . $bt . pack('J', $l);
$count = strlen($str);
$l = $count / $this->STR_LEN;
$vr = hex2bin($this->IV);
for ($i = 0; $i < $l; $i++) {
$vr = $this->cf($vr, substr($str, $i * $this->STR_LEN, $this->STR_LEN));
}
if($raw){
return $vr;
}
return bin2hex($vr);
}
private function getK($l)
{
$v = $l % $this->LEN;
return $v + $this->STR_LEN < $this->LEN
? $this->LEN - $this->STR_LEN - $v - 1
: ($this->LEN * 2) - $this->STR_LEN - $v - 1;
}
private function getB($k)
{
$arg = [128];
$arg = array_merge($arg, array_fill(0, intval($k / 8), 0));
return pack('C*', ...$arg);
}
public function signFile($file)
{
$l = filesize($file) * 8;
$k = $this->getK($l);
$bt = $this->getB($k) . pack('J', $l);
$hd = fopen($file, 'r');
$vr = hex2bin($this->IV);
$str = fread($hd, $this->STR_LEN);
if ($l > $this->LEN - $this->STR_LEN - 1) {
do {
$vr = $this->cf($vr, $str);
$str = fread($hd, $this->STR_LEN);
} while (!feof($hd));
}
$str = $str . $bt;
$count = strlen($str) * 8;
$l = $count / $this->LEN;
for ($i = 0; $i < $l; $i++) {
$vr = $this->cf($vr, substr($str, $i * $this->STR_LEN, $this->STR_LEN));
}
return bin2hex($vr);
}
private function t($i)
{
return $i < 16 ? 0x79cc4519 : 0x7a879d8a;
}
private function cf($ai, $bi)
{
$wr = array_values(unpack('N*', $bi));
for ($i = 16; $i < 68; $i++) {
$wr[$i] = $this->p1($wr[$i - 16]
^
$wr[$i - 9]
^
$this->lm($wr[$i - 3], 15))
^
$this->lm($wr[$i - 13], 7)
^
$wr[$i - 6];
}
$wr1 = [];
for ($i = 0; $i < 64; $i++) {
$wr1[] = $wr[$i] ^ $wr[$i + 4];
}
list($a, $b, $c, $d, $e, $f, $g, $h) = array_values(unpack('N*', $ai));
for ($i = 0; $i < 64; $i++) {
$ss1 = $this->lm(
($this->lm($a, 12) + $e + $this->lm($this->t($i), $i % 32) & 0xffffffff),
7);
$ss2 = $ss1 ^ $this->lm($a, 12);
$tt1 = ($this->ff($i, $a, $b, $c) + $d + $ss2 + $wr1[$i]) & 0xffffffff;
$tt2 = ($this->gg($i, $e, $f, $g) + $h + $ss1 + $wr[$i]) & 0xffffffff;
$d = $c;
$c = $this->lm($b, 9);
$b = $a;
$a = $tt1;
$h = $g;
$g = $this->lm($f, 19);
$f = $e;
$e = $this->p0($tt2);
}
return pack('N*', $a, $b, $c, $d, $e, $f, $g, $h) ^ $ai;
}
private function ff($j, $x, $y, $z)
{
return $j < 16 ? $x ^ $y ^ $z : ($x & $y) | ($x & $z) | ($y & $z);
}
private function gg($j, $x, $y, $z)
{
return $j < 16 ? $x ^ $y ^ $z : ($x & $y) | (~$x & $z);
}
private function lm($a, $n)
{
return ($a >> (32 - $n) | (($a << $n) & 0xffffffff));
}
private function p0($x)
{
return $x ^ $this->lm($x, 9) ^ $this->lm($x, 17);
}
private function p1($x)
{
return $x ^ $this->lm($x, 15) ^ $this->lm($x, 23);
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace Rtgm\util;
class FormatSign
{
public function run($sign)
{
list($binR, $binS) = $this->_decode_rs(base64_decode($sign));
$binR = $this->_trim_int_pad($binR);
$binS = $this->_trim_int_pad($binS);
$lenR = strlen($binR);
$lenS = strlen($binS);
$result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS;
return base64_encode($result);
}
/**
*
*
* @return string
*/
/**
* 招行的解签,没有用标准的asn1解析函数,当出现r,s的位数不足的时候就报错了,只支持rs, 31,32字节,当字节数少时强制补0吧
*
* @param string $sign
* @return string
*/
public function format_cmbc($sign){
list($binR, $binS) = $this->_decode_rs(base64_decode($sign));
while(strlen($binR)<32){
$binR = chr(0).$binR;
}
while(strlen($binS)<32){
$binS = chr(0).$binS;
}
$lenR = strlen($binR);
$lenS = strlen($binS);
$result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS;
return base64_encode($result);
}
private function _trim_int_pad($binStr)
{
// echo bin2hex($binStr)."\n";
//trim 0
while(ord($binStr[0])==0){
$binStr = substr($binStr,1);
}
// add 0 if necessary
if(ord($binStr[0])>127){
$binStr = chr(0).$binStr;
}
// echo bin2hex($binStr)."\n";
return $binStr;
}
private function _decode_rs($binSign)
{
$rLen = ord($binSign[3]);
$binR = substr($binSign, 4, $rLen);
$binS = substr($binSign, (4 + $rLen + 2));
// echo bin2hex($binR) . "\n----------\n" . bin2hex($binS) . "\n";
return [$binR, $binS];
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace Rtgm\util;
class KeyCompress {
/**
* sm2压缩公钥计算全公钥
*
* @param string $compressedKey
* @return string
*/
public static function decompressPublicKey($compressedKey)
{
// 获取压缩标志和X坐标
$flag = substr($compressedKey, 0, 2);
$x = substr($compressedKey, 2);
// 将16进制字符串转换为大整数
$p = gmp_init('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16);
$a = gmp_init('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16);
$b = gmp_init('28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16);
// 计算Y坐标
// y2 = x3 + ax + b
$x = gmp_init($x, 16);
$alpha = gmp_powm($x, 3, $p);
$beta = gmp_add(gmp_mod(gmp_mul($a, $x), $p), $b);
$y2 = gmp_mod(gmp_add($alpha, $beta), $p);
$y = gmp_powm($y2, gmp_div_q(gmp_add($p, 1), 4), $p);
// y2 的值开方有两个值,根据奇偶判定取哪一个
if ($flag == "02") {
// 如果压缩标志为“02”,则Y坐标为偶数
if (gmp_strval(gmp_mod($y, 2)) == "0") {
return "04" . gmp_strval($x, 16) . str_pad(gmp_strval($y, 16), 64, "0", STR_PAD_LEFT);
} else {
$y = gmp_sub($p, $y);
return "04" . gmp_strval($x, 16) . str_pad(gmp_strval($y, 16), 64, "0", STR_PAD_LEFT);
}
} elseif ($flag == "03") {
// 如果压缩标志为“03”,则Y坐标为奇数
if (gmp_strval(gmp_mod($y, 2)) == "1") {
return "04" . gmp_strval($x, 16) . str_pad(gmp_strval($y, 16), 64, "0", STR_PAD_LEFT);
} else {
$y = gmp_sub($p, $y);
return "04" . gmp_strval($x, 16) . str_pad(gmp_strval($y, 16), 64, "0", STR_PAD_LEFT);
}
} else {
return null;
}
}
/**
* 压缩公钥
*
* @param string $publicKey
* @return string
*/
public static function compressPublicKey($publicKey)
{
if (strlen($publicKey) == 130) {
$publicKey = substr($publicKey, 2);
}
// 将16进制字符串转换为GMP数值
$x = gmp_init(substr($publicKey, 0, 64), 16);
$y = gmp_init(substr($publicKey, 64, 64), 16);
// 判断Y坐标奇偶性
if (gmp_strval(gmp_mod($y, 2)) == "0") {
$flag = "02";
} else {
$flag = "03";
}
// 拼接压缩后的公钥
$compressedPublicKey = $flag . str_pad(gmp_strval($x, 16), 64, "0", STR_PAD_LEFT);
return $compressedPublicKey;
}
}
+647
View File
@@ -0,0 +1,647 @@
<?php
namespace Rtgm\util;
use FG\ASN1\ASNObject;
use FG\ASN1\Identifier;
class MyAsn1
{
const CLASS_UNIVERSAL = 0;
const CLASS_APPLICATION = 1;
const CLASS_CONTEXT_SPECIFIC = 2;
const CLASS_PRIVATE = 3;
const TYPE_BOOLEAN = 1;
const TYPE_INTEGER = 2;
const TYPE_BIT_STRING = 3;
const TYPE_OCTET_STRING = 4;
const TYPE_NULL = 5;
const TYPE_OBJECT_IDENTIFIER = 6;
const TYPE_OBJECT_DESCRIPTOR = 7;
const TYPE_INSTANCE_OF = 8; // EXTERNAL
const TYPE_REAL = 9;
const TYPE_ENUMERATED = 10;
const TYPE_EMBEDDED = 11;
const TYPE_UTF8_STRING = 12;
const TYPE_RELATIVE_OID = 13;
const TYPE_SEQUENCE = 16; // SEQUENCE OF
const TYPE_SET = 17; // SET OF
const TYPE_NUMERIC_STRING = 18;
const TYPE_PRINTABLE_STRING = 19;
const TYPE_TELETEX_STRING = 20; // T61String
const TYPE_VIDEOTEX_STRING = 21;
const TYPE_IA5_STRING = 22;
const TYPE_UTC_TIME = 23;
const TYPE_GENERALIZED_TIME = 24;
const TYPE_GRAPHIC_STRING = 25;
const TYPE_VISIBLE_STRING = 26; // ISO646String
const TYPE_GENERAL_STRING = 27;
const TYPE_UNIVERSAL_STRING = 28;
const TYPE_CHARACTER_STRING = 29;
const TYPE_BMP_STRING = 30;
const TYPE_CHOICE = -1;
const TYPE_ANY = -2;
const TYPE_ANY_RAW = -3;
const TYPE_ANY_SKIP = -4;
const TYPE_ANY_DER = -5;
public static function decode_file($pemfile)
{
$data = self::pem2der(file_get_contents($pemfile));
// var_dump($data);
return self::decode($data);
}
public static function decode($data, $format = 'bin')
{
if ($format == 'base64') {
$data = base64_decode($data);
} else if ($format == 'hex') {
$data = hex2bin($data);
}
$asnObject = ASNObject::fromBinary($data);
return self::printObject($asnObject);
}
public static function printObject(ASNObject $object, $depth = 0)
{
$content = $object->getContent();
if (is_array($content)) {
$result = array();
foreach ($object as $child) {
$rs = self::printObject($child, $depth + 1);
$result[] = $rs;
}
return $result;
} else {
$type = $object->getType();
// $strval = $object->__toString();
// 如果是 oid的话,tostring时是取的oidText, PHPasn1没有sm2等相关的就会去调用 http://oid-info.com/get/{$oidString}的接口,然后超时0.5秒
// 这里相当于直接取get_contents(), 也是可以的
$strval = $content;
if ($type == 6) { //oid
$rt = self::OIDtoText($strval);
} else if ($type == 2) {
$rt = self::format_bigint($strval);
}
// else if($type==4){
// if(substr($strval,0,2)=='30') { //可以再分解
// return self::decode($strval, 'hex');
// } else {
// $rt = $strval;
// }
// }
else {
$rt = $strval;
}
return $rt;
// $name = Identifier::getShortName($type);
// $name = str_replace(" ", "-", $name);
// return "{$name}($type)_$rt";
}
}
public static function printObject2(ASNObject $object, $depth = 0)
{
$treeSymbol = '';
$depthString = str_repeat('─', $depth);
if ($depth > 0) {
$treeSymbol = '├';
}
// $type = $object->getType();
$name = Identifier::getShortName($object->getType());
echo "{$treeSymbol}{$depthString}{$name}: ";
$strval = $object->__toString();
$result[] = $strval;
echo $object->__toString() . PHP_EOL;
$content = $object->getContent();
// print_R($content);
if (is_array($content)) {
foreach ($object as $child) {
self::printObject2($child, $depth + 1);
}
}
}
protected static function pem2der($pem_data)
{
$begin = "-----";
$end = "-----END";
$pem_data = substr($pem_data, strpos($pem_data, $begin, 6) + strlen($begin));
$pem_data = substr($pem_data, 0, strpos($pem_data, $end));
$der = base64_decode($pem_data);
return $der;
}
/**
* 大数都转成16进制
*
* @param bigint|string $data
* @return string
*/
protected static function format_bigint($data)
{
$hex = gmp_strval(gmp_init($data, 10), 16);
return self::padding_one_zero($hex);
}
public static function padding_one_zero($hex)
{
if (strlen($hex) % 2 == 1) {
$hex = '0' . $hex;
}
return $hex;
}
public static function padding_zero($hex, $len = 64)
{
$left = $len - strlen($hex);
if ($left > 0) {
$hex = str_repeat('0', $left) . $hex;
}
return $hex;
}
/**
* from https://github.com/vakata/asn1
*
* @var array
*/
public static $oids = array(
'sm2' => '1.2.156.10197.1.301',
'sm3WithSM2Encryption' => '1.2.156.10197.1.501',
'sha1' => '1.3.14.3.2.26',
'sha256' => '2.16.840.1.101.3.4.2.1',
'sha384' => '2.16.840.1.101.3.4.2.2',
'sha512' => '2.16.840.1.101.3.4.2.3',
'sha224' => '2.16.840.1.101.3.4.2.4',
'md5' => '1.2.840.113549.2.5',
'md2' => '1.3.14.7.2.2.1',
'ripemd160' => '1.3.36.3.2.1',
'MD4withRSA' => '1.2.840.113549.1.1.3',
'SHA1withECDSA' => '1.2.840.10045.4.1',
'SHA224withECDSA' => '1.2.840.10045.4.3.1',
'SHA256withECDSA' => '1.2.840.10045.4.3.2',
'SHA384withECDSA' => '1.2.840.10045.4.3.3',
'SHA512withECDSA' => '1.2.840.10045.4.3.4',
'dsa' => '1.2.840.10040.4.1',
'SHA1withDSA' => '1.2.840.10040.4.3',
'SHA224withDSA' => '2.16.840.1.101.3.4.3.1',
'SHA256withDSA' => '2.16.840.1.101.3.4.3.2',
'rsaEncryption' => '1.2.840.113549.1.1.1',
'countryName' => '2.5.4.6',
'organization' => '2.5.4.10',
'organizationalUnit' => '2.5.4.11',
'stateOrProvinceName' => '2.5.4.8',
'locality' => '2.5.4.7',
'commonName' => '2.5.4.3',
'subjectKeyIdentifier' => '2.5.29.14',
'keyUsage' => '2.5.29.15',
'subjectAltName' => '2.5.29.17',
'basicConstraints' => '2.5.29.19',
'nameConstraints' => '2.5.29.30',
'cRLDistributionPoints' => '2.5.29.31',
'certificatePolicies' => '2.5.29.32',
'authorityKeyIdentifier' => '2.5.29.35',
'policyConstraints' => '2.5.29.36',
'extKeyUsage' => '2.5.29.37',
'authorityInfoAccess' => '1.3.6.1.5.5.7.1.1',
'anyExtendedKeyUsage' => '2.5.29.37.0',
'serverAuth' => '1.3.6.1.5.5.7.3.1',
'clientAuth' => '1.3.6.1.5.5.7.3.2',
'codeSigning' => '1.3.6.1.5.5.7.3.3',
'emailProtection' => '1.3.6.1.5.5.7.3.4',
'timeStamping' => '1.3.6.1.5.5.7.3.8',
'ocspSigning' => '1.3.6.1.5.5.7.3.9',
'ecPublicKey' => '1.2.840.10045.2.1',
'secp256r1' => '1.2.840.10045.3.1.7',
'secp256k1' => '1.3.132.0.10',
'secp384r1' => '1.3.132.0.34',
'pkcs5PBES2' => '1.2.840.113549.1.5.13',
'pkcs5PBKDF2' => '1.2.840.113549.1.5.12',
'des-EDE3-CBC' => '1.2.840.113549.3.7',
'data' => '1.2.840.113549.1.7.1', // CMS data
'signed-data' => '1.2.840.113549.1.7.2', // CMS signed-data
'enveloped-data' => '1.2.840.113549.1.7.3', // CMS enveloped-data
'digested-data' => '1.2.840.113549.1.7.5', // CMS digested-data
'encrypted-data' => '1.2.840.113549.1.7.6', // CMS encrypted-data
'authenticated-data' => '1.2.840.113549.1.9.16.1.2', // CMS authenticated-data
'tstinfo' => '1.2.840.113549.1.9.16.1.4', // RFC3161 TSTInfo,
'pkix' => '1.3.6.1.5.5.7',
'pe' => '1.3.6.1.5.5.7.1',
'qt' => '1.3.6.1.5.5.7.2',
'kp' => '1.3.6.1.5.5.7.3',
'ad' => '1.3.6.1.5.5.7.48',
'cps' => '1.3.6.1.5.5.7.2.1',
'unotice' => '1.3.6.1.5.5.7.2.2',
'ocsp' => '1.3.6.1.5.5.7.48.1',
'caIssuers' => '1.3.6.1.5.5.7.48.2',
'timeStamping' => '1.3.6.1.5.5.7.48.3',
'caRepository' => '1.3.6.1.5.5.7.48.5',
'at' => '2.5.4',
'name' => '2.5.4.41',
'surname' => '2.5.4.4',
'givenName' => '2.5.4.42',
'initials' => '2.5.4.43',
'generationQualifier' => '2.5.4.44',
'commonName' => '2.5.4.3',
'localityName' => '2.5.4.7',
'stateOrProvinceName' => '2.5.4.8',
'organizationName' => '2.5.4.10',
'organizationalUnitName' => '2.5.4.11',
'title' => '2.5.4.12',
'description' => '2.5.4.13',
'dnQualifier' => '2.5.4.46',
'countryName' => '2.5.4.6',
'serialNumber' => '2.5.4.5',
'pseudonym' => '2.5.4.65',
'postalCode' => '2.5.4.17',
'streetAddress' => '2.5.4.9',
'uniqueIdentifier' => '2.5.4.45',
'role' => '2.5.4.72',
'postalAddress' => '2.5.4.16',
'domainComponent' => '0.9.2342.19200300.100.1.25',
'pkcs-9' => '1.2.840.113549.1.9',
'emailAddress' => '1.2.840.113549.1.9.1',
'ce' => '2.5.29',
'authorityKeyIdentifier' => '2.5.29.35',
'subjectKeyIdentifier' => '2.5.29.14',
'keyUsage' => '2.5.29.15',
'privateKeyUsagePeriod' => '2.5.29.16',
'certificatePolicies' => '2.5.29.32',
'anyPolicy' => '2.5.29.32.0',
'policyMappings' => '2.5.29.33',
'subjectAltName' => '2.5.29.17',
'issuerAltName' => '2.5.29.18',
'subjectDirectoryAttributes' => '2.5.29.9',
'basicConstraints' => '2.5.29.19',
'nameConstraints' => '2.5.29.30',
'policyConstraints' => '2.5.29.36',
'cRLDistributionPoints' => '2.5.29.31',
'extKeyUsage' => '2.5.29.37',
'anyExtendedKeyUsage' => '2.5.29.37.0',
'kp-serverAuth' => '1.3.6.1.5.5.7.3.1',
'kp-clientAuth' => '1.3.6.1.5.5.7.3.2',
'kp-codeSigning' => '1.3.6.1.5.5.7.3.3',
'kp-emailProtection' => '1.3.6.1.5.5.7.3.4',
'kp-timeStamping' => '1.3.6.1.5.5.7.3.8',
'kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9',
'inhibitAnyPolicy' => '2.5.29.54',
'freshestCRL' => '2.5.29.46',
'pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1',
'pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11',
'cRLNumber' => '2.5.29.20',
'issuingDistributionPoint' => '2.5.29.28',
'deltaCRLIndicator' => '2.5.29.27',
'cRLReasons' => '2.5.29.21',
'certificateIssuer' => '2.5.29.29',
'holdInstructionCode' => '2.5.29.23',
'holdInstruction' => '1.2.840.10040.2',
'holdinstruction-none' => '1.2.840.10040.2.1',
'holdinstruction-callissuer' => '1.2.840.10040.2.2',
'holdinstruction-reject' => '1.2.840.10040.2.3',
'invalidityDate' => '2.5.29.24',
'md2' => '1.2.840.113549.2.2',
'md5' => '1.2.840.113549.2.5',
'sha1' => '1.3.14.3.2.26',
'dsa' => '1.2.840.10040.4.1',
'dsa-with-sha1' => '1.2.840.10040.4.3',
'pkcs-1' => '1.2.840.113549.1.1',
'rsaEncryption' => '1.2.840.113549.1.1.1',
'md2WithRSAEncryption' => '1.2.840.113549.1.1.2',
'md5WithRSAEncryption' => '1.2.840.113549.1.1.4',
'sha1WithRSAEncryption' => ['1.2.840.113549.1.1.5', '1.3.14.3.2.29'],
'dhpublicnumber' => '1.2.840.10046.2.1',
'keyExchangeAlgorithm' => '2.16.840.1.101.2.1.1.22',
'ansi-X9-62' => '1.2.840.10045',
'ecSigType' => '1.2.840.10045.4',
'ecdsa-with-SHA1' => '1.2.840.10045.4.1',
'fieldType' => '1.2.840.10045.1',
'prime-field' => '1.2.840.10045.1.1',
'characteristic-two-field' => '1.2.840.10045.1.2',
'characteristic-two-basis' => '1.2.840.10045.1.2.3',
'gnBasis' => '1.2.840.10045.1.2.3.1',
'tpBasis' => '1.2.840.10045.1.2.3.2',
'ppBasis' => '1.2.840.10045.1.2.3.3',
'publicKeyType' => '1.2.840.10045.2',
'ecPublicKey' => '1.2.840.10045.2.1',
'ellipticCurve' => '1.2.840.10045.3',
'c-TwoCurve' => '1.2.840.10045.3.0',
'c2pnb163v1' => '1.2.840.10045.3.0.1',
'c2pnb163v2' => '1.2.840.10045.3.0.2',
'c2pnb163v3' => '1.2.840.10045.3.0.3',
'c2pnb176w1' => '1.2.840.10045.3.0.4',
'c2pnb191v1' => '1.2.840.10045.3.0.5',
'c2pnb191v2' => '1.2.840.10045.3.0.6',
'c2pnb191v3' => '1.2.840.10045.3.0.7',
'c2pnb191v4' => '1.2.840.10045.3.0.8',
'c2pnb191v5' => '1.2.840.10045.3.0.9',
'c2pnb208w1' => '1.2.840.10045.3.0.10',
'c2pnb239v1' => '1.2.840.10045.3.0.11',
'c2pnb239v2' => '1.2.840.10045.3.0.12',
'c2pnb239v3' => '1.2.840.10045.3.0.13',
'c2pnb239v4' => '1.2.840.10045.3.0.14',
'c2pnb239v5' => '1.2.840.10045.3.0.15',
'c2pnb272w1' => '1.2.840.10045.3.0.16',
'c2pnb304w1' => '1.2.840.10045.3.0.17',
'c2pnb359v1' => '1.2.840.10045.3.0.18',
'c2pnb368w1' => '1.2.840.10045.3.0.19',
'c2pnb431r1' => '1.2.840.10045.3.0.20',
'primeCurve' => '1.2.840.10045.3.1',
'prime192v1' => '1.2.840.10045.3.1.1',
'prime192v2' => '1.2.840.10045.3.1.2',
'prime192v3' => '1.2.840.10045.3.1.3',
'prime239v1' => '1.2.840.10045.3.1.4',
'prime239v2' => '1.2.840.10045.3.1.5',
'prime239v3' => '1.2.840.10045.3.1.6',
'prime256v1' => '1.2.840.10045.3.1.7',
'RSAES-OAEP' => '1.2.840.113549.1.1.7',
'pSpecified' => '1.2.840.113549.1.1.9',
'RSASSA-PSS' => '1.2.840.113549.1.1.10',
'mgf1' => '1.2.840.113549.1.1.8',
'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14',
'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11',
'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12',
'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13',
'sha224' => '2.16.840.1.101.3.4.2.4',
'sha256' => '2.16.840.1.101.3.4.2.1',
'sha384' => '2.16.840.1.101.3.4.2.2',
'sha512' => '2.16.840.1.101.3.4.2.3',
'GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4',
'GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3',
'GostR3410-2001' => '1.2.643.2.2.20',
'GostR3410-94' => '1.2.643.2.2.19',
'netscape' => '2.16.840.1.113730',
'netscape-cert-extension' => '2.16.840.1.113730.1',
'netscape-cert-type' => '2.16.840.1.113730.1.1',
'netscape-comment' => '2.16.840.1.113730.1.13',
'netscape-ca-policy-url' => '2.16.840.1.113730.1.8',
'logotype' => '1.3.6.1.5.5.7.1.12',
'entrustVersInfo' => '1.2.840.113533.7.65.0',
'verisignPrivate' => '2.16.840.1.113733.1.6.9',
'unstructuredName' => '1.2.840.113549.1.9.2',
'challengePassword' => '1.2.840.113549.1.9.7',
'extensionRequest' => '1.2.840.113549.1.9.14',
'userid' => '0.9.2342.19200300.100.1.1',
's/mime' => '1.2.840.113549.1.9.15',
'unstructuredAddress' => '1.2.840.113549.1.9.8',
'rc2-cbc' => '1.2.840.113549.3.2',
'rc4' => '1.2.840.113549.3.4',
'desCBC' => '1.3.14.3.2.7',
'qcStatements' => '1.3.6.1.5.5.7.1.3',
'pkixQCSyntax-v1' => '1.3.6.1.5.5.7.11.1',
'pkixQCSyntax-v2' => '1.3.6.1.5.5.7.11.2',
'ipsecEndSystem' => '1.3.6.1.5.5.7.3.5',
'ipsecTunnel' => '1.3.6.1.5.5.7.3.6',
'ipsecUser' => '1.3.6.1.5.5.7.3.7',
'OCSP' => '1.3.6.1.5.5.7.48.1',
'countryOfCitizenship' => '1.3.6.1.5.5.7.9.4',
'IPSECProtection' => '1.3.6.1.5.5.8.2.2',
'telephoneNumber' => '2.5.4.20',
'organizationIdentifier' => '2.5.4.97',
);
public static $oidTexts = array(
'1.2.156.10197.1.301' => 'sm2',
'1.2.156.10197.1.501' => 'sm3WithSM2Encryption',
'1.3.14.3.2.26' => 'sha1',
'2.16.840.1.101.3.4.2.1' => 'sha256',
'2.16.840.1.101.3.4.2.2' => 'sha384',
'2.16.840.1.101.3.4.2.3' => 'sha512',
'2.16.840.1.101.3.4.2.4' => 'sha224',
'1.2.840.113549.2.5' => 'md5',
'1.2.840.113549.2.2' => 'md2',
'1.3.36.3.2.1' => 'ripemd160',
'1.2.840.113549.1.1.3' => 'MD4withRSA',
'1.2.840.10045.4.1' => 'SHA1withECDSA',
'1.2.840.10045.4.3.1' => 'SHA224withECDSA',
'1.2.840.10045.4.3.2' => 'SHA256withECDSA',
'1.2.840.10045.4.3.3' => 'SHA384withECDSA',
'1.2.840.10045.4.3.4' => 'SHA512withECDSA',
'1.2.840.10040.4.1' => 'dsa',
'1.2.840.10040.4.3' => 'SHA1withDSA',
'2.16.840.1.101.3.4.3.1' => 'SHA224withDSA',
'2.16.840.1.101.3.4.3.2' => 'SHA256withDSA',
'1.2.840.113549.1.1.1' => 'rsaEncryption',
'2.5.4.6' => 'countryName',
'2.5.4.10' => 'organization',
'2.5.4.11' => 'organizationalUnit',
'2.5.4.8' => 'stateOrProvinceName',
'2.5.4.7' => 'locality',
'2.5.4.3' => 'commonName',
'2.5.29.14' => 'subjectKeyIdentifier',
'2.5.29.15' => 'keyUsage',
'2.5.29.17' => 'subjectAltName',
'2.5.29.19' => 'basicConstraints',
'2.5.29.30' => 'nameConstraints',
'2.5.29.31' => 'cRLDistributionPoints',
'2.5.29.32' => 'certificatePolicies',
'2.5.29.35' => 'authorityKeyIdentifier',
'2.5.29.36' => 'policyConstraints',
'2.5.29.37' => 'extKeyUsage',
'1.3.6.1.5.5.7.1.1' => 'authorityInfoAccess',
'2.5.29.37.0' => 'anyExtendedKeyUsage',
'1.3.6.1.5.5.7.3.1' => 'serverAuth',
'1.3.6.1.5.5.7.3.2' => 'clientAuth',
'1.3.6.1.5.5.7.3.3' => 'codeSigning',
'1.3.6.1.5.5.7.3.4' => 'emailProtection',
'1.3.6.1.5.5.7.48.3' => 'timeStamping',
'1.3.6.1.5.5.7.3.9' => 'ocspSigning',
'1.2.840.10045.2.1' => 'ecPublicKey',
'1.2.840.10045.3.1.7' => 'secp256r1',
'1.3.132.0.10' => 'secp256k1',
'1.3.132.0.34' => 'secp384r1',
'1.2.840.113549.1.5.13' => 'pkcs5PBES2',
'1.2.840.113549.1.5.12' => 'pkcs5PBKDF2',
'1.2.840.113549.3.7' => 'des-EDE3-CBC',
'1.2.840.113549.1.7.1' => 'data',
'1.2.840.113549.1.7.2' => 'signed-data',
'1.2.840.113549.1.7.3' => 'enveloped-data',
'1.2.840.113549.1.7.5' => 'digested-data',
'1.2.840.113549.1.7.6' => 'encrypted-data',
'1.2.840.113549.1.9.16.1.2' => 'authenticated-data',
'1.2.840.113549.1.9.16.1.4' => 'tstinfo',
'1.3.6.1.5.5.7' => 'pkix',
'1.3.6.1.5.5.7.1' => 'pe',
'1.3.6.1.5.5.7.2' => 'qt',
'1.3.6.1.5.5.7.3' => 'kp',
'1.3.6.1.5.5.7.48' => 'ad',
'1.3.6.1.5.5.7.2.1' => 'cps',
'1.3.6.1.5.5.7.2.2' => 'unotice',
'1.3.6.1.5.5.7.48.1' => 'ocsp',
'1.3.6.1.5.5.7.48.2' => 'caIssuers',
'1.3.6.1.5.5.7.48.5' => 'caRepository',
'2.5.4' => 'at',
'2.5.4.41' => 'name',
'2.5.4.4' => 'surname',
'2.5.4.42' => 'givenName',
'2.5.4.43' => 'initials',
'2.5.4.44' => 'generationQualifier',
'2.5.4.7' => 'localityName',
'2.5.4.10' => 'organizationName',
'2.5.4.11' => 'organizationalUnitName',
'2.5.4.12' => 'title',
'2.5.4.13' => 'description',
'2.5.4.46' => 'dnQualifier',
'2.5.4.5' => 'serialNumber',
'2.5.4.65' => 'pseudonym',
'2.5.4.17' => 'postalCode',
'2.5.4.9' => 'streetAddress',
'2.5.4.45' => 'uniqueIdentifier',
'2.5.4.72' => 'role',
'2.5.4.16' => 'postalAddress',
'0.9.2342.19200300.100.1.25' => 'domainComponent',
'1.2.840.113549.1.9' => 'pkcs-9',
'1.2.840.113549.1.9.1' => 'emailAddress',
'2.5.29' => 'ce',
'2.5.29.16' => 'privateKeyUsagePeriod',
'2.5.29.32.0' => 'anyPolicy',
'2.5.29.33' => 'policyMappings',
'2.5.29.18' => 'issuerAltName',
'2.5.29.9' => 'subjectDirectoryAttributes',
'1.3.6.1.5.5.7.3.1' => 'kp-serverAuth',
'1.3.6.1.5.5.7.3.2' => 'kp-clientAuth',
'1.3.6.1.5.5.7.3.3' => 'kp-codeSigning',
'1.3.6.1.5.5.7.3.4' => 'kp-emailProtection',
'1.3.6.1.5.5.7.3.8' => 'kp-timeStamping',
'1.3.6.1.5.5.7.3.9' => 'kp-OCSPSigning',
'2.5.29.54' => 'inhibitAnyPolicy',
'2.5.29.46' => 'freshestCRL',
'1.3.6.1.5.5.7.1.1' => 'pe-authorityInfoAccess',
'1.3.6.1.5.5.7.1.11' => 'pe-subjectInfoAccess',
'2.5.29.20' => 'cRLNumber',
'2.5.29.28' => 'issuingDistributionPoint',
'2.5.29.27' => 'deltaCRLIndicator',
'2.5.29.21' => 'cRLReasons',
'2.5.29.29' => 'certificateIssuer',
'2.5.29.23' => 'holdInstructionCode',
'1.2.840.10040.2' => 'holdInstruction',
'1.2.840.10040.2.1' => 'holdinstruction-none',
'1.2.840.10040.2.2' => 'holdinstruction-callissuer',
'1.2.840.10040.2.3' => 'holdinstruction-reject',
'2.5.29.24' => 'invalidityDate',
'1.2.840.10040.4.3' => 'dsa-with-sha1',
'1.2.840.113549.1.1' => 'pkcs-1',
'1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
'1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
'1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
'1.3.14.3.2.29' => 'sha1WithRSAEncryption',
'1.2.840.10046.2.1' => 'dhpublicnumber',
'2.16.840.1.101.2.1.1.22' => 'keyExchangeAlgorithm',
'1.2.840.10045' => 'ansi-X9-62',
'1.2.840.10045.4' => 'ecSigType',
'1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
'1.2.840.10045.1' => 'fieldType',
'1.2.840.10045.1.1' => 'prime-field',
'1.2.840.10045.1.2' => 'characteristic-two-field',
'1.2.840.10045.1.2.3' => 'characteristic-two-basis',
'1.2.840.10045.1.2.3.1' => 'gnBasis',
'1.2.840.10045.1.2.3.2' => 'tpBasis',
'1.2.840.10045.1.2.3.3' => 'ppBasis',
'1.2.840.10045.2' => 'publicKeyType',
'1.2.840.10045.3' => 'ellipticCurve',
'1.2.840.10045.3.0' => 'c-TwoCurve',
'1.2.840.10045.3.0.1' => 'c2pnb163v1',
'1.2.840.10045.3.0.2' => 'c2pnb163v2',
'1.2.840.10045.3.0.3' => 'c2pnb163v3',
'1.2.840.10045.3.0.4' => 'c2pnb176w1',
'1.2.840.10045.3.0.5' => 'c2pnb191v1',
'1.2.840.10045.3.0.6' => 'c2pnb191v2',
'1.2.840.10045.3.0.7' => 'c2pnb191v3',
'1.2.840.10045.3.0.8' => 'c2pnb191v4',
'1.2.840.10045.3.0.9' => 'c2pnb191v5',
'1.2.840.10045.3.0.10' => 'c2pnb208w1',
'1.2.840.10045.3.0.11' => 'c2pnb239v1',
'1.2.840.10045.3.0.12' => 'c2pnb239v2',
'1.2.840.10045.3.0.13' => 'c2pnb239v3',
'1.2.840.10045.3.0.14' => 'c2pnb239v4',
'1.2.840.10045.3.0.15' => 'c2pnb239v5',
'1.2.840.10045.3.0.16' => 'c2pnb272w1',
'1.2.840.10045.3.0.17' => 'c2pnb304w1',
'1.2.840.10045.3.0.18' => 'c2pnb359v1',
'1.2.840.10045.3.0.19' => 'c2pnb368w1',
'1.2.840.10045.3.0.20' => 'c2pnb431r1',
'1.2.840.10045.3.1' => 'primeCurve',
'1.2.840.10045.3.1.1' => 'prime192v1',
'1.2.840.10045.3.1.2' => 'prime192v2',
'1.2.840.10045.3.1.3' => 'prime192v3',
'1.2.840.10045.3.1.4' => 'prime239v1',
'1.2.840.10045.3.1.5' => 'prime239v2',
'1.2.840.10045.3.1.6' => 'prime239v3',
'1.2.840.10045.3.1.7' => 'prime256v1',
'1.2.840.113549.1.1.7' => 'RSAES-OAEP',
'1.2.840.113549.1.1.9' => 'pSpecified',
'1.2.840.113549.1.1.10' => 'RSASSA-PSS',
'1.2.840.113549.1.1.8' => 'mgf1',
'1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
'1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
'1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
'1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
'1.2.643.2.2.4' => 'GostR3411-94-with-GostR3410-94',
'1.2.643.2.2.3' => 'GostR3411-94-with-GostR3410-2001',
'1.2.643.2.2.20' => 'GostR3410-2001',
'1.2.643.2.2.19' => 'GostR3410-94',
'2.16.840.1.113730' => 'netscape',
'2.16.840.1.113730.1' => 'netscape-cert-extension',
'2.16.840.1.113730.1.1' => 'netscape-cert-type',
'2.16.840.1.113730.1.13' => 'netscape-comment',
'2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
'1.3.6.1.5.5.7.1.12' => 'logotype',
'1.2.840.113533.7.65.0' => 'entrustVersInfo',
'2.16.840.1.113733.1.6.9' => 'verisignPrivate',
'1.2.840.113549.1.9.2' => 'unstructuredName',
'1.2.840.113549.1.9.7' => 'challengePassword',
'1.2.840.113549.1.9.14' => 'extensionRequest',
'0.9.2342.19200300.100.1.1' => 'userid',
'1.2.840.113549.1.9.15' => 's/mime',
'1.2.840.113549.1.9.8' => 'unstructuredAddress',
'1.2.840.113549.3.2' => 'rc2-cbc',
'1.2.840.113549.3.4' => 'rc4',
'1.3.14.3.2.7' => 'desCBC',
'1.3.6.1.5.5.7.1.3' => 'qcStatements',
'1.3.6.1.5.5.7.11.1' => 'pkixQCSyntax-v1',
'1.3.6.1.5.5.7.11.2' => 'pkixQCSyntax-v2',
'1.3.6.1.5.5.7.3.5' => 'ipsecEndSystem',
'1.3.6.1.5.5.7.3.6' => 'ipsecTunnel',
'1.3.6.1.5.5.7.3.7' => 'ipsecUser',
'1.3.6.1.5.5.7.48.1' => 'OCSP',
'1.3.6.1.5.5.7.9.4' => 'countryOfCitizenship',
'1.3.6.1.5.5.8.2.2' => 'IPSECProtection',
'2.5.4.20' => 'telephoneNumber',
'2.5.4.97' => 'organizationIdentifier',
);
/**
* from https://github.com/vakata/asn1
*
* @param string $id
* @return string
*/
public static function OIDtoText($id)
{
// echo $id."\n";
$text = self::$oidTexts[$id] ?? $id;
return $text;
}
/**
* from https://github.com/vakata/asn1
*
* @param string $text
* @return string
*/
public static function TextToOID($text)
{
$res = static::$oids[$text] ?? null;
if (is_array($res)) {
$res = $res[0];
}
return $res ?? $text;
}
}
function getMillisecond()
{
list($microsecond, $time) = explode(' ', microtime()); //' '中间是一个空格
return (float)sprintf('%.0f', (floatval($microsecond) + floatval($time)) * 1000);
}
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace Rtgm\util;
class SmSignFormatRS
{
public static function asn1_to_rs($str, $format = 'base64')
{
if ($format == 'base64') {
$str = base64_decode($str);
} else if ($format == 'hex') {
$str = hex2bin($str);
}
$arr = \FG\ASN1\ASNObject::fromBinary($str);
// var_dump($arr[0]->getContent());die();
$r = self::_padding_zero(self::_format_bigint($arr[0]->getContent()));
$s = self::_padding_zero(self::_format_bigint($arr[1]->getContent()));
return base64_encode($r . $s);
}
protected static function _format_bigint($data)
{
$hex = gmp_strval(gmp_init($data, 10), 16);
return $hex;
}
/**
* rs要固定长度,经测试会有1%的概率出现长度短的,要补0
*
* @param string $hex
* @return string
*/
protected static function _padding_zero($hex)
{
$len = 64; // r,s都是32字节
$left = $len - strlen($hex);
if ($left > 0) {
$hex = str_repeat('0', $left) . $hex;
}
return hex2bin($hex);
}
/**
* r+s ==> asn1(r+s)
*
* @param string $str
* @param string $format
* @return string
*/
public static function rs_to_asn1($str, $format = 'base64')
{
if ($format == 'base64') {
$str = base64_decode($str);
} else if ($format == 'hex') {
$str = hex2bin($str);
}
$binR = self::_trim_int_pad(substr($str, 0, 32));
$binS = self::_trim_int_pad(substr($str, 32));
$lenR = strlen($binR);
$lenS = strlen($binS);
$result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS;
return base64_encode($result);
}
/**
* 去掉多余的0
*
* @param string $binStr
* @return string
*/
protected static function _trim_int_pad($binStr)
{
//trim 0
while (ord($binStr[0]) == 0) {
$binStr = substr($binStr, 1);
}
// add 0 if necessary
if (ord($binStr[0]) > 127) {
$binStr = chr(0) . $binStr;
}
// echo bin2hex($binStr)."\n";
return $binStr;
}
}