.gitignore000064400000000037150736750010006540 0ustar00vendor2/ vendor/ composer.lock README.md000064400000005177150736750010006041 0ustar00# php sm2 sm3 sm4 国密算法整理 * php版本的国密sm2的签名算法,非对称加解密算法(非对称加密刚上线,目前测试无问题,不能保证兼容其他语言,有问题可以提issues),sm3的hash, sm4的对称加解密,要求PHP7,打开gmp支持 * 目前如果服务器配套的使用的是openssl 1.1.1x, 目前到1.1.1.l(L) ,sm3,sm4都可以直接用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.4下做的 ### SM2 * 签名验签算法主体基于PHPECC算法架构,添加了sm2的椭圆参数, * 参考了 https://github.com/ToAnyWhere/phpsm2 童鞋的sm2验签算法,密钥生成算法 * 添加了签名算法, 支持sm2的16进制,base64公私钥的签名,验签算法 * 支持从文件中读取pem文件的签名,验签算法 * 添加了sm2的非对称加密的算法,但速度一般,有待优化,不能保证兼容所有语言进行加解密,目前测试了js, python的相互加解密 * sm2的加密解密算法在openssl 1.1.1的版本下自带的函数中暂无sm2的公钥私钥的加密函数,得自己实现,建议使用C,C++的算法,打包成PHP扩展的方式 * 由于 openssl没有实现sm2withsm3算法,用系统函数无法实现签名及证书的自签名分发 ### SM3 * 该算法直接使用 https://github.com/ToAnyWhere/phpsm2 中sm2签名用到的匹配sm3, 未做修改 * 也可使用 openssl的函数, 详见openssl_tsm3.php ### SM4 * 该算法直接封装使用 https://github.com/lizhichao/sm 的sm4算法, 同时该项目支持 sm3,sm4 ,可以composer安装 * 由于sm4-ecb, sm4-cbc加密需要补齐,项目lizhichao/sm项目未做补齐操作,这里封装的时候,针对这两个算法做了补齐操作, 其他如sm4-ctr,sm4-cfb,sm4-ofb等,可以直接用 * 在openssl 1.1.1下可使用系统的函数,已支持sm4-cbc,sm4-cfb,sm4-ctr,sm4-ecb,sm4-ofb, 详见openssl_tsm4.php ### 总结 * 这里封装的测试函数已与相关的js, python, java都可以互签互认 * js: https://github.com/JuneAndGreen/sm-crypto * python: https://github.com/duanhongyi/gmssl * java: https://github.com/ZZMarquis/gmhelper * openssl: 升到1.1.1以后,支持sm3,sm4的加解密,还不支持sm2的公私钥加解密,也不支持sm2的签名,得使用原生代码实现,签名中需要实现sm2withsm3, openssl1.1.1只实现了sm2whithsha256 composer.json000064400000001153150736750010007272 0ustar00{ "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/" } } } composer.lock000064400000014777150736750010007271 0ustar00{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "25389224104081d04661b5268219fba0", "packages": [ { "name": "fgrosse/phpasn1", "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/fgrosse/PHPASN1.git", "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296", "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296", "shasum": "", "mirrors": [ { "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", "preferred": true } ] }, "require": { "php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0" }, "require-dev": { "php-coveralls/php-coveralls": "~2.0", "phpunit/phpunit": "^6.3 || ^7.0 || ^8.0" }, "suggest": { "ext-bcmath": "BCmath is the fallback extension for big integer calculations", "ext-curl": "For loading OID information from the web if they have not bee defined statically", "ext-gmp": "GMP is the preferred extension for big integer calculations", "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { "FG\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Friedrich Große", "email": "friedrich.grosse@gmail.com", "homepage": "https://github.com/FGrosse", "role": "Author" }, { "name": "All contributors", "homepage": "https://github.com/FGrosse/PHPASN1/contributors" } ], "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", "homepage": "https://github.com/FGrosse/PHPASN1", "keywords": [ "DER", "asn.1", "asn1", "ber", "binary", "decoding", "encoding", "x.509", "x.690", "x509", "x690" ], "support": { "issues": "https://github.com/fgrosse/PHPASN1/issues", "source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0" }, "time": "2021-12-11T12:41:06+00:00" }, { "name": "mdanter/ecc", "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/phpecc/phpecc.git", "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpecc/phpecc/zipball/34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", "shasum": "", "mirrors": [ { "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", "preferred": true } ] }, "require": { "ext-gmp": "*", "fgrosse/phpasn1": "^2.0", "php": "^7.0||^8.0" }, "require-dev": { "phpunit/phpunit": "^6.0||^8.0||^9.0", "squizlabs/php_codesniffer": "^2.0", "symfony/yaml": "^2.6|^3.0" }, "type": "library", "autoload": { "psr-4": { "Mdanter\\Ecc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Matyas Danter", "homepage": "http://matejdanter.com/", "role": "Author" }, { "name": "Thibaud Fabre", "email": "thibaud@aztech.io", "homepage": "http://aztech.io", "role": "Maintainer" }, { "name": "Thomas Kerin", "email": "afk11@users.noreply.github.com", "role": "Maintainer" } ], "description": "PHP Elliptic Curve Cryptography library", "homepage": "https://github.com/phpecc/phpecc", "keywords": [ "Diffie", "ECDSA", "Hellman", "curve", "ecdh", "elliptic", "nistp192", "nistp224", "nistp256", "nistp384", "nistp521", "phpecc", "secp256k1", "secp256r1" ], "support": { "issues": "https://github.com/phpecc/phpecc/issues", "source": "https://github.com/phpecc/phpecc/tree/v1.0.0" }, "time": "2021-01-16T19:42:14+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.2" }, "platform-dev": [], "plugin-api-version": "2.1.0" } src/ecc/Curves/CurveFactory.php000064400000010103150736750010012460 0ustar00curveSm2(); } $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); } } src/ecc/RtEccFactory.php000064400000002351150736750010011133 0ustar00 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; } } src/ecc/Sm2Curve.php000064400000003445150736750010010256 0ustar00adapter = $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); } } src/ecc/Sm2Signer.php000064400000010312150736750010010410 0ustar00adapter = $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 ) ); } } src/overwrite.php000064400000000664150736750010010104 0ustar00 __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); src/sm/RtSm2.php000064400000031777150736750010007455 0ustar00adapter = RtEccFactory::getAdapter(); $this->generator = RtEccFactory::getSmCurves()->generatorSm2(); if(in_array($formatSign,$this->arrFormat)){ $this->formatSign = $formatSign; } else { $this->formatSign = 'hex'; } } /** * 随机生成一对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 * @return string */ public function doEncrypt($document, $publicKey) { $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); return $c1.$c3.$c2; } /** * SM2 私钥解密算法 * * @param string $document * @param string $privateKey * @return string */ public function doDecrypt($encryptData,$privateKey) { // $encryptData = $c1.$c3.$c2 $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); $c3 = substr($encryptData,$c1Length,64); $c2 = substr($encryptData,$c1Length+strlen($c3)); $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())); if($c3 == $c3_){ //hash签名相同, return $document; } else { return ''; } } /** * SM2 签名明文16进制密码 * */ public function doSign( $document, $privateKey, $userId = null ) { if ( empty( $userId ) ) { $userId = $this->userId; } $adapter = $this->adapter; $generator = $this->generator; $algorithm = 'sha256'; $secret = gmp_init( $privateKey, 16 ); $key = new PrivateKey( $adapter, $generator, $secret ); return $this->_dosign( $document, $key, $adapter, $generator, $userId, $algorithm ); } /** * SM2 签名pem密码 * */ public function doSignOutKey( $document, $privateKeyFile, $userId = null ) { if ( empty( $userId ) ) { $userId = $this->userId; } if ( !file_exists( $privateKeyFile ) ) { throw new \Exception( 'privatekey file not exists' ); } $adapter = $this->adapter; $generator = $this->generator; //这个sha256 只是生成随机数时用到,和主体算法无关 $algorithm = 'sha256'; $pemSerializer = new PemPrivateKeySerializer( new DerPrivateKeySerializer( $adapter ) ); $keyData = file_get_contents( $privateKeyFile ); $key = $pemSerializer->parse( $keyData ); return $this->_dosign( $document, $key, $adapter, $generator, $userId, $algorithm ); } protected function _dosign( $document, $key, $adapter,$generator, $userId, $algorithm = 'sha256' ) { $publickey = $key->getPublicKey(); $obPoint = $key->getPublicKey()->getPoint(); $pubKeyX = $adapter->decHex( $obPoint->getX() ); $pubKeyY = $adapter->decHex( $obPoint->getY() ); $hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId ); # Derandomized signatures are not necessary, but is avoids # the risk of a low entropy RNG, causing accidental reuse # of a k value for a different message, which leaks the # private key. if ( $this->useDerandomizedSignatures ) { $random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getHmacRandomGenerator( $key, $hash, $algorithm ); } else { $random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getRandomGenerator(); } $randomK = $random->generate( $generator->getOrder() ); $signer = new Sm2Signer( $adapter ); $signature = $signer->sign( $key, $hash, $randomK ); $serializer = new DerSignatureSerializer(); $serializedSig = $serializer->serialize( $signature ); if($this->formatSign == 'hex') { return bin2hex($serializedSig); } else if($this->formatSign == 'base64' ) { return base64_encode( $serializedSig ) . PHP_EOL; } //缺省 hex return bin2hex($serializedSig); } public function verifySign( $document, $sign, $publicKey, $userId = null ) { $adapter = $this->adapter; $generator = $this->generator; if ( empty( $userId ) ) { $userId = $this->userId; } if($this->formatSign == 'hex') { $sigData = hex2bin($sign); } else if($this->formatSign == 'base64' ) { $sigData = base64_decode( $sign ); } else { $sigData = hex2bin($sign); } // Parse signature $sigSerializer = new DerSignatureSerializer(); $sig = $sigSerializer->parse( $sigData ); // get hash list( $pubKeyX, $pubKeyY ) = $this->_getKeyXY( $publicKey ); $hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId ); // get pubkey parse $key = $this->_getPubKeyObject( $pubKeyX, $pubKeyY ); $signer = new Sm2Signer( $adapter ); return $signer->verify( $key, $sig, $hash ); } public function verifySignOutKey( $document, $sign, $publickeyFile, $userId = null ) { if ( empty( $userId ) ) { $userId = $this->userId; } if ( !file_exists( $publickeyFile ) ) { throw new \Exception( 'publickey file not exists' ); } $adapter = $this->adapter; $generator = $this->generator; if($this->formatSign == 'hex') { $sigData = hex2bin($sign); } else if($this->formatSign == 'base64' ) { $sigData = base64_decode( $sign ); } else { $sigData = hex2bin($sign); } // Parse signature $sigSerializer = new DerSignatureSerializer(); $sig = $sigSerializer->parse( $sigData ); // Parse public key $keyData = file_get_contents( $publickeyFile ); $derSerializer = new DerPublicKeySerializer( $adapter ); $pemSerializer = new PemPublicKeySerializer( $derSerializer ); $key = $pemSerializer->parse( $keyData ); $pubKeyX = $this->decHex( $key->getPoint()->getX() ); $pubKeyY = $this->decHex( $key->getPoint()->getY() ); $hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId ); $signer = new Sm2Signer( $adapter ); return $signer->verify( $key, $sig, $hash ); } /** * */ protected function _doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId ) { $hasher = new Sm2WithSm3(); $hash = $hasher->getSm3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId ); return gmp_init( Hex2ByteBuf::ByteArrayToHexString( $hash ), 16 ); } protected function _getKeyXY( $publicKey ) { if ( strlen( $publicKey ) == 128 ) { $pubKeyX = substr( $publicKey, 0, 64 ); $pubKeyY = substr( $publicKey, -64 ); } else if ( strlen( $publicKey ) == 130 && substr( $publicKey, 0, 2 ) == '04' ) { $pubKeyX = substr( $publicKey, 2, 64 ); $pubKeyY = substr( $publicKey, -64 ); } else { throw new \Exception( 'publickey format error' ); } return [$pubKeyX, $pubKeyY]; } protected function _getPubKeyObject( $pubKeyX, $pubKeyY ) { $generator = $this->generator; // __construct( GmpMathInterface $adapter, CurveFpInterface $curve, \GMP $x, \GMP $y, \GMP $order = null, bool $infinity = false ) $x = gmp_init( $pubKeyX, 16 ); $y = gmp_init( $pubKeyY, 16 ); $point = new Point( $this->adapter, $generator->getCurve(), $x, $y ); // __construct( GmpMathInterface $adapter, GeneratorPoint $generator, PointInterface $point ) return new PublicKey( $this->adapter, $this->generator, $point ); } protected function _str2hex($str){ $res = array(); for($i=0; $iBlockUpdate($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; $ism4 = 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'); } } } src/smecc/SM2/Cipher.php000064400000005717150736750010010767 0ustar00sm3keybase = 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; } } src/smecc/SM2/Hex2ByteBuf.php000064400000004620150736750010011634 0ustar00getPublicKeyFrom($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; } */ } src/smecc/SM2/Sm2WithSm3.php000064400000010664150736750010011432 0ustar00Update( ($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; } } src/smecc/SM3/GeneralDigest.php000064400000004347150736750010012271 0ustar00arraycopy($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; } } src/smecc/SM3/SM3Digest.php000064400000021650150736750010011312 0ustar00Reset(); } 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)); } }src/smecc/SM4/Sm4.php000064400000023664150736750010010223 0ustar00ck16($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); } }src/smecc/SM4OLD/README.md000064400000000043150736750010010647 0ustar00# sm 国密sm3,sm4 ECB加密源码 src/smecc/SM4OLD/SM3.php000064400000011731150736750010010511 0ustar00message = $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

* An offset to check for. *

* * @return bool true on success or false on failure. *

*

* 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

* The offset to retrieve. *

* * @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

* The offset to assign the value to. *

* @param mixed $value

* The value to set. *

* * @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

* The offset to unset. *

* * @return void * @since 5.0.0 */ public function offsetUnset($offset) { unset($this->hash_value[$offset]); } } src/smecc/SM4OLD/Sm4.php000064400000020052150736750010010546 0ustar00_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]; } } } src/smecc/SM4OLD/handler/BigJHandler.php000064400000003227150736750010013636 0ustar00extended($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, W′0, W′1, · · · , W′63, * 用于压缩函数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] ) ); } } } src/smecc/SM4OLD/handler/JHandler.php000064400000003357150736750010013220 0ustar00setT($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); } } src/smecc/SM4OLD/handler/README.md000064400000000546150736750010012274 0ustar00# *handle/* 目录 本目录包含了加密过程中的核心算法 * `\SM3\handler\JHandler` 根据J的不同,进行不同处理的抽象类 * `\SM3\handler\SmalJHandler` 变量j处于较小值时的处理类 * `\SM3\handler\BigJHandler` 变量j处于较大值时的处理类 * `\SM3\handler\Substitution` 置换函数类src/smecc/SM4OLD/handler/SmallJHandler.php000064400000003307150736750010014204 0ustar00X = $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); } } src/smecc/SM4OLD/libs/README.md000064400000000411150736750010011577 0ustar00# *libs/* 目录 本目录包含了 *动态读写配置* 和 *一些底层的基础操作(如:按位运算)* * `\SM3\libs\Config` 配置类,获取和动态设定配置 * `\SM3\libs\WordConversion` 字的位运算类,包含了位的各种运算 src/smecc/SM4OLD/libs/WordConversion.php000064400000015642150736750010014026 0ustar00= 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); } } src/smecc/SM4OLD/types/BitString.php000064400000011732150736750010013161 0ustar00getString(); } 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('/(?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

* An offset to check for. *

* * @return bool true on success or false on failure. *

*

* 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

* The offset to assign the value to. *

* @param mixed $value

* The value to set. *

* * @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

* The offset to unset. *

* * @return void * @since 5.0.0 */ public function offsetUnset($offset) { unset($this->bit_string[$offset]); } } src/smecc/SM4OLD/types/README.md000064400000000232150736750010012013 0ustar00# *types/* 目录 本目录为自定义的扩展类型的目录 * `\SM3\types\BitString` 比特串 类型 * `\SM3\types\Word` 字 类型src/smecc/SM4OLD/types/Word.php000064400000002641150736750010012166 0ustar00bit_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; } } test/data/rsa.pem000064400000001624150736750010007733 0ustar00-----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----- test/data/rsapub.pem000064400000000420150736750010010433 0ustar00-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC451WOI/1BtLSeaB2XJadUdfh7 AkJlHLcz4jeVTuemWLhT+E96A4VZJlkXi+dlhmimBq8ScpqjOpj7fAtwFzXfyhk6 WbV2qAb4yttZz5oA6aOpuZ4n4viEE15o68AzxK69MiKwLwzD/t8EJcP1iU11TCW9 O+G2KcrZsfVly9pkUwIDAQAB -----END PUBLIC KEY----- test/data/sm2.pem000064400000000342150736750010007643 0ustar00-----BEGIN EC PRIVATE KEY----- MHcCAQEEIDMLq58c/Ox37b0NA4Ok65BcRRG+OmF1O+LtAIwRvmm8oAoGCCqBHM9V AYItoUQDQgAEyqo4GGHqDU6XIBpDCzEfi7Z2EpUzmU/s46pJioQkd7tNYAb3Em2J JJRFMK4l6WPlGze3zC66NaRZuyBagjDiVQ== -----END EC PRIVATE KEY-----test/data/sm2pub.pem000064400000000261150736750010010352 0ustar00-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyqo4GGHqDU6XIBpDCzEfi7Z2EpUz mU/s46pJioQkd7tNYAb3Em2JJJRFMK4l6WPlGze3zC66NaRZuyBagjDiVQ== -----END PUBLIC KEY-----test/openssl_encryptsm2.php000064400000001716150736750010012116 0ustar00 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"; test/openssl_tsm3.php000064400000000104150736750010010664 0ustar00generatekey()); //生成明文密钥 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 )); } test/tsm2_encrypt.php000064400000001642150736750010010674 0ustar00doEncrypt($document, $publicKey); echo ("加密后: ".$m2EncryptData); $m2DecryptData = $sm2->doDecrypt($m2EncryptData,$privateKey); echo "\n===========\n"; echo ("解密后:".$m2DecryptData); $document = "我爱你ILOVEYOU!"; echo "\n===========\n"; $m2EncryptData = $sm2 ->doEncrypt($document, $publicKey); echo ("加密后: ".$m2EncryptData); $m2DecryptData = $sm2->doDecrypt($m2EncryptData,$privateKey); echo "\n===========\n"; echo ("解密后:".$m2DecryptData);test/tsm3.php000064400000000221150736750010007121 0ustar00digest($data,1));test/tsm4.php000064400000002440150736750010007127 0ustar00encrypt($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');