PHP+gmssl命令行实现国密加解密

1. 国密介绍

国密算法是我国自主研发创新的一套数据加密处理系列算法。从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,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。

2. 实现方法

PHP调用linux命令实现,可实现SM2加解密签名验签,SM3摘要加密,SM4对称加密文件加密,不需要gmssl和PHP编译,变动小。

2. 环境要求

相对来讲,环境要求低,只要下载安装gmssl就好,并且与openssl兼容
其实在openssl1.1.1+上已经实现了国密算法C语言实现,openssl不用多介绍了吧(不知道的好好反思一下),有C扩展经验的同学可以自己实现C扩展,当然可以直接拿C语言实现国密算法的就当笑话看吧,原谅我C语言早还给大学了,所以写C扩展供PHP调用对我来说短时间还是没办法做到,立个flag,C扩展从入门到放弃走起。
但是在本文中并没有使用openssl,而是他的一个分支Gmssl,因为openssl命令行并没有提供SM2的加解密和签名验签(也可能是我没找到,o(╥﹏╥)o)

github:gmsslgit地址
gmssl网站:地址 有点耐心,这网站时不时打不开

3. 安装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

4. bug片段

/**
 * 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章