GmSSL的PHP语言绑定

GmSSL项目通过PHP的扩展的方式为PHP语言提供国密算法的支持,应用通过openssl为前缀的函数实现国密算法和标准相关的杂凑计算、加密、数字签名、X.509证书、数字信封、SSL通信等功能。

编译与安装

GmSSL的PHP扩展代码位于源代码中的的php/ext/openssl目录,PHP扩展需要和PHP源代码目录树一起编译,过程如下:

  1. 下载并编译安装GmSSL;
  2. 下载最新的PHP源代码并解压;
  3. 用GmSSL的php/ext/openssl目录替代PHP源代码中的ext/openssl目录;
  4. 执行./configure --with-openssl; make; sudo make install编译和安装PHP;
  5. 修改PHP配置文件php.ini启用openssl扩展,即取消php.iniextension=openssl前面的注释。
  6. 可以通过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()可以获得扩展支持的对称加密方案,其中应包含sms4zuc的多个加密方案;通过openssl_get_curve_names()可以获得扩展内置的命名椭圆曲线参数,其中应包含sm2p256v1wapip192v1两条国密曲线。

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