国密算法是我国自主研发创新的一套数据加密处理系列算法。从SM1-SM4分别实现了对称、非对称、摘要等算法功能。特别适合应用于嵌入式物联网等相关领域,完成身份认证和数据加解密等功能。当然,默认的前提条件是算法密钥必须保证安全性,因此要将国密算法嵌入到硬件加密芯片中结合使用。
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。
由于SM1、SM4加解密的分组大小为128bit,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。
PHP调用linux命令实现,可实现SM2加解密签名验签,SM3摘要加密,SM4对称加密文件加密,不需要gmssl和PHP编译,变动小。
相对来讲,环境要求低,只要下载安装gmssl就好,并且与openssl兼容
其实在openssl1.1.1+上已经实现了国密算法C语言实现,openssl不用多介绍了吧(不知道的好好反思一下),有C扩展经验的同学可以自己实现C扩展,当然可以直接拿C语言实现国密算法的就当笑话看吧,原谅我C语言早还给大学了,所以写C扩展供PHP调用对我来说短时间还是没办法做到,立个flag,C扩展从入门到放弃走起。
但是在本文中并没有使用openssl,而是他的一个分支Gmssl,因为openssl命令行并没有提供SM2的加解密和签名验签(也可能是我没找到,o(╥﹏╥)o)
github:gmsslgit地址
gmssl网站:地址 有点耐心,这网站时不时打不开
下载gmssl:点击下载
unzip master.zip
cd GmSSL-master/
./config --prefix=/usr/local/gmssl --openssldir=/usr/local/gmssl no-shared //“--prefix=/usr/local/gmssl” 指定安装路径 “no-shared” 只编译静态库,不编译动态库,解决和openssl兼容问题
make
make install
/**
* php exec请求linux命令拼凑完整结果
*
* @param $command 命令行
*
* @return mixed|string
* @author [email protected]
*/
function cutil_exec($command) {
$str = '';
try {
$res = exec($command, $out);
if ($res) {
foreach ($out as $value) {
$str .= $value;
}
}
}
catch (Exception $e) {
}
return $str;
}
/**
*
* 生成国密SM2非对称加密公钥
*
* @param $key 标识key
* @param string $path 公钥文件存储路径
*
* @return String 返回公钥信息
*/
function generateSm2PubKey($key = "", $path = '/var/www/html/sm2/') {
if (!file_exists($path . $key . '.key')) {
$cmd = "/usr/local/gmssl/bin/gmssl ec -in " . $path . $key . "_priv.key -pubout -out " . $path . $key . "_pub.key";
$publicKey = "";
$res = cutil_exec($cmd);
if ($res) {
if (file_exists($path . $key . '_pub.key')) {
$publicKey = file_get_contents($path . $key . '_pub.key');
}
}
return $publicKey;
}
return file_get_contents($path . $key . '_pub.key');
}
/**
* 生成SM2国密私钥
*
* @param $key 私钥标识
* @param string $path 私钥文件路径
*
* @return false|string 返回私钥信息
*/
function generateSm2PrivateKey($key, $path = "/var/www/html/sm2/") {
$privateKey = "";
if (!file_exists($path)) {
mkdir($path, 0775);
}
if (file_exists($path . $key . '_priv.key')) {
return file_get_contents($path . $key . '_priv.key');
}
$cmd = '/usr/local/gmssl/bin/gmssl ecparam -genkey -name SM2 -out ' . $path . $key . '_priv.key';
$res = cutil_exec($cmd);
if ($res) {
if (file_exists($path . $key . '_priv.key')) {
$privateKey = file_get_contents($path . $key . '_priv.key');
}
}
return $privateKey;
}
/**
* 国密SM2加签
*
* @param $key 标识key
* @param $str 加签字符串
* @param string $path 秘钥文件路径
*
* @return bool 返回加签是否成功
*/
function sm2Sign($key, $str, $path = "/var/www/html/sm2/") {
$cmd = 'echo -n ' . $str . '|/usr/local/gmssl/bin/gmssl sm2utl -sign -inkey ' . $path . $key . '_priv.key -id infogo -out ' . $path . $key . '_sign.key';
$res = cutil_exec($cmd);
if ($res == '') {
return true;
}
else {
// 记录日志
}
return false;
}
/**
* SM2国密验签
*
* @param $key 标识key
* @param $str 验签字符串
* @param string $path 秘钥文件路径
*
* @return bool 返回验签是否通过
*/
function sm2Verify($key, $str, $path = "/var/www/html/sm2/") {
try {
$cmd = 'echo -n ' . $str . '|/usr/local/gmssl/bin/gmssl sm2utl -verify -sigfile ' . $path . $key . '_sign.key -pubin -inkey ' . $path . $key . '_pub.key -id infogo';
$res = cutil_exec($cmd);
if ($res == 'Signature Verification Successful') {
return true;
}
}
catch (Exception $e) {
// 记录日志
}
return false;
}
/**
* SM2国密加密
*
* @param $key
* @param $str
* @param string $path
*
* @return mixed|string
* @author [email protected]
*/
function sm2Encrypt($key, $str, $path = "/var/www/html/sm2/") {
$res = "";
try {
$str = iconv('gbk', 'utf-8', $str);
$cmd = 'echo -n ' . $str . '|/usr/local/gmssl/bin/gmssl sm2utl -encrypt -pubin -inkey ' . $path . $key . '_pub.key|base64';
$res = cutil_exec($cmd);
}
catch (Exception $e) {
// 记录日志
}
return $res;
}
/**
* sm2国密解密
*
* @param $key 标识key
* @param $str 解密字符串
* @param string $path 秘钥路径
*
* @return false|string
*/
function sm2Decrypt($key, $str, $path = "/var/www/html/sm2/") {
$res = "";
try {
$res = cutil_exec_wait('/sh/gm.sh sm2dec' . $str);
$res = iconv('utf-8', 'gbk', $res);
}
catch (Exception $e) {
// 记录日志
}
return $res;
}
/**
* SM3加密摘要
*
* @param $str
*
* @return mixed
*/
function sm3Encrypt($str) {
try {
$str = iconv('gbk', 'utf-8', $str);
$cmd = 'echo -n "' . $str . '"|/usr/local/gmssl/bin/gmssl dgst -sm3 ';
$res = cutil_exec($cmd);
if ($res) { // 字符串处理去掉标准输出标识
$temp = explode('=', $res);
$res = trim($temp[1]);
}
return $res;
}
catch (Exception $e) {
// 记录日志
}
return false;
}
/**
* SM4加密字符串
*
* @param $str 需要加密的字符串
* @param string $type 加密类型,默认sms4 注意这里的加密类型和openssl不同,此处为sms4-*
* @param string $sign 签名值 默认test
*
* @return mixed
*/
function sm4EncryptStr($str, $sign = 'test', $type = "sms4") {
try {
$str = iconv('gbk', 'utf-8', $str);
$key = md5($sign);
$cmd = 'echo ' . $str . '|/usr/local/gmssl/bin/gmssl enc -e -' . $type . ' -salt -K ' . $key . ' -iv 462d53c8abac0|base64';
$res = cutil_exec($cmd);
return $res;
}
catch (Exception $e) {
// 记录日志
}
return false;
}
/**
* SM4解密字符串
*
* @param $str 需要解密的字符串
* @param string $type 加密类型,默认为sm4,默认sms4 注意这里的加密类型和openssl不同,此处为sms4-*
* @param string $sign 签名值
*
* @return mixed
*/
function sm4DecryptStr($str, $sign = 'test', $type = "sms4") {
try {
$str = iconv('gbk', 'utf-8', $str);
$key = md5($sign);
$cmd = 'echo ' . $str . '|base64 -d -i|/usr/local/gmssl/bin/gmssl enc -d -' . $type . ' -iv 462d53c8abac0 -K ' . $key;
$res = iconv('utf-8', 'gbk', cutil_exec($cmd));
return $res;
}
catch (Exception $e) {
// 记录日志
}
return false;
}
/**
* sm4加密文件(暂时保留,不启用)
*
* @param $fileName 文件名称
* @param string $type 加密方式,默认为sm4,默认sms4 注意这里的加密类型和openssl不同,此处为sms4-*
*
* @return bool|false|string
*/
function sm4EncryptFile($fileName, $type = 'sms4') {
try {
$cmd = '/gm1.sh sm4fileenc ' . $fileName . ' ' . $type;
$res = cutil_exec('/gm1.sh sm4fileenc ' . $fileName . ' ' . $type);
if ($res == "") {
// sm4DecryptFile($fileName);
return file_get_contents($fileName);
}
}
catch (Exception $e) {
// 记录日志
}
return false;
}
/**
* sm4解密文件
*
* @param $fileName 文件名称
* @param string $type 加密方式,默认为sm4,默认sms4 注意这里的加密类型和openssl不同,此处为sms4-*
* @param bool $flag 标识返回文件内容
*
* @return bool
*/
function sm4DecryptFile($fileName, $type = 'sms4', $flag = false) {
try {
$res = cutil_exec_wait('/sh/gm.sh sm4filedec ' . $fileName . ' ' . $type);
if ($res == '') {
if ($flag) { // 返回文件内容
return file_get_contents($fileName);
}
// 返回文件名称
return $fileName;
}
}
catch (Exception $e) {
// 记录日志
}
return false;
}
shell脚本gm.sh部分内容:
#!/bin/bash
case $1 in
'sm2dec')
# SM2解密
echo -n $2|base64 -d -i|/usr/local/gmssl/bin/gmssl sm2utl -decrypt -inkey /var/www/html/download/sm2/net_auth_priv.key
;;
'sm4fileenc')
# SM4文件加密
# $2 输入文件名
# $3 加密方式
# 备份源文件
mv $2 $2.bak
# 加密生成源文件
/usr/local/gmssl/bin/gmssl enc -e -$3 -a -salt -in $2.bak -out $2 -k infogo
;;
'sm4filedec')
# SM4文件解密
# $2 输入文件名
# $3 解密方式与加密方式对应
# 重命名文件,避免错误
mv $2 $2.temp
# 解密重命名后的文件生成源文件
/usr/local/gmssl/bin/gmssl enc -d -$3 -a -salt -in $2.temp -out $2 -k infogo
# 移除临时文件
rm -f $2.temp
;;
esac