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