
1. 为什么PHP开发者需要关注SM2国密算法最近几年在金融、政务等领域国密算法SM2的应用越来越广泛。作为PHP开发者我们经常需要对接银行、支付平台等机构的接口而这些接口很多都要求使用SM2加密。但很多同行在使用PHP处理SM2证书时都遇到了各种问题比如证书解析失败、加密报错等。SM2是基于椭圆曲线密码学(ECC)的非对称加密算法相比传统的RSA算法有几个明显优势安全性更高256位的ECC相当于3072位的RSA运算速度更快签名和验签速度比RSA快很多密钥长度更短256位的私钥比2048位的RSA私钥短很多在实际项目中我发现PHP内置的openssl扩展对SM2支持并不完善。比如openssl_pkey_get_public()函数在解析SM2证书时就经常报错。这是因为openssl默认不支持国密算法需要额外安装扩展。2. 搭建支持SM2的PHP环境2.1 安装GmSSL扩展要让PHP支持SM2我们需要安装GmSSL扩展。GmSSL是国密算法的开源实现提供了完整的SM2、SM3、SM4算法支持。安装步骤如下# 克隆GmSSL-PHP扩展 git clone https://github.com/GmSSL/GmSSL-PHP.git cd GmSSL-PHP # 编译安装 phpize ./configure make sudo make install # 在php.ini中添加扩展 echo extensiongmssl.so /path/to/php.ini安装完成后可以通过phpinfo()查看是否加载成功。如果看到gmssl模块说明安装成功。2.2 验证SM2支持安装完成后我们可以用以下代码测试SM2是否可用if (!extension_loaded(gmssl)) { die(GmSSL扩展未加载); } if (!function_exists(gmssl_sm2_encrypt)) { die(SM2加密函数不可用); } echo SM2支持正常;3. SM2证书的解析与处理3.1 证书格式转换在实际项目中我们拿到的证书可能是多种格式的常见的有PEM、DER、CER等。PHP处理SM2证书前最好先统一转换为PEM格式。# DER转PEM openssl x509 -in cert.der -inform der -outform pem -out cert.pem # CER转PEM openssl x509 -in cert.cer -outform pem -out cert.pem转换完成后可以用文本编辑器打开PEM文件确认格式是否正确。正确的PEM格式应该以-----BEGIN CERTIFICATE-----开头以-----END CERTIFICATE-----结尾。3.2 解析SM2证书使用GmSSL扩展解析SM2证书比openssl扩展更可靠$cert file_get_contents(sm2.pem); $pubkey gmssl_get_publickey($cert); if ($pubkey false) { die(解析证书失败: . gmssl_error()); } echo 证书解析成功;这里gmssl_get_publickey()函数专门用于解析SM2证书的公钥避免了openssl_pkey_get_public()的兼容性问题。4. 实现SM2加密与解密4.1 SM2加密有了公钥后我们就可以用SM2加密数据了$data 需要加密的敏感数据; $encrypted gmssl_sm2_encrypt($pubkey, $data); if ($encrypted false) { die(加密失败: . gmssl_error()); } file_put_contents(encrypted.dat, $encrypted);加密后的数据是二进制格式可以直接保存到文件或传输给接收方。4.2 SM2解密解密需要私钥私钥通常以PEM格式存储$privateKey file_get_contents(private.pem); $encrypted file_get_contents(encrypted.dat); $decrypted gmssl_sm2_decrypt($privateKey, $encrypted); if ($decrypted false) { die(解密失败: . gmssl_error()); } echo 解密结果: . $decrypted;注意私钥文件需要妥善保管泄露私钥会导致加密数据被破解。5. 常见问题与解决方案5.1 证书解析失败如果遇到证书解析失败可以尝试以下步骤确认证书格式是否正确检查证书是否包含SM2公钥尝试用openssl命令验证证书openssl x509 -in cert.pem -noout -text如果openssl能解析但GmSSL不能可能是证书编码问题5.2 加密/解密失败加密解密失败通常有几个原因公钥/私钥不匹配加密数据长度超过SM2限制(最大长度与密钥长度相关)密钥文件损坏或格式不正确可以先用openssl验证密钥对是否匹配# 提取公钥 openssl x509 -pubkey -noout -in cert.pem pubkey.pem # 验证密钥对 openssl pkeyutl -verify -inkey private.pem -pubin -in pubkey.pem5.3 性能优化SM2虽然比RSA快但在高并发场景下仍可能成为瓶颈。可以考虑使用连接池复用加密上下文对小数据采用对称加密(SM4)用SM2加密对称密钥启用硬件加速(如果服务器支持)6. 实际应用案例6.1 与支付平台对接最近在对接某银行支付接口时对方要求所有请求数据都用SM2加密。我们使用GmSSL扩展实现了以下流程从银行提供的CER证书导出PEM格式公钥用SM2加密请求参数将加密后的数据Base64编码后传输接收响应后用私钥解密关键代码片段// 加密请求数据 $requestData json_encode($params); $encrypted gmssl_sm2_encrypt($bankPubKey, $requestData); $base64Encoded base64_encode($encrypted); // 发送请求... // 解密响应 $responseEncrypted base64_decode($responseBody); $decrypted gmssl_sm2_decrypt($ourPrivateKey, $responseEncrypted); $responseData json_decode($decrypted, true);6.2 敏感数据存储我们系统中有一些需要加密存储的用户敏感信息使用SM2加密后的存储方案为每个用户生成独立的SM2密钥对用户数据加密后存储只有用相应用户的私钥才能解密数据私钥由用户自己保管(如通过客户端加密后上传)这种方案即使数据库泄露攻击者也无法解密数据。7. 进阶话题7.1 证书链验证在金融等对安全性要求高的场景通常需要验证证书链获取根CA证书和中间证书构建证书链验证终端证书的有效性GmSSL提供了证书链验证功能$rootCA file_get_contents(rootCA.pem); $intermediateCA file_get_contents(intermediateCA.pem); $endCert file_get_contents(endCert.pem); $chain [$endCert, $intermediateCA]; $result gmssl_verify_cert_chain($rootCA, $chain); if ($result ! true) { die(证书验证失败: . gmssl_error()); }7.2 签名与验签除了加密解密SM2还可以用于数字签名// 签名 $data 重要数据; $signature gmssl_sm2_sign($privateKey, $data); // 验签 $valid gmssl_sm2_verify($pubkey, $data, $signature); if ($valid) { echo 签名验证通过; } else { echo 签名验证失败; }签名功能在合同、交易等需要防篡改的场景非常有用。7.3 与其他语言的交互在实际项目中可能需要与其他语言开发的系统交互。SM2加密的数据可以通过以下方式跨语言使用统一使用PEM格式的公钥/私钥加密后的数据使用ASN.1 DER编码传输时使用Base64编码例如Java可以使用BouncyCastle库处理SM2加密数据与PHP互通。