first commit

This commit is contained in:
annnj-company
2026-04-17 18:29:53 +08:00
parent e49fa5a215
commit 130c1026c4
5615 changed files with 1639145 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
vendor2/
vendor/
composer.lock

58
pgserver/vendor/lpilp/guomi/README.md vendored Normal file
View File

@@ -0,0 +1,58 @@
# php sm2 sm3 sm4 国密算法整理
* 本项目支持php版本的国密sm2的签名算法非对称加解密算法非对称加密刚上线目前测试无问题不能保证兼容其他语言有问题可以提issuessm3的hash sm4的对称加解密要求PHP打开gmp支持
* 目前如果服务器配套的使用的是openssl 1.1.1x 目前到1.1.1.l(L) sm3sm4都可以直接用openssl_xxx系列函数直接实现不必大量的代码但不支持sm2的签名sm2的加解密
* 有一个sm3 sm4的比较好的代码 https://github.com/lizhichao/sm 可以使用composer安装只是这个的ecb cbc没有做补齐
### 使用(how to use)
* 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
### 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的公钥私钥的加密函数得自己实现建议使用CC++的算法打包成PHP扩展的方式
* SM2的非对称加密缺省的是c1c3c2 请使用的时候注意下对方返回的是c1c3c2还是c1c2c3进行相应的修改更新,还有一点就是本项目中c1前面没有04 视对接方的需求,看是否添加\x04, v1.0.6版已对c1c3c2还是c1c2c3做了兼容缺省是c1c3c2,添加相应的modetype后可以兼容两种模式使用方法见 test/tsm2_encrypt.php
### SM3
* 该算法直接使用 https://github.com/ToAnyWhere/phpsm2 中sm2签名用到的匹配sm3 未做修改
* 也可使用 openssl的函数 详见openssl_tsm3.php
### SM4
* 该算法直接封装使用 https://github.com/lizhichao/sm 的sm4算法 同时该项目支持 sm3,sm4 可以composer安装
* 由于sm4-ecb sm4-cbc加密需要补齐项目lizhichao/sm项目未做补齐操作这里封装的时候针对这两个算法做了补齐操作 其他如sm4-ctr,sm4-cfbsm4-ofb等可以直接用
* 在openssl 1.1.1下可使用系统的函数已支持sm4-cbcsm4-cfbsm4-ctr,sm4-ecbsm4-ofb 详见openssl_tsm4.php
### 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 安装就可, 注意的就是在python2下 string与byte[] 是可以隐式转换的在python3下需要显式地将string转成byte[]
* java: https://github.com/ZZMarquis/gmhelper 注意下java中文的转码问题getBytes("UTF-8") 要加上编码类型, 因为 getBytes()函数的缺省编码是随操作系统的如果是在中文版的windows中使用缺省是GBK编码就会出现中文的编码的问题而造成签名无法通过
* openssl: 升到1.1.1以后支持sm3sm4的加解密还不支持sm2的公私钥加解密也不支持sm2的签名得使用原生代码实现签名中需要实现sm2withsm3 openssl1.1.1只实现了sm2whithsha256;还有一点很诡异用yum/dnf安装的openssl只支持sm3 如果是自己编译安装的就支持sm3sm4
+ go: https://github.com/tjfoc/gmsm 一家做区块链的公司开源的项目在go方面可以说是最早开源的了sm2主要有两个问题
1. 暂无使用外部密码明文生成公私钥构造函数,比较简单可自行添加
2. sm2的非对称加解密例子中使用EncryptAsn1这个函数asn1是将 c1拆开为pxpy两部分asn1(pxpyc3c2)的编码在sm2类中也有直接Encrypt函数生成的是'\x04'+c1+c3+c2 与其它语言生成的加密串在c1部分多加了一个\x04 看互通代码的需求自行处理
3. 签名与验签的方法可与本项目的PHP语言是互签互认的
4. sm4的对称加解密cbc等需要IV向量的模式将iv做为全局变量了对于不同的串要使用不同的iv时就会出现麻烦请根据需要修改代码将iv加到参数里
+ C#: 项目也比较少基本是基于https://www.bouncycastle.org/ 的BC加密库(java也是基于该库)该库1.8.4后版本支持sm2sm3sm4考察搜索到的几个项目https://github.com/hz281529512/SecretTest 完整性算比较好
1. 项目提供sm2的签名加解密都加入的项目其他的项目里没有签名算法sm2的加解密的返回值与上面的go项目一样返回的是asn1编码过的如需要原始的请自行修改更新
2. sm4的cbc的封装后把 key与iv弄成一个值了如需要请自行修改更新
### SM2签名常见问题
* 提供的私钥是base64的短串一般直接 bin2hex(base64_decode(str)) 就是明文的密钥了
* 文件格式的密钥一般有pkcs1与pkcs8两个格式本项目只支持pkcs1格式的密钥使用前请先进行相关的转换一般 pkcs8是四行pkcs1是三行区别见 https://www.jianshu.com/p/a428e183e72e
* 关于签名的字符串的问题有些项目会将原始字符串哈稀后再对哈稀值进行签名有些对这哈稀值又进行了hex2bin操作后再签名请双方按约定的标准确定最后签名的数据值双方保持一致即可
* 个别项目会碰到asn1的时候解析不了如果是密钥解析不了一般就是pkcs8的问题如果只是数据解析不了的话看报错调试下目前碰到的是招行的一个问题对int数据的asn1编码有前面补0与否不按标准来全都补0
* 签名的结果是asn1(r,s),有极个别的项目签名出来的只是 r+s的字符串组合验证签名的时候注意下。
* ***招行*** 的签名回调系统在做asn1编码的int类型的时候标准是最高位大于7D是要补0反之则不要补0而招行返回的统一32字节长度对于一些不用补0的也强制补0使用的第三方phpasn1会报错可以屏蔽该错误或是将返回的签名过滤下项目提供了一个过滤的类 src/util/FormatSign->run(),使用测试见 test/tfsign.php
* 同样的,在用本项目给 ***招行*** 签名的时候就会出现上面同样的问题编码int的时候招行认为签名的r,s都是32字节这时对于偶发的小于32字节的r,s就得补0操作src/util/FormatSign->format_cmbc()函数,目前只针对招行, 所以只是以补丁的方式,没有加入主程序
* 以上是现行的招行的程序打的补丁不能保证招行修复了这个bug, 假设招行修复了这个bug,使用了format_cmbc()那个小概率的反而会错了请根据实际情况使用招行有提供相关的sdk,看是否更新就知道了

View File

@@ -0,0 +1,31 @@
{
"name": "lpilp/guomi",
"description": "国密sm2",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "recent",
"email": "lpilp@126.com"
}
],
"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/"
}
}
}

View File

@@ -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);
}
}

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);
}
}

View File

@@ -0,0 +1,149 @@
<?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';
// const SM2_OID = '1.2.840.10045.2.1';
/**
* @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;
}
}

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);
}
}

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 ) );
}
}

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);

View File

@@ -0,0 +1,396 @@
<?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 = false;
// 输入输出的签名方式 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 );
}
//缺省 hex
return bin2hex($serializedSig);
}
/**
* Undocumented function
*
* @param string $document
* @param string $sign
* @param string $publicKey 如提供的base64的可使用 bin2hex(base64_decode($publicKey))
* @param string|null $userId
* @return bool
*/
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);
}
public 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;
}
}

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);
}
}

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');
}
}
}

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;
}
}

View File

@@ -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 = $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;
}
}

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;
} */
}

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;
}
}

View File

@@ -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;
}
}

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));
}
}

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);
}
}

View File

@@ -0,0 +1,2 @@
# sm
国密sm3,sm4 ECB加密源码

View File

@@ -0,0 +1,196 @@
<?php
namespace icequeen\sm\smecc\SM4;
use ErrorException;
use Rtgm\smecc\SM4\handler\ExtendedCompression;
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\BitString;
/**
* 入口类
* Class Sm3
*
* @package SM3
* @error_code 90xxx
*/
class SM3 implements \ArrayAccess
{
/** @var string 初始值常数 */
const IV = '7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e';
/** @var string 消息(加密前的结果) */
private $message = '';
/** @var string 杂凑值(加密后的结果) */
private $hash_value = '';
/**
* 实例化时直接调用将参数传给主方法
* Sm3 constructor.
*
* @param $message string|mixed 传入的消息
*
* @throws \ErrorException
*/
public function __construct($message)
{
// 输入验证
if (is_int($message)) {
$message = (string)$message;
}
if (empty($message)) {
$message = '';
}
if (!is_string($message)) {
throw new ErrorException('参数类型必须为string请检查后重新输入', 90001);
}
/** @var string message 消息 */
$this->message = $message;
/** @var string hash_value 杂凑值 */
$this->hash_value = $this->sm3();
}
/**
* 主方法
*
* @return string
* @throws \ErrorException
*/
private function sm3()
{
/** @var string $m 转化后的消息(二进制码) */
// $json = json_encode($this->message);
$m = new BitString($this->message, false);
//var_dump($m);die(); //11010010 10111011
// 一、填充
$l = strlen($m);
// 满足l + 1 + k ≡ 448mod512 的最小的非负整数
$k = $l % 512;
$k = $k + 64 >= 512
? 512 - ($k % 448) - 1
: 512 - 64 - $k - 1;
$bin_l = new BitString($l);
// 填充后的消息
$m_fill = new BitString(
$m # 原始消息m
. '1' # 拼个1
. str_pad('', $k, '0') # 拼上k个比特的0
. (
strlen($bin_l) >= 64
? substr($bin_l, 0, 64)
: str_pad($bin_l, 64, '0', STR_PAD_LEFT)
) # 64比特l的二进制表示
);
// 二、迭代压缩
// 迭代过程
$B = str_split($m_fill, 512);
/** @var int $n m'可分为的组数 */
$n = ($l + $k + 65) / 512;
if (count($B) !== $n) {
throw new ErrorException();
}
$V = array(
WordConversion::hex2bin(self::IV),
);
$extended = new ExtendedCompression();
foreach ($B as $key => $Bi) {
// print_r($Bi."\n"."====== $key ========\n".strlen($Bi)."\n");
$V[$key + 1] = $extended->CF($V[$key], $Bi)->getBitString();
}
krsort($V);
reset($V);
$binary = current($V);
$rt = WordConversion::bin2hex($binary);
return $rt;
}
/**
* 方便直接输出实例化的对象
*
* @return string
*/
public function __toString()
{
return $this->hash_value;
}
/**
* Whether a offset exists
*
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset)
{
return isset($this->hash_value[$offset]);
}
/**
* Offset to retrieve
*
* @link https://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
*
* @return mixed Can return all value types.
* @since 5.0.0
*/
public function offsetGet($offset)
{
return $this->hash_value[$offset];
}
/**
* Offset to set
*
* @link https://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
*
* @return \SM3\SM3
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
$this->hash_value[$offset] = $value;
return $this;
}
/**
* Offset to unset
*
* @link https://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
*
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
unset($this->hash_value[$offset]);
}
}

View File

@@ -0,0 +1,196 @@
<?php
namespace Rtgm\smecc\SM4;
class Sm4
{
static $SM4_CK = array(
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
);
static $SM4_SBOX = array(
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
);
static $SM4_FK = array(0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC);
private $_rk = array();
private $_block_size = 16;
public function __construct()
{
if (PHP_INT_SIZE < 8) {
throw new \Exception('64bit OS supported only');
}
if (version_compare(PHP_VERSION, "7.0", "<")) {
throw new \Exception('php version 7.0 or greater');
}
}
public function encrypt($key, $data)
{
$this->_rk = array();
$this->sM4KeySchedule($key);
$bytes = $this->pad($data);
$chunks = array_chunk($bytes, $this->_block_size);
$ciphertext = "";
foreach ($chunks as $chunk) {
$ciphertext .= $this->sM4Encrypt($chunk);
}
return bin2hex($ciphertext);
return base64_encode($ciphertext);
}
public function decrypt($key, $data)
{
$this->_rk = array();
// $data = base64_decode($data);
$data = hex2bin($data);
if (strlen($data) < 0 || strlen($data) % $this->_block_size != 0) {
return false;
}
$this->sM4KeySchedule($key);
$bytes = unpack("C*", $data);
$chunks = array_chunk($bytes, $this->_block_size);
$plaintext = "";
foreach ($chunks as $chunk) {
$plaintext .= substr($this->sM4Decrypt($chunk), 0, 16);
}
$plaintext = $this->un_pad($plaintext);
return $plaintext;
}
private function sM4Decrypt($cipherText)
{
$x = array();
for ($j = 0; $j < 4; $j++) {
$x[$j] = ($cipherText[$j * 4] << 24) | ($cipherText[$j * 4 + 1] << 16) | ($cipherText[$j * 4 + 2] << 8)
| ($cipherText[$j * 4 + 3]);
}
for ($i = 0; $i < 32; $i++) {
$tmp = $x[$i + 1] ^ $x[$i + 2] ^ $x[$i + 3] ^ $this->_rk[31 - $i];
$buf = (self::$SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 | (self::$SM4_SBOX[($tmp >> 16) & 0xFF]) << 16
| (self::$SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 | (self::$SM4_SBOX[$tmp & 0xFF]);
$x[$i + 4] = $x[$i] ^ ($buf ^ $this->sm4Rotl32(($buf), 2) ^ $this->sm4Rotl32(($buf), 10)
^ $this->sm4Rotl32(($buf), 18) ^ $this->sm4Rotl32(($buf), 24));
}
$plainText = array();
for ($k = 0; $k < 4; $k++) {
$plainText[4 * $k] = ($x[35 - $k] >> 24) & 0xFF;
$plainText[4 * $k + 1] = ($x[35 - $k] >> 16) & 0xFF;
$plainText[4 * $k + 2] = ($x[35 - $k] >> 8) & 0xFF;
$plainText[4 * $k + 3] = ($x[35 - $k]) & 0xFF;
}
return $this->bytesToString($plainText);
}
private function sM4Encrypt($plainText)
{
$x = array();
for ($j = 0; $j < 4; $j++) {
$x[$j] = ($plainText[$j * 4] << 24) | ($plainText[$j * 4 + 1] << 16) | ($plainText[$j * 4 + 2] << 8)
| ($plainText[$j * 4 + 3]);
}
for ($i = 0; $i < 32; $i++) {
$tmp = $x[$i + 1] ^ $x[$i + 2] ^ $x[$i + 3] ^ $this->_rk[$i];
$buf = (self::$SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 | (self::$SM4_SBOX[($tmp >> 16) & 0xFF]) << 16
| (self::$SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 | (self::$SM4_SBOX[$tmp & 0xFF]);
$x[$i + 4] = $x[$i] ^ ($buf ^ $this->sm4Rotl32(($buf), 2) ^ $this->sm4Rotl32(($buf), 10)
^ $this->sm4Rotl32(($buf), 18) ^ $this->sm4Rotl32(($buf), 24));
}
$cipherText = array();
for ($k = 0; $k < 4; $k++) {
$cipherText[4 * $k] = ($x[35 - $k] >> 24) & 0xFF;
$cipherText[4 * $k + 1] = ($x[35 - $k] >> 16) & 0xFF;
$cipherText[4 * $k + 2] = ($x[35 - $k] >> 8) & 0xFF;
$cipherText[4 * $k + 3] = ($x[35 - $k]) & 0xFF;
}
return $this->bytesToString($cipherText);
}
private function stringToBytes($string)
{
return unpack('C*', $string);
}
private function bytesToString($bytes)
{
return vsprintf(str_repeat('%c', count($bytes)), $bytes);
}
private function pad($data)
{
$bytes = $this->stringToBytes($data);
$rem = $this->_block_size - count($bytes) % $this->_block_size;
for ($i = 0; $i < $rem; $i++) {
array_push($bytes, $rem);
}
return $bytes;
}
private function un_pad($data)
{
$bytes = $this->stringToBytes($data);
$rem = $bytes[count($bytes)];
$bytes = array_slice($bytes, 0, count($bytes) - $rem);
return $this->bytesToString($bytes);
}
private function sm4Rotl32($buf, $n)
{
return (($buf << $n) & 0xffffffff) | ($buf >> (32 - $n));
}
private function sM4KeySchedule($key)
{
$this->_rk = array();
$key = array_values(unpack("C*", $key));
$k = array();
// for ($i = 0; $i < 4; $i++) {
// $k[$i] = self::$SM4_FK[$i]
// ^ ((($key[4 * $i] ?? 0) << 24)
// | (($key[4 * $i + 1] ?? 0) << 16)
// | (($key[4 * $i + 2] ?? 0) << 8)
// | ($key[4 * $i + 3] ?? null));
// }
for ($i = 0; $i < 4; $i++) {
$k[$i] = self::$SM4_FK[$i]
^ (((isset($key[4 * $i]) ? $key[4 * $i] : 0) << 24)
| ((isset($key[4 * $i + 1]) ? $key[4 * $i + 1] : 0) << 16)
| ((isset($key[4 * $i + 2]) ? $key[4 * $i + 2] : 0) << 8)
| (isset($key[4 * $i + 3]) ? $key[4 * $i + 3] : null));
}
for ($j = 0; $j < 32; $j++) {
$tmp = $k[$j + 1] ^ $k[$j + 2] ^ $k[$j + 3] ^ self::$SM4_CK[$j];
$buf = (self::$SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 | (self::$SM4_SBOX[($tmp >> 16) & 0xFF]) << 16
| (self::$SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 | (self::$SM4_SBOX[$tmp & 0xFF]);
$k[$j + 4] = $k[$j] ^ (($buf) ^ ($this->sm4Rotl32(($buf), 13)) ^ ($this->sm4Rotl32(($buf), 23)));
$this->_rk[$j] = $k[$j + 4];
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
/**
* BigJHandler @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14日 2019年
* Powered by PhpStorm
*/
/**
* 小j处理类
* Class BigJHandler
*
* @package SM3\handler
*/
class BigJHandler extends JHandler
{
/** @var int j的最大可用值 */
const SMALLEST_J = 16;
/** @var int j的最小可用值 */
const BIGGEST_J = 63;
/** @var string T常量 */
const T = '7a879d8a';
/**
* 补充父类
* SmallJHandler constructor.
*/
public function __construct()
{
parent::__construct(self::T, self::SMALLEST_J, self::BIGGEST_J);
}
/**
* 布尔函数
*
* @param $X string 长度32的比特串
* @param $Y string
* @param $Z string
*
* @return mixed
*/
public function FF($X, $Y, $Z)
{
$X_and_Y = WordConversion::andConversion(array($X, $Y));
$X_and_Z = WordConversion::andConversion(array($X, $Z));
$Y_and_Z = WordConversion::andConversion(array($Y, $Z));
return WordConversion::orConversion(
array(
$X_and_Y,
$X_and_Z,
$Y_and_Z
)
);
}
/**
* 布尔函数
*
* @param $X
* @param $Y
* @param $Z
*
* @return mixed
*/
public function GG($X, $Y, $Z)
{
$X_and_Y = WordConversion::andConversion(array($X, $Y));
$not_X = WordConversion::notConversion($X);
$not_X_and_Z = WordConversion::andConversion(array($not_X, $Z));
return WordConversion::orConversion(array($X_and_Y, $not_X_and_Z));
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace Rtgm\smecc\SM4\handler;
/**
* ExtendedCompression @ SM3-PHP
*
* Code BY ch4o5
* 10月. 15日 2019年
* Powered by PhpStorm
*/
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\Word;
/**
* 扩展压缩算法
* Class ExtendedCompression
*
* @package SM3\handler
*/
class ExtendedCompression
{
/** @var array $W */
private $W;
/** @var array $W ' */
private $W_s;
/**
* 压缩函数
*
* @param $Vi
* @param $Bi
*
* @return Word
* @throws \ErrorException
*/
public function CF($Vi, $Bi)
{
// 消息扩展
$this->extended($Bi);
/** @var array $registers 八个寄存器的名字 */
$registers = array(
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H'
);
// 将 Vi 的值依次放入八个寄存器中
// 下列注释用于防止IDE报错
/** @var Word $A */
/** @var Word $B */
/** @var Word $C */
/** @var Word $D */
/** @var Word $E */
/** @var Word $F */
/** @var Word $G */
/** @var Word $H */
foreach ($registers as $i => $register) {
$$register = new Word(substr($Vi, $i * 32, 32));
}
$small_j_handler = new SmallJHandler();
$big_j_handler = new BigJHandler();
for ($j = 0; $j < 64; $j++) {
$j_handler = ($j >= SmallJHandler::SMALLEST_J && $j < BigJHandler::SMALLEST_J)
? $small_j_handler
: $big_j_handler;
$SS1 = WordConversion::shiftLeftConversion(
WordConversion::addConversion(
array(
WordConversion::shiftLeftConversion($A, 12),
$E,
WordConversion::shiftLeftConversion($j_handler->getT(), $j)
)
),
7
);
$SS2 = WordConversion::xorConversion(
array(
$SS1,
WordConversion::shiftLeftConversion($A, 12)
)
);
$TT1 = WordConversion::addConversion(
array(
$j_handler->FF($A, $B, $C),
$D,
$SS2,
$this->W_s[$j]
)
);
$TT2 = WordConversion::addConversion(
array(
$j_handler->GG($E, $F, $G),
$H,
$SS1,
$this->W[$j]
)
);
$D = $C;
$C = WordConversion::shiftLeftConversion($B, 9);
$B = $A;
$A = $TT1;
$H = $G;
$G = WordConversion::shiftLeftConversion($F, 19);
$F = $E;
$TT2_object = new Substitution($TT2);
$E = $TT2_object->P0();
}
return WordConversion::xorConversion(
array(
join(
'',
array(
(new Word($A)),
(new Word($B)),
(new Word($C)),
(new Word($D)),
(new Word($E)),
(new Word($F)),
(new Word($G)),
(new Word($H))
)
),
$Vi
)
);
}
/**
* 消息扩展
*
* 将消息分组B(i)按以下方法扩展生成132个字W0, W1, · · · , W67, W0, W1, · · · , W63
* 用于压缩函数CF
*
* @param $Bi 消息分组中的第i个最大512位 (\SM3\types\BitString)
*
* @return void
* @throws \ErrorException
*/
public function extended($Bi)
{
// 将消息分组B(i)划分为16个字W0, W1, · · · , W15。
$this->W = $this->W_s = array();
$word_per_times = (int)ceil(strlen($Bi) / 16);
for ($i = 0; $i < 16; $i++) {
$this->W[$i] = new Word(
substr($Bi, $i * $word_per_times, $word_per_times)
);
}
// 计算W
for ($j = 16; $j <= 67; $j++) {
$param_1 = (new Substitution(
WordConversion::xorConversion(
array(
$this->W[$j - 16],
$this->W[$j - 9],
WordConversion::shiftLeftConversion($this->W[$j - 3], 15)
)
)
));
$this->W[$j] = WordConversion::xorConversion(
array(
$param_1->P1(),
WordConversion::shiftLeftConversion($this->W[$j - 13], 7),
$this->W[$j - 6]
)
);
}
unset($j);
// 计算W'
for ($j = 0; $j <= 63; $j++) {
$this->W_s[$j] = WordConversion::xorConversion(
array(
$this->W[$j],
$this->W[$j + 4]
)
);
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\Word;
/**
* JHandler @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14th 2019
* Powered by PhpStorm
*/
/**
* j处理抽象类
* Class JHandler
*
* @package SM3\instantiation
*/
abstract class JHandler
{
/** @var string 常量T */
protected $T = '';
/** @var array j的长度区间 */
protected $section_j = array();
/**
* JHandler constructor.
*
* @param $T string 常量T
* @param $smallest int j的最小可用值
* @param $biggest int j的最大可用值
*/
public function __construct($T, $smallest, $biggest)
{
$this->setT($T);
$this->setSectionJ($smallest, $biggest);
}
/**
* 配置 继承本抽象类的子类可以处理的j的大小
*
* @param $smallest int j的最小长度
* @param $biggest int j的最大长度
*/
public function setSectionJ($smallest, $biggest)
{
$this->section_j = array($smallest, $biggest);
}
/**
* 布尔函数
*
* @param $X string 长度32的比特串
* @param $Y string
* @param $Z string
*
* @return mixed
*/
abstract public function FF($X, $Y, $Z);
/**
* 布尔函数
*
* @param $X
* @param $Y
* @param $Z
*
* @return mixed
*/
abstract public function GG($X, $Y, $Z);
/**
* 读取常量T
* @return Word
* @throws \ErrorException
*/
public function getT()
{
return new Word($this->T);
}
/**
* 配置常量T
*
* @param string $T
*/
public function setT($T)
{
$this->T = WordConversion::hex2bin($T);
}
}

View File

@@ -0,0 +1,12 @@
# *handle/* 目录
本目录包含了加密过程中的核心算法
* `\SM3\handler\JHandler`
根据J的不同进行不同处理的抽象类
* `\SM3\handler\SmalJHandler`
变量j处于较小值时的处理类
* `\SM3\handler\BigJHandler`
变量j处于较大值时的处理类
* `\SM3\handler\Substitution`
置换函数类

View File

@@ -0,0 +1,79 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
/**
* 小j处理类
* Class SmallJHandler
*
* @package SM3\handler
*/
class SmallJHandler extends JHandler
{
/** @var int j的最大可用值 */
const SMALLEST_J = 0;
/** @var int j的最小可用值 */
const BIGGEST_J = 15;
/** @var string T常量 */
const T = '79cc4519';
/**
* 补充父类
* SmallJHandler constructor.
*/
public function __construct()
{
parent::__construct(self::T, self::SMALLEST_J, self::BIGGEST_J);
}
/**
* 布尔函数
*
* @param $X \SM3\types\Word 长度32的比特串
* @param $Y \SM3\types\Word 长度32的比特串
* @param $Z \SM3\types\Word 长度32的比特串
*
* @return \SM3\types\Word
*/
public function FF($X, $Y, $Z)
{
return self::boolFunction($X, $Y, $Z);
}
/**
* 小j值的布尔函数公共方法
*
* @param $X \SM3\types\Word 长度32的比特串
* @param $Y \SM3\types\Word 长度32的比特串
* @param $Z \SM3\types\Word 长度32的比特串
*
* @return \SM3\types\Word
*/
private static function boolFunction($X, $Y, $Z)
{
return WordConversion::xorConversion(
array(
$X,
$Y,
$Z
)
);
}
/**
* 布尔函数
*
* @param $X \SM3\types\Word 长度32的比特串
* @param $Y \SM3\types\Word 长度32的比特串
* @param $Z \SM3\types\Word 长度32的比特串
*
* @return \SM3\types\Word
*/
public function GG($X, $Y, $Z)
{
return self::boolFunction($X, $Y, $Z);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\Word;
/**
* Substitution @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14日 2019年
* Powered by PhpStorm
*/
/**
* 置换函数
* Class Substitution
*
* @package SM3\handler
*/
class Substitution
{
/** @var \SM3\types\Word 待置换的字 */
private $X;
/** @var array P0函数中两次左移的位数 */
private $P0_shiftLeft_times = array(9, 17);
/** @var array P1函数中两次左移的位数 */
private $P1_shiftLeft_times = array(15, 23);
/**
* Substitution constructor.
*
* @param $X
*/
public function __construct($X)
{
$this->X = $X;
}
/**
* 压缩函数中的置换函数
*
* @return \SM3\types\Word 置换结果
*/
public function P0()
{
return $this->substitutionFunction(0);
}
/**
* 置换函数的公共函数
* @param $type
* @return Word
* @throws \ErrorException
*/
private function substitutionFunction($type)
{
if (!in_array($type, array(0, '0', 1, '1'))) {
return new Word('');
}
$times_name = $type == 1
? $this->P1_shiftLeft_times
: $this->P0_shiftLeft_times;
$X_shiftLeft_1 = WordConversion::shiftLeftConversion($this->X, $times_name[0]);
$X_shiftLeft_2 = WordConversion::shiftLeftConversion($this->X, $times_name[1]);
return WordConversion::xorConversion(
array(
$this->X,
$X_shiftLeft_1,
$X_shiftLeft_2
)
);
}
/**
* 消息扩展中的置换函数
* @return Word
* @throws \ErrorException
*/
public function P1()
{
return $this->substitutionFunction(1);
}
}

View File

@@ -0,0 +1,8 @@
# *libs/* 目录
本目录包含了 *动态读写配置**一些底层的基础操作(如:按位运算)*
* `\SM3\libs\Config`
配置类,获取和动态设定配置
* `\SM3\libs\WordConversion`
字的位运算类,包含了位的各种运算

View File

@@ -0,0 +1,271 @@
<?php
namespace Rtgm\smecc\SM4\libs;
/**
* Conversion @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14th 2019
* Powered by PhpStorm
*/
use Rtgm\smecc\SM4\types\Word;
/**
* 字的位运算类
* Class Conversion
*
* @package SM3\libs
*/
class WordConversion
{
/**
* 字的异或运算
*
* @param $params array 需要进行异或运算的字列表
*
* @return Word
* @api $value \SM3\types\Word
*
*/
public static function xorConversion($params)
{
return self::conversion($params, 3);
}
public static function conversion_func($prevent, $current, $type)
{
if (is_null($prevent)) {
return $current;
}
$prevent = strval($prevent);
$current = strval($current);
if (strlen($current) >= strlen($prevent)) {
$longest = strlen($current);
$longest_value = $current;
$shortest = strlen($prevent);
} else {
$longest = strlen($prevent);
$longest_value = $prevent;
$shortest = strlen($current);
}
if ($prevent === '0' || $current === '0') {
switch ($type) {
// and
case 1:
return 0;
// or
case 2:
// xor
case 3:
// add
case 5:
return $prevent == '0' ? $current : $prevent;
default:
break;
}
}
$value = array();
/**
* 加运算时需要,用来储存需要进几
*
* @var int 向前一位进的数字
*/
$carry = 0;
/**
* 大端
*
* 这里从大端跑完之后,结果数组的序号是从大到小排列的
* 还需要根据键名排序一次
*
* 个人感觉区分不区分大端并没有什么意义
* 如果换成字符串拼接的话更好用
* 但是方便你理解,还是按照大端+数组的方式进行的排列
*/
for ($i = $longest - 1; $i >= 0; $i--) {
$prevent_number = $prevent[$i];
switch ($type) {
// 与
case 1:
$value[$i] = ($i >= $shortest)
? $longest_value[$i]
: ($prevent_number & $current[$i]);
break;
// 或
case 2:
$value[$i] = ($i >= $shortest)
? ~$longest_value[$i]
: ($prevent_number | $current[$i]);
break;
// 异或
case 3:
$value[$i] = $i > $shortest
? 1
: (intval($prevent_number) ^ intval($current[$i]));
break;
// 非(按位取反)
case 4:
$value[$i] = $prevent_number === '1'
? '0'
: '1';
break;
// 加
case 5:
$add = $prevent_number + $current[$i] + $carry;
$value[$i] = $add % 2;
$carry = ($add - $value[$i]) / 2;
break;
// 特殊情况
default:
break;
}
}
ksort($value);
return new Word(join('', $value));
}
/**
* @param $params array 需要进行异或运算的字列表
* @param $type int 位运算类型
* @return Word|null 运算结果
*/
private static function conversion($params, $type)
{
$prevent = null;
foreach ($params as $param){
$prevent = self::conversion_func($prevent,$param,$type);
}
return $prevent;
}
/**
* 字的与运算
*
* @param $params array 需要进行与运算的字列表
*
* @return Word
*/
public static function andConversion($params)
{
return self::conversion($params, 1);
}
/**
* 字的或运算
*
* @param $params
*
* @return Word
*/
public static function orConversion($params)
{
return self::conversion($params, 2);
}
/**
* 字的非运算
*
* @param $word
*
* @return Word
*/
public static function notConversion($word)
{
return self::conversion(array($word, null), 4);
}
/**
* @param $word Word 待运算的字
* @param $times int 左移的位数
* @return Word
* @throws \ErrorException
*/
public static function shiftLeftConversion($word, $times)
{
return new Word(
substr(
$word,
($times % strlen($word))
)
. substr(
$word,
0,
($times % strlen($word))
)
);
}
/**
* 将16进制数串转换为二进制数据
* 使用字符串形式实现解决了PHP本身进制转换的时候受限于浮点数大小的问题
*
* @param int|string $hex
*
* @return string
*/
public static function hex2bin($hex)
{
// 格式化为字符串
$hex = strval($hex);
/** 十六进制转二进制每1位一组 */
defined('HEX_TO_BIN_NUM') || define('HEX_TO_BIN_NUM', 1);
/** @var array $hex_array 把指定的十六进制数按位切片为数组 */
$hex_array = str_split($hex, HEX_TO_BIN_NUM);
// 最终的二进制数字(为确保长度不丢失,使用字符串类型)
$binary = '';
foreach ($hex_array as $number) {
$bin_number = strval(base_convert($number, 16, 2));
if (strlen($bin_number) < 4) {
$bin_number = str_pad($bin_number, 4, '0', STR_PAD_LEFT);
}
$binary .= $bin_number;
}
return $binary;
}
/**
* 二进制转十六进制
* @param $bin
*
* @return string
*/
public static function bin2hex($bin)
{
// 格式化为字符串
$bin = strval($bin);
/** 二进制转十六进制每4位一组 */
defined('BIN_TO_HEX_NUM') || define('BIN_TO_HEX_NUM', 4);
/** @var array $bin_array 把指定的二进制数按位切片为数组 */
$bin_array = str_split($bin, BIN_TO_HEX_NUM);
// 最终的二进制数字(为确保长度不丢失,使用字符串类型)
$hex = '';
foreach ($bin_array as $number) {
$hex .= strval(base_convert($number, 2, 16));
}
return $hex;
}
/**
* 二进制加运算
*
* @param array $params
*
* @return Word
*/
public static function addConversion($params)
{
return self::conversion($params, 5);
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace Rtgm\smecc\SM4\types;
use ErrorException;
/**
* BitString @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14th 2019
* Powered by PhpStorm
*/
/**
* 比特串
* 由0和1组成的二进制数字序列。
* Class BitString
*
* @package SM3\types
*/
class BitString implements \ArrayAccess
{
/** @var string 一个比特串类型的变量 */
protected $bit_string = '';
/**
* BitString constructor.
*
* @param $string string|BitString|Word|mixed 传入的数据
* @param false|null $is_bit_string 是否比特串(只有入口明确知道不是)
*
* @throws \ErrorException
*/
public function __construct($string, $is_bit_string = null)
{
if (is_object($string)) {
$string = $string->getString();
}
if ($is_bit_string === false) {
// 指定不是比特串的,直接转换
$this->bit_string = "{$this->str2bin($string)}";
} else {
// 默认走个验证试试
$this->bit_string = $this->is_bit_string($string)
? $string
: "{$this->str2bin($string)}";
}
}
/**
* 字符串转比特串
*
* @param $str int|string 普通字符串
*
* @return string 转换为比特串
* @throws \ErrorException
*/
private function str2bin($str)
{
if (!is_string($str) && !is_int($str)) {
throw new ErrorException('输入的类型错误');
}
if (is_int($str)) {
return decbin($str);
}
$fileType = mb_detect_encoding($str , array('UTF-8','GBK','LATIN1','BIG5')) ;
if( $fileType != 'UTF-8'){
$str = mb_convert_encoding($str ,'utf-8' , $fileType);
}
$arr = preg_split('/(?<!^)(?!$)/u', $str);
foreach ($arr as &$v) {
$fileType = mb_detect_encoding($v , array('UTF-8','GBK','LATIN1','BIG5')) ;
/* if( $fileType != 'GBK'){
$v = mb_convert_encoding($v ,'GBK' , $fileType); //这里也要utf8啊
} */
if( $fileType != 'UTF-8'){
$str = mb_convert_encoding($str ,'utf-8' , $fileType);
}
$temp = unpack('H*', $v);
$v = base_convert($temp[1], 16, 2);
while (strlen($v) < 8) {
$v = '0' . $v;
}
unset($temp);
}
return join('', $arr);
}
/**
* 判断是否为比特串类型
*
* @param string|Word $string
*
* @return bool
*/
public function is_bit_string($string)
{
if (is_object($string)) {
$string = $string->getString();
}
// 检查是否为字符串
if (!is_string($string)) {
return false;
}
// 检查是否为只有0和1组成的字符串
$array = array_filter(str_split($string));
foreach ($array as $value) {
if (!in_array(
$value,
array(
0,
'0',
1,
'1'
),
true
)) {
return false;
}
}
return true;
}
public function __toString()
{
return $this->getString();
}
/**
* 获取比特串的值
*
* @return string
*/
public function getString()
{
return $this->bit_string;
}
public function offsetGet($offset)
{
return $this->bit_string[$offset];
}
/**
* Whether a offset exists
*
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset)
{
return isset($this->bit_string[$offset]);
}
/**
* Offset to set
*
* @link https://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
*
* @return BitString
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
$this->bit_string[$offset] = $value;
return $this;
}
/**
* Offset to unset
*
* @link https://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
*
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
unset($this->bit_string[$offset]);
}
}

View File

@@ -0,0 +1,8 @@
# *types/* 目录
本目录为自定义的扩展类型的目录
* `\SM3\types\BitString`
比特串 类型
* `\SM3\types\Word`
字 类型

View File

@@ -0,0 +1,73 @@
<?php
namespace Rtgm\smecc\SM4\types;
/**
* 字类型
* 长度32的比特串
* Class Word
*
* @package SM3\types\
*/
class Word extends BitString implements \ArrayAccess
{
/** @var int 设置长度为32 */
const length = 32;
/** @var string */
private $word = '';
/**
* Word constructor.
*
* @param $string
*
* @throws \ErrorException
*/
public function __construct($string)
{
parent::__construct($string);
if (strlen($this->bit_string) === self::length) {
$this->word = $this->bit_string;
} else {
$this->word = intval($this->bit_string) === 0
? 0
: $this->bit_string;
if (strlen($this->word) <= self::length) {
$this->word = str_pad(
$this->word,
self::length,
'0',
STR_PAD_LEFT
);
} else {
$this->word = substr($this->bit_string, -(self::length));
}
}
}
public function __toString()
{
return $this->word;
}
public function offsetGet($offset)
{
return $this->word[$offset];
}
public function getString()
{
return $this->word;
}
/**
* @return string
*/
public function getBitString()
{
return $this->bit_string;
}
}

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];
}
}

View File

@@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALjnVY4j/UG0tJ5o
HZclp1R1+HsCQmUctzPiN5VO56ZYuFP4T3oDhVkmWReL52WGaKYGrxJymqM6mPt8
C3AXNd/KGTpZtXaoBvjK21nPmgDpo6m5nifi+IQTXmjrwDPErr0yIrAvDMP+3wQl
w/WJTXVMJb074bYpytmx9WXL2mRTAgMBAAECgYB8/TuTKvbOTKXvkne2LWjQKjK+
jAdvrBqXQurJzh2/D6gCcw7ebrTcT5TX2uWfdl0VJd2AfeHURw8UqIRe0AHEzj98
yLF4DNgJRbGLCbJm67idyt41E+jnc6zrboLmpOkQ0njYibNulqgbJQ2DaFhnoLI1
1CoPW383WxUfV6LquQJBAPLbtfPs+tKrun7zOaJ0GWMYRZBFxVNKE9EQS4X2pg+g
NHo+b3bNPPoE66Ug+D69R1AYKC7QkP/zvXqT8dC1UVcCQQDC6MjeJqX4M4C9sfm6
x75VqmKNX6qaXzv28wrtMk7anSiR7YQUCs0kchHQz8hpYb+SQio4DhRYJyDHnO+f
V/tlAkEAl33IbYVpHNs0tZYhYJ4NbAwq8IklQwRVgjco9ULbaankslCaFUCHhepl
2BbN480MXAEFp9ZAbPjtPDZnRGqmdwJARxy1hM/nCAqKcyuHKM2NtSWoNVptHUuX
zj7YgrgzKvHiBnP5F3Lla+GufFgDD+fZBFzdmHwSkiMcg5fPmXx5PQJAfKdeiisM
1qDcCIFgHSD8JX3Di/oKPF/zyZ3dgFDC8SfqHh/0UuF3WBNUFHpHCO8CoZS/QioK
YKN9KVMfaZThsw==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC451WOI/1BtLSeaB2XJadUdfh7
AkJlHLcz4jeVTuemWLhT+E96A4VZJlkXi+dlhmimBq8ScpqjOpj7fAtwFzXfyhk6
WbV2qAb4yttZz5oA6aOpuZ4n4viEE15o68AzxK69MiKwLwzD/t8EJcP1iU11TCW9
O+G2KcrZsfVly9pkUwIDAQAB
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDMLq58c/Ox37b0NA4Ok65BcRRG+OmF1O+LtAIwRvmm8oAoGCCqBHM9V
AYItoUQDQgAEyqo4GGHqDU6XIBpDCzEfi7Z2EpUzmU/s46pJioQkd7tNYAb3Em2J
JJRFMK4l6WPlGze3zC66NaRZuyBagjDiVQ==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgHaEvjmM9ZMt0xCHT
Y65RBRkWxY9bBfl/Fag0bvP1r9OgCgYIKoEcz1UBgi2hRANCAATQeZSDbPzUA57d
UZTQBjdiY36CNk6ecsEuMvG3XpNxoJzome32RDEUkDc/qihPAmHaK48SCuVaoG5B
Hk+QBDaJ
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyqo4GGHqDU6XIBpDCzEfi7Z2EpUz
mU/s46pJioQkd7tNYAb3Em2JJJRFMK4l6WPlGze3zC66NaRZuyBagjDiVQ==
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,40 @@
<?php
// 说明: PHP自带的函数好像不支持椭圆相关的非对称加密,只支持rsa
$prifile = 'data/sm2.pem';
$pubfile = 'data/sm2pub.pem';
$prifile = 'data/rsa.pem';
$pubfile = 'data/rsapub.pem';
$data = "I love you!";
$priKey = openssl_pkey_get_private(file_get_contents($prifile));
$pubKey = openssl_pkey_get_public(file_get_contents($pubfile));
// var_dump($priKey);die();
// print_r(openssl_get_md_methods());
openssl_sign($data,$sign,$priKey,'sha256');
openssl_sign($data,$sign2,$priKey,'sha256WithRSAEncryption');
echo bin2hex($sign) ."\n";
echo bin2hex($sign2) ."\n";
exit();
echo "----------以下rsa的加解密----------------\n";
$res = openssl_private_encrypt($data, $encrypted, $priKey);
echo bin2hex($encrypted)."\n--------------------------\n";
$res = openssl_public_decrypt($encrypted,$decryptd,$pubKey);
echo $decryptd."\n--------------------------\n";
if($decryptd == $data){
echo "good !!!";
} else {
echo "bad !!!";
}

View File

@@ -0,0 +1,21 @@
<?php
// openssl支持密码对的生成
// 但未支持解签名,证书颁发openssl 1.1.1 版本目前 不支持 sm3withsm2, 因为sm3withsm2的算法与普通的sha256 with ecdsa的椭圆算法不一样
// git上有相关的gmssl或是tassl的基于openssl开发会支持需要安装然后替换掉当前的openssl, 并且将替换的openssl用源码方式编译到PHP中较麻烦
// 操作请参考: http://gmssl.org/docs/php-api.html
// 生成密码对
$config = array(
"private_key_type" => OPENSSL_KEYTYPE_EC,
"curve_name" => "SM2"
);
$sslconf = "/usr/local/php/extras/openssl/openssl.cnf";
$config['config'] = $sslconf;
$prikey = openssl_pkey_new($config);
openssl_pkey_export($prikey, $prikeypem,null,$config);
echo $prikeypem."\n";
$pubkeypem = openssl_pkey_get_details($prikey)["key"];
echo $pubkeypem."\n";

View File

@@ -0,0 +1,3 @@
<?php
$msg = '我爱你ILOVEYOU!';
echo openssl_digest($msg, 'sm3');

View File

@@ -0,0 +1,41 @@
<?php
/**
* openssl 1.1.1 已支持以下加密算法
* aes-128-cbc,aes-128-cbc-hmac-sha1,aes-128-cbc-hmac-sha256,aes-128-ccm,aes-128-cfb,aes-128-cfb1,aes-128-cfb8,aes-128-ctr,aes-128-ecb,aes-128-gcm,aes-128-ocb,aes-128-ofb,aes-128-xts,aes-192-cbc,aes-192-ccm,aes-192-cfb,aes-192-cfb1,aes-192-cfb8,aes-192-ctr,aes-192-ecb,aes-192-gcm,aes-192-ocb,aes-192-ofb,aes-256-cbc,aes-256-cbc-hmac-sha1,aes-256-cbc-hmac-sha256,aes-256-ccm,aes-256-cfb,aes-256-cfb1,aes-256-cfb8,aes-256-ctr,aes-256-ecb,aes-256-gcm,aes-256-ocb,aes-256-ofb,aes-256-xts,aes128,aes128-wrap,aes192,aes192-wrap,aes256,aes256-wrap,aria-128-cbc,aria-128-ccm,aria-128-cfb,aria-128-cfb1,aria-128-cfb8,aria-128-ctr,aria-128-ecb,aria-128-gcm,aria-128-ofb,aria-192-cbc,aria-192-ccm,aria-192-cfb,aria-192-cfb1,aria-192-cfb8,aria-192-ctr,aria-192-ecb,aria-192-gcm,aria-192-ofb,aria-256-cbc,aria-256-ccm,aria-256-cfb,aria-256-cfb1,aria-256-cfb8,aria-256-ctr,aria-256-ecb,aria-256-gcm,aria-256-ofb,aria128,aria192,aria256,bf,bf-cbc,bf-cfb,bf-ecb,bf-ofb,blowfish,camellia-128-cbc,camellia-128-cfb,camellia-128-cfb1,camellia-128-cfb8,camellia-128-ctr,camellia-128-ecb,camellia-128-ofb,camellia-192-cbc,camellia-192-cfb,camellia-192-cfb1,camellia-192-cfb8,camellia-192-ctr,camellia-192-ecb,camellia-192-ofb,camellia-256-cbc,camellia-256-cfb,camellia-256-cfb1,camellia-256-cfb8,camellia-256-ctr,camellia-256-ecb,camellia-256-ofb,camellia128,camellia192,camellia256,cast,cast-cbc,cast5-cbc,cast5-cfb,cast5-ecb,cast5-ofb,chacha20,chacha20-poly1305,des,des-cbc,des-cfb,des-cfb1,des-cfb8,des-ecb,des-ede,des-ede-cbc,des-ede-cfb,des-ede-ecb,des-ede-ofb,des-ede3,des-ede3-cbc,des-ede3-cfb,des-ede3-cfb1,des-ede3-cfb8,des-ede3-ecb,des-ede3-ofb,des-ofb,des3,des3-wrap,desx,desx-cbc,id-aes128-CCM,id-aes128-GCM,id-aes128-wrap,id-aes128-wrap-pad,id-aes192-CCM,id-aes192-GCM,id-aes192-wrap,id-aes192-wrap-pad,id-aes256-CCM,id-aes256-GCM,id-aes256-wrap,id-aes256-wrap-pad,id-smime-alg-CMS3DESwrap,idea,idea-cbc,idea-cfb,idea-ecb,idea-ofb,rc2,rc2-128,rc2-40,rc2-40-cbc,rc2-64,rc2-64-cbc,rc2-cbc,rc2-cfb,rc2-ecb,rc2-ofb,rc4,rc4-40,rc4-hmac-md5,seed,seed-cbc,seed-cfb,seed-ecb,seed-ofb,
* sm4,sm4-cbc,sm4-cfb,sm4-ctr,sm4-ecb,sm4-ofb
* 其中支持sm4相关的各种算法模式,缺省使用的是cbc算法
* 加密串短可以使用ecb计算速度快串长的话ecb的扩散性差安全性不足就使用带向量的其他算法如cbc
*/
// echo implode(',',openssl_get_cipher_methods(true));
// $ivlen = openssl_cipher_iv_length("sm4");
// $iv = openssl_random_pseudo_bytes($ivlen);
// sm4的iv长度是16字符
$key = "0123456789abcdef";
$iv = "1234567887654321";
$data = '我爱你ILOVEYOU!';
// openssl_encrypt($plaintext, "sms4", $key, $options=0, $iv);
// options = 0 base64, 1( OPENSSL_RAW_DATA ):原始二进制 , 这里为了与tsm4对比就使用hex方式
// sm4-ecb算法
$ciphertext =openssl_encrypt($data, "sm4-ecb", $key, $options=OPENSSL_RAW_DATA);
$enc = bin2hex($ciphertext);
echo "encrypt: $enc\n";
$original_plaintext = openssl_decrypt($ciphertext, "sm4-ecb", $key, $options=OPENSSL_RAW_DATA);
echo "encrypt: $original_plaintext\n";
// 缺省的sm4算法使用的是sm4-cbc模式
$ciphertext =openssl_encrypt($data, "sm4", $key, $options=OPENSSL_RAW_DATA,$iv);
// $ciphertext =openssl_encrypt($data, "sm4-cbc", $key, $options=OPENSSL_RAW_DATA,$iv); //这两个是一样的
$enc = bin2hex($ciphertext);
echo "encrypt: $enc\n";
$original_plaintext = openssl_decrypt($ciphertext, "sm4", $key, $options=OPENSSL_RAW_DATA,$iv);
echo "encrypt: $original_plaintext\n";
// sm4-cfb,sm4-ctr,sm4-ofb 的使用方法一样
$ciphertext =openssl_encrypt($data, "sm4-cfb", $key, $options=OPENSSL_RAW_DATA,$iv);
$enc = bin2hex($ciphertext);
echo "encrypt: $enc\n";
$original_plaintext = openssl_decrypt($ciphertext, "sm4-cfb", $key, $options=OPENSSL_RAW_DATA,$iv);
echo "encrypt: $original_plaintext\n";

View File

@@ -0,0 +1,24 @@
<?php
require_once '../vendor/autoload.php';
use Rtgm\util\FormatSign;
$goodSign = 'MEUCIQDWveKrtx6VrosnYQHNBnRjolrlmi/mHwMWKU4bDxakQwIgfSX20s+Ci1SvFQBgx+kRMU3Z1xbHtT0kpZfAXVH8poc=';
$badSign = 'MEYCIQDWveKrtx6VrosnYQHNBnRjolrlmi/mHwMWKU4bDxakQwIhAH0l9tLPgotUrxUAYMfpETFN2dcWx7U9JKWXwF1R/KaH';
$fs = new FormatSign();
$newSign = $fs->run($badSign);
echo $newSign."\n";
if ($newSign == $goodSign) {
echo "OK";
} else {
echo "Bad";
}
echo "\n=========\n";
//good 补0
$goodSign2 = "MEMCHx7T5iZF+kfk0mNDxVOX2ZOytWjcFBDCRMyUZsvdk\/8CICOZz0A91TlSbZWAhs8J24nWT35l1Su8zegr+vomI9P+";
echo (bin2hex(base64_decode($goodSign2)));
echo "\n";
$newSign2 = $fs->format_cmbc($goodSign2);
echo (bin2hex(base64_decode($newSign2)));

View File

@@ -0,0 +1,41 @@
<?php
require_once '../vendor/autoload.php';
use Rtgm\sm\RtSm2;
$publicKey = '043d9d4cc71a285af936b36880fd4d6155c22957cd2c84ea313469065207fb951b9ef1db79d69af8886e91e833da1ebc6bfdde86e70f52923d6e042eaa147624c7'; // 公钥
$privateKey = 'a7763cd4fe7db2a2146fc09bf2d5e5a30e10c51b7e4bed00b3a26ec79ba78ff3'; // 私钥
$publicfile = "./data/sm2pub.pem";
$privatefile = "./data/sm2.pem";
$userId = '1234567812345678';
$document = "我爱你ILOVEYOU!";
define('GK',1);
define('SIGN',1);
define('SIGNPEM',1);
//返回的签名16进制还是base64, 目前可选hex,与base64两种
// __construct($formatSign='hex', $randFixed = true)
$sm2 = new RtSm2('base64',true);
if(GK){
echo "\n----------生成明文密钥对--------------------------\n";
print_r($sm2->generatekey()); //生成明文密钥
echo "\n----------生成pem密钥对--------------------------\n";
print_r($sm2->generatePemkey()); //生成pem密钥请放到相应的文件中
}
if(SIGN){
echo "\n---------明文密钥签名---------------------------\n";
$sign = $sm2->doSign( $document, $privateKey, $userId);
print_r($sign);
echo "\n---------明文密钥验签---------------------------\n";
var_dump($sm2->verifySign( $document, $sign, $publicKey, $userId ));
}
if(SIGNPEM){
echo "\n---------PEM密钥签名---------------------------\n";
$sign = $sm2->doSignOutKey( $document, $privatefile, $userId);
print_r($sign);
echo "\n---------PEM密钥验签---------------------------\n";
var_dump($sm2->verifySignOutKey( $document, $sign, $publicfile, $userId ));
}

View File

@@ -0,0 +1,39 @@
<?php
require_once '../vendor/autoload.php';
use Rtgm\sm\RtSm2;
$sm2 = new RtSm2();
$publicKey = '043d9d4cc71a285af936b36880fd4d6155c22957cd2c84ea313469065207fb951b9ef1db79d69af8886e91e833da1ebc6bfdde86e70f52923d6e042eaa147624c7'; // 公钥
$privateKey = 'a7763cd4fe7db2a2146fc09bf2d5e5a30e10c51b7e4bed00b3a26ec79ba78ff3'; // 私钥
$document = str_repeat('abcdef',10);
// sm2的非对称加解密不建议加密太长的字符串
echo "原始: $document";
$m2EncryptData = $sm2 ->doEncrypt($document, $publicKey);
echo ("\n加密后: ".$m2EncryptData);
$m2DecryptData = $sm2->doDecrypt($m2EncryptData,$privateKey);
echo ("\n解密后:".$m2DecryptData);
echo "\n------------------------------------------------------------------\n";
$document = "我爱你ILOVEYOU!";
echo "\n原始: $document";
$m2EncryptData = $sm2 ->doEncrypt($document, $publicKey);
echo ("\n加密后: ".$m2EncryptData);
$m2DecryptData = $sm2->doDecrypt($m2EncryptData,$privateKey);
echo ("\n解密后: ".$m2DecryptData);
echo "\n------------------以上是标准的 c1c3c2 串----------------------------\n";
// define("C1C3C2",1);
// define("C1C2C3",0);
// doEncrypt($document, $publicKey, $model = C1C3C2),
// trim是如果加密后前面带着04就去掉
// doDecrypt($encryptData,$privateKey,$trim = true,$model = C1C3C2)
echo "\n------------------以下是使用 c1c2c3 串可对比上面生成------------------\n";
$document = "我爱你ILOVEYOU!";
echo "\n原始: $document";
$m2EncryptData = $sm2 ->doEncrypt($document, $publicKey,C1C2C3);
echo ("\n加密后: ".$m2EncryptData);
$m2DecryptData = $sm2->doDecrypt($m2EncryptData,$privateKey,1,C1C2C3);
echo ("\n解密后: ".$m2DecryptData);

View File

@@ -0,0 +1,6 @@
<?php
require_once '../vendor/autoload.php';
use Rtgm\sm\RtSm3;
$sm3 = new RtSm3();
$data = '我爱你ILOVEYOU!';
print_r($sm3->digest($data,1));

View File

@@ -0,0 +1,35 @@
<?php
require_once '../vendor/autoload.php';
use Rtgm\sm\RtSm4;
$key = "0123456789abcdef";
$iv = '1234567887654321';
$sm4 = new RtSm4($key);
$data = '我1爱你ILOVEYOU!!!';
$data = str_repeat('abc',7);
//sm4 的ecb 与cbc加密有补齐16*nl
// sm4->encrypt($data, $type = 'sm4', $iv = '', $format = 'hex')
// openssl_encrypt ,和服务器openssl版本PHP版本有关有些服务器可能不支持sm4-* 相关的对称加密算法,
echo "==== test sm4 sm4-cbc============";
echo "\nphp sm4: ".$hex = $sm4->encrypt($data,'sm4',$iv); //default is cbc
echo "\nphp decode: ".$sm4->decrypt($hex,'sm4',$iv,'hex');
echo "\n==== test sm4-ecb============";
echo "\nphp sm4-ecb: ".$hex = $sm4->encrypt($data,'sm4-ecb');
echo "\nphp decode: ".$sm4->decrypt($hex,'sm4-ecb','','hex');
echo "\n==== test sm4-ofb============";
echo "\nphp sm4-ofb: ".$hex = $sm4->encrypt($data,'sm4-ofb',$iv);
echo "\nphp decode: ".$sm4->decrypt($hex,'sm4-ofb',$iv,'hex');
echo "\n==== test sm4-cfb============";
echo "\nphp sm4-cfb: ".$hex = $sm4->encrypt($data,'sm4-cfb',$iv);
echo "\nphp decode: ".$sm4->decrypt($hex,'sm4-cfb',$iv,'hex');
echo "\n==== test sm4-ctr============";
echo "\nphp sm4-ctr: ".$hex = $sm4->encrypt($data,'sm4-ctr',$iv);
echo "\nphp decode: ".$sm4->decrypt($hex,'sm4-ctr',$iv,'hex');

View File

@@ -0,0 +1,28 @@
<?php
require_once '../vendor/autoload.php';
use Rtgm\sm\RtSm2;
$publicKey = '043d9d4cc71a285af936b36880fd4d6155c22957cd2c84ea313469065207fb951b9ef1db79d69af8886e91e833da1ebc6bfdde86e70f52923d6e042eaa147624c7'; // 公钥
$privateKey = 'a7763cd4fe7db2a2146fc09bf2d5e5a30e10c51b7e4bed00b3a26ec79ba78ff3'; // 私钥
$publicfile = "./data/sm2pub.pem";
$privatefile = "./data/sm2.pem";
$userId = '1234567812345678';
$document = "我爱你ILOVEYOU!";
define('GK',1);
define('SIGN',1);
define('SIGNPEM',1);
//返回的签名16进制还是base64, 目前可选hex,与base64两种
// __construct($formatSign='hex', $randFixed = true)
$sm2 = new RtSm2('base64',true);
if(SIGN){
echo "\n---------明文密钥签名---------------------------\n";
$sign = $sm2->doSign( $document, $privateKey, $userId);
print_r($sign);
echo "\n---------明文密钥验签---------------------------\n";
var_dump($sm2->verifySign( $document, $sign, $publicKey, $userId ));
}