┌──────┬────────────┬─────────────┬──────────────────────────────────┐──────────────────────────────────────────────── │ 算法 │ 类型 │ 对标 │ 用途 │ ├──────┼────────────┼─────────────┼──────────────────────────────────┤──────────────────────────────────────────────── │ SM2 │ 非对称加密 │ RSA/ECDSA │ 数字签名、密钥交换、加密小数据 │ ├──────┼────────────┼─────────────┼──────────────────────────────────┤ │ SM3 │ 哈希 │ SHA-256│ 数据完整性校验、数字签名前的摘要 │ ├──────┼────────────┼─────────────┼──────────────────────────────────┤ │ SM4 │ 对称加密 │ AES-128│ 大数据加密、文件加密、传输加密 │ └──────┴────────────┴─────────────┴──────────────────────────────────┘ 实际项目里三个经常组合用SM2 交换密钥 → SM4 加密数据 → SM3 校验完整性。---PHP 扩展选型 方案一gmssl 扩展推荐 GmSSL 是国内主流国密库有官方 PHP 扩展。 # 先安装 GmSSL git clone https://github.com/guanzhi/GmSSL cd GmSSLmkdir buildcd build cmake..makemake install # 安装 PHP 扩展 pecl install gmssl;php.ini extensiongmssl 方案二纯 PHP 实现无需扩展 composer require tjfoc/gmsm # 不行这是 Go 的 composer require idealo/php-sm-crypto # 纯PHP性能差但无依赖 composer require furqansiddiqui/ecc-php 国内用得最多的纯 PHP 库 composer require lybc/phpgm 方案三openssl 命令行调用 OpenSSL1.1.1已支持国密可以通过 exec 调用但不推荐生产用。---SM3 哈希 最简单先从这个开始。 使用 gmssl 扩展 classSM3{public staticfunctionhash(string $data):string{returngmssl_sm3($data);}public staticfunctionhmac(string $data,string $key):string{returngmssl_sm3_hmac($key,$data);}public staticfunctionverify(string $data,string $hash):bool{returnhash_equals(self::hash($data),$hash);}}使用纯 PHP 实现 use Lybc\PhpGm\Sm3;$hashSm3::hash(hello world);// 返回 64 位十六进制字符串// 用于替代 SHA256 做密码哈希$passwordHashSm3::hash($password.$salt);实际应用替代 MD5/SHA1 做数据校验 classDataIntegrity{public staticfunctionsign(array $data,string $secret):string{ksort($data);$strhttp_build_query($data).key.$secret;returnstrtoupper(SM3::hash($str));}public staticfunctionverify(array $data,string $sign,string $secret):bool{returnhash_equals(self::sign($data,$secret),$sign);}}---SM4 对称加密 基本用法 use Lybc\PhpGm\Sm4;classSM4Cipher{private const KEY_LENGTH16;// SM4 密钥固定 128 位 16 字节public staticfunctionencrypt(string $plaintext,string $key,string $iv):string{self::validateKey($key);if(empty($iv)){$ivrandom_bytes(16);}$sm4newSm4($key,cbc);// CBC 模式$ciphertext$sm4-encrypt($plaintext,$iv);// 将 IV 拼在密文前面解密时取出returnbase64_encode($iv.$ciphertext);}public staticfunctiondecrypt(string $ciphertext,string $key):string{self::validateKey($key);$rawbase64_decode($ciphertext);$ivsubstr($raw,0,16);$datasubstr($raw,16);$sm4newSm4($key,cbc);return$sm4-decrypt($data,$iv);}private staticfunctionvalidateKey(string $key):void{if(strlen($key)!self::KEY_LENGTH){thrownew\InvalidArgumentException(SM4 key must be 16 bytes);}}}使用 gmssl 扩展性能更好 classSM4GmSSL{public staticfunctionencrypt(string $plaintext,string $key):string{$ivrandom_bytes(GMSSL_SM4_BLOCK_SIZE);$ciphertextgmssl_sm4_cbc_encrypt($key,$iv,$plaintext);returnbase64_encode($iv.$ciphertext);}public staticfunctiondecrypt(string $encoded,string $key):string{$rawbase64_decode($encoded);$ivsubstr($raw,0,GMSSL_SM4_BLOCK_SIZE);$ciphertextsubstr($raw,GMSSL_SM4_BLOCK_SIZE);returngmssl_sm4_cbc_decrypt($key,$iv,$ciphertext);}}实际应用数据库敏感字段加密 classEncryptedModelextendsModel{// 需要加密存储的字段protected array $encryptable[id_card,bank_card,phone];private staticfunctiongetKey():string{returnconfig(app.sm4_key);// 16字节从环境变量读}publicfunctionsetAttribute($key,$value):mixed{if(in_array($key,$this-encryptable)$value!null){$valueSM4Cipher::encrypt($value,self::getKey());}returnparent::setAttribute($key,$value);}publicfunctiongetAttribute($key):mixed{$valueparent::getAttribute($key);if(in_array($key,$this-encryptable)$value!null){returnSM4Cipher::decrypt($value,self::getKey());}return$value;}}---SM2 非对称加密 生成密钥对 use Lybc\PhpGm\Sm2;classSM2KeyPair{public staticfunctiongenerate():array{$sm2newSm2();$keypair$sm2-createKey();return[private_key$keypair[private_key],// 十六进制public_key$keypair[public_key],// 十六进制04开头];}public staticfunctionsaveToFiles(array $keypair,string $dir):void{file_put_contents($dir./sm2_private.key,$keypair[private_key]);file_put_contents($dir./sm2_public.key,$keypair[public_key]);chmod($dir./sm2_private.key,0600);}}加密与解密 classSM2Cipher{// SM2 加密用公钥public staticfunctionencrypt(string $plaintext,string $publicKey):string{$sm2newSm2();$ciphertext$sm2-encrypt($plaintext,$publicKey);returnbase64_encode($ciphertext);}// SM2 解密用私钥public staticfunctiondecrypt(string $ciphertext,string $privateKey):string{$sm2newSm2();return$sm2-decrypt(base64_decode($ciphertext),$privateKey);}}数字签名 classSM2Signer{// 签名私钥签SM3 做摘要public staticfunctionsign(string $data,string $privateKey):string{$sm2newSm2();$hashSM3::hash($data);$signature$sm2-sign($hash,$privateKey);returnbase64_encode($signature);}// 验签公钥验public staticfunctionverify(string $data,string $signature,string $publicKey):bool{$sm2newSm2();$hashSM3::hash($data);return$sm2-verify($hash,base64_decode($signature),$publicKey);}}实际应用接口请求签名 classApiSigner{public staticfunctionsignRequest(array $params,string $privateKey):array{ksort($params);$params[timestamp]time();$params[nonce]bin2hex(random_bytes(8));$signStrhttp_build_query($params);$params[sign]SM2Signer::sign($signStr,$privateKey);$params[sign_type]SM2;return$params;}public staticfunctionverifyRequest(array $params,string $publicKey):bool{// 防重放时间戳5分钟内有效if(abs(time()-$params[timestamp])300){returnfalse;}$sign$params[sign];unset($params[sign],$params[sign_type]);ksort($params);$signStrhttp_build_query($params);returnSM2Signer::verify($signStr,$sign,$publicKey);}}---三算法组合信封加密 实际传输大数据时的标准做法 classSM2SM4Envelope{/** * 加密 * 1. 随机生成 SM4 密钥 * 2. SM4 加密数据 * 3. SM2 加密 SM4 密钥 * 4. SM3 对密文做完整性校验 */public staticfunctionseal(string $plaintext,string $sm2PublicKey):array{$sm4Keyrandom_bytes(16);$ciphertextSM4Cipher::encrypt($plaintext,$sm4Key);$encryptedKeySM2Cipher::encrypt(bin2hex($sm4Key),$sm2PublicKey);$macSM3::hmac($ciphertext,$sm4Key);return[encrypted_key$encryptedKey,ciphertext$ciphertext,mac$mac,];}/** * 解密反向操作 */public staticfunctionopen(array $envelope,string $sm2PrivateKey):string{$sm4KeyHexSM2Cipher::decrypt($envelope[encrypted_key],$sm2PrivateKey);$sm4Keyhex2bin($sm4KeyHex);// 验证完整性$expectedMacSM3::hmac($envelope[ciphertext],$sm4Key);if(!hash_equals($expectedMac,$envelope[mac])){thrownew\RuntimeException(数据完整性校验失败);}returnSM4Cipher::decrypt($envelope[ciphertext],$sm4Key);}}---与 HTTPS/TLS 的国密改造 如果需要 HTTPS 层也用国密等保三级有时要求 # Nginx 需要编译 tongsuo 或 BabaSSL 支持 ssl_protocols TLSv1.2TLSv1.3;ssl_ciphersECC-SM2-SM4-CBC-SM3:ECDHE-SM2-SM4-CBC-SM3:...;ssl_certificate/path/to/sm2_cert.pem;ssl_certificate_key/path/to/sm2_key.pem;PHP 层不需要改动TLS 握手在 Nginx 层完成。---常见坑 密钥长度 SM4 密钥必须是16字节不能用字符串直接当密钥要用hash(sm3,$password,true)派生后取前16字节。 编码问题 SM2 公钥有压缩02/03开头和非压缩04开头两种格式不同库默认不同互通时要确认格式一致。 C04 格式 部分政务系统要求 SM2 密文用 C1C3C2 顺序而不是默认的 C1C2C3对接时要问清楚。 性能 纯 PHP 实现的 SM2 很慢签名一次约50-200ms高并发场景必须用 gmssl 扩展或异步队列处理。---选型建议 ┌────────────────────┬─────────────────────────┐ │ 场景 │ 推荐方案 │ ├────────────────────┼─────────────────────────┤ │ 等保合规、政务对接 │ gmssl 扩展硬件密码机 │ ├────────────────────┼─────────────────────────┤ │ 普通商业项目 │ 纯 PHP 库lybc/phpgm │ ├────────────────────┼─────────────────────────┤ │ 数据库字段加密 │ SM4 CBC 模式 │ ├────────────────────┼─────────────────────────┤ │ 接口签名 │ SM2SM3 │ ├────────────────────┼─────────────────────────┤ │ 大文件传输加密 │ 信封加密SM2SM4SM3 │ └────────────────────┴─────────────────────────┘