PHP 扩展SmSSL 支持国密sm2 sm3 sm4代码实现
GmSSL的PHP语言绑定
GmSSL项目通过PHP的扩展的方式为PHP语言提供国密算法的支持,应用通过openssl为前缀的函数实现国密算法和标准相关的杂凑计算、加密、数字签名、X.509证书、数字信封、SSL通信等功能。
编译与安装
GmSSL的PHP扩展代码位于源代码中的的php/ext/openssl目录,PHP扩展需要和PHP源代码目录树一起编译,过程如下:
- 下载并编译安装GmSSL;
- 下载最新的PHP源代码并解压;
- 用GmSSL的php/ext/openssl目录替代PHP源代码中的ext/openssl目录;
- 执行./configure –with-openssl; make; sudo make install编译和安装PHP;
- 修改PHP配置文件php.ini启用openssl扩展,即取消php.ini中extension=openssl前面的注释。
- 可以通过PHP的phpinfo()函数确认openssl扩展已经启用,通过ldd命令检查PHP的二进制程序调用了GmSSL的动态库而不是系统默认的OpenSSL动态库。
首先下载并编译安装GmSSL-2.0代码,在Linux操作系统中通常默认的编译选项为—prefix=/usr/local和—openssldir=/usr/local/ssl。然后从源代码编译PHP。PHP的默认配置不编译OpenSSL扩展,需要显式指定—with-openssl才会编译OpenSSL扩展。可以通过ldd检查安装好的php可执行程序是否链接了预期的库。
示例
显示扩展的GmSSL库版本号
常量OPENSSL_VERSION_TEXT是GmSSL版本号的字符串,可以通过这个版本字符串判断当前系统的openssl扩展是否为GmSSL版。
<?php printf("Versoin : %s\n", OPENSSL_VERSION_TEXT); ?>
显示扩展支持的密码算法
通过openssl_get_md_methods()可以获得扩展支持的密码杂凑函数,其中应包含sm3算法;通过openssl_get_cipher_methods()可以获得扩展支持的对称加密方案,其中应包含sms4和zuc的多个加密方案;通过openssl_get_curve_names()可以获得扩展内置的命名椭圆曲线参数,其中应包含sm2p256v1和wapip192v1两条国密曲线。
<?php print_r(openssl_get_md_methods(false)); print_r(openssl_get_cipher_methods(true)); print_r(openssl_get_curve_names()); ?>
计算SM3杂凑值
通过openssl_digest()可以计算消息摘要,注意在指定SM3算法的时候,即可以用算法的名字字符串,也可以用内置的整数常量OPENSSL_ALGO_SM3。
<?php $msg = "abc"; printf("sm3(\"%s\") = %s\n", $msg, openssl_digest($msg, "sm3")); printf("sm3(\"%s\") = %s\n", $msg, openssl_digest($msg, OPENSSL_ALGO_SM3)); ?>
SM4加密和解密
通过openssl_random_pseudo_bytes()可以生成SMS4加密密钥和IV,通过openssl_encrypt()和openssl_decrypt()可以实现加密和解密。
<?php $key = openssl_random_pseudo_bytes(16); $ivlen = openssl_cipher_iv_length("sms4"); $iv = openssl_random_pseudo_bytes($ivlen); $plaintext = "message to be encrypted"; $ciphertext = openssl_encrypt($plaintext, "sms4", $key, $options=0, $iv); $original_plaintext = openssl_decrypt($ciphertext, "sms4", $key, $options=0, $iv); printf("sms4enc(\"%s\") = %s\n", $plaintext, bin2hex($ciphertext)); printf("sms4dec(%s) = \"%s\"\n", bin2hex($ciphertext), $original_plaintext); ?>
生成SM2密钥对
<?php $prikey = openssl_pkey_new(array( "private_key_type" => OPENSSL_KEYTYPE_EC, "curve_name" => "sm2p256v1" )); openssl_pkey_export($prikey, $prikeypem); echo $prikeypem; $pubkeypem = openssl_pkey_get_details($prikey)["key"]; echo $pubkeypem; $pubkey = openssl_pkey_get_public($pubkeypem); ?>
也可以从证书和私钥文件中导入
<?php $pubkey = openssl_pkey_get_public("file://localhost-signcer.pem"); $prikey = openssl_pkey_get_private("file://localhost-signkey.pem"); ?>
SM2签名与验签
扩展通过openssl_sign()和openssl_verify()函数提供对任意长消息进行杂凑并签名的功能,当选择SM3作为杂凑算法,sm2p256v1曲线上的密钥时,执行标准的带Z值(签名者ID采用默认值)的SM2签名。
<?php openssl_sign($msg, $signature, $prikey, "sm3"); $ok = openssl_verify($msg, $signature, $pubkey, OPENSSL_ALGO_SM3); ?>
注意,验证签名结果只有为1时才签名正确,其他值均表示错误。
SM2封装与解封
扩展通过openssl_seal和openssl_open提供公钥封装和解封的功能,当选择sm2p256v曲线上的椭圆曲线密钥时,执行SM2的封装和解封。封装的主要流程是随机生成SMS4密钥,用该密钥以CBC模式加密输入数据,用SM2公钥加密随机生成的SMS4密钥。
<?php openssl_seal($plaintext, $sealed, $ekeys, array($pubkey), "sms4", $iv); openssl_open($sealed, $opened, $ekeys[0], $prikey, "sms4", $iv); ?>
签发SM2证书
扩展通过openssl_csr_new生成新的CSR,并通过openssl_csr_sign函数用CA证书和私钥对CSR签名,生成SM2证书。
<?php $key = openssl_pkey_new(array( "private_key_type" => OPENSSL_KEYTYPE_EC, "curve_name" => "sm2p256v1")); $dn = array( "countryName" => "CN", "stateOrProvinceName" => "Beijing", "localityName" => "Beijing2", "organizationName" => "Peking University", "organizationalUnitName" => "GmSSL Group", "commonName" => "Alice", "emailAddress" => "alice@gmssl.org" ); $csr = openssl_csr_new($dn, $key, array("digest_alg" => "sm3")); $serial = 5876; $cacert = "file://cacert.pem"; $cakey = array("file://cakey.pem", "Your password"); $cert = openssl_csr_sign($csr, $cacert, $cakey, $days=365, array( "digest_alg" => "sm3" ), $serial); openssl_pkey_export_to_file($key, "key.pem", "Your password", array("encrypt_key_cipher" => OPENSSL_CIPHER_SMS4_CBC)); openssl_x509_export_to_file($cert, "cert.pem", $notext=true); ?>
导出PKCS #12文件
PKCS #12格式的文件是将用户的证书和私钥打包并用口令加密保护,一些软件在导入用户证书时需要提供PKCS #12格式的文件。扩展通过openssl_pkcs12_export_to_file()函数可以将用户的证书和私钥打包生成PKCS #12格式的文件。下面的例子中$cert和$key是已经生成的用户证书和私钥,”cert.p12″是待导出的文件。
<?php openssl_pkcs12_export_to_file($cert, "cert.p12", $key, "Your password"); ?>