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