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