接口簽名與數(shù)據(jù)加密
前言
公司業(yè)務(wù)主要是和第三方機(jī)構(gòu)合作,遇到過(guò)各種各樣的加密,今天就來(lái)簡(jiǎn)單講解一下常見(jiàn)的加密方式
簡(jiǎn)單簽名
有些機(jī)構(gòu)的加密方式簡(jiǎn)單一些,常見(jiàn)遇到會(huì)用md5和sha1.
$sign_key = '5eb63bbbe01eeed093cb22bb8f5acdc3'; // 機(jī)構(gòu)提供
$post_data = [
'order_no' => 'A2018040323437434',
'time' => time(),
'status' => 'hello world',
];
$post_data['sign'] = md5($post_data['order_no'] . $post_data['time'] . $sign_key); // 防止一個(gè)簽名重復(fù)使用,且可以在接口處驗(yàn)證時(shí)間,時(shí)間與當(dāng)前時(shí)間差大則說(shuō)明請(qǐng)求偽造
上面的$post_data就是一些機(jī)構(gòu)的簡(jiǎn)單簽名方式,一般由機(jī)構(gòu)提供key和簽名規(guī)則,常見(jiàn)和key和時(shí)間戳識(shí)別符聯(lián)合加密,也有將整個(gè)請(qǐng)求參數(shù)用遞歸方式拼接加密.sha1大致與上方相同.這種簽名方式僅僅是對(duì)接口做了加密,可是請(qǐng)求數(shù)據(jù)和返回?cái)?shù)據(jù)還是十分清晰的,一旦讓別人捕獲,可以很簡(jiǎn)單的識(shí)別出其中的關(guān)鍵信息.于是就有了數(shù)據(jù)可逆加密
數(shù)據(jù)可逆加密
這里就介紹兩種常見(jiàn)的可逆加密
discuz提供的一個(gè)方案
/**
* 加解密函數(shù)
* @param $string
* @param string $operation
* @param string $key
* @param int $expiry
* @return bool|string
*/
function encrypt($string, $operation = 'DECODE', $key = 'dhyuerwbcytwbzghn', $expiry = 0)
{
// 動(dòng)態(tài)密匙長(zhǎng)度嵌牺,相同的明文會(huì)生成不同密文就是依靠動(dòng)態(tài)密匙
$ckey_length = 4;
// 密匙
$key = md5($key);
// 密匙a會(huì)參與加解密
$keya = md5(substr($key, 0, 16));
// 密匙b會(huì)用來(lái)做數(shù)據(jù)完整性驗(yàn)證
$keyb = md5(substr($key, 16, 16));
// 密匙c用于變化生成的密文
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length):
substr(md5(microtime()), -$ckey_length)) : '';
// 參與運(yùn)算的密匙
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
// 明文,前10位用來(lái)保存時(shí)間戳兼砖,解密時(shí)驗(yàn)證數(shù)據(jù)有效性摊溶,10到26位用來(lái)保存$keyb(密匙b)欲虚,
//解密時(shí)會(huì)通過(guò)這個(gè)密匙驗(yàn)證數(shù)據(jù)完整性
// 如果是解碼的話纯续,會(huì)從第$ckey_length位開(kāi)始蔬充,因?yàn)槊芪那?ckey_length位保存 動(dòng)態(tài)密匙牧抵,以保證解密正確
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) :
sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
// 產(chǎn)生密匙簿
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
// 用固定的算法笛匙,打亂密匙簿,增加隨機(jī)性犀变,好像很復(fù)雜妹孙,實(shí)際上對(duì)并不會(huì)增加密文的強(qiáng)度
for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
// 核心加解密部分
for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
// 從密匙簿得出密匙進(jìn)行異或,再轉(zhuǎn)成字符
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == 'DECODE') {
// 驗(yàn)證數(shù)據(jù)有效性获枝,請(qǐng)看未加密明文的格式
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) &&
substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
} else {
// 把動(dòng)態(tài)密匙保存在密文里蠢正,這也是為什么同樣的明文,生產(chǎn)不同密文后能解密的原因
// 因?yàn)榧用芎蟮拿芪目赡苁且恍┨厥庾址沉眨瑥?fù)制過(guò)程可能會(huì)丟失机隙,所以用base64編碼
return $keyc.str_replace('=', '', base64_encode($result));
}
}
$encode_str = encrypt('hello world!', 'ENCODE');
echo $encode_str . PHP_EOL;
$decode_str = encrypt($encode_str);
echo $decode_str . PHP_EOL;
輸出
fab9fhs559JrIfE97/H5dbLn/oztFxJEltuUG3rQiSf8Q5r1yeAORV4
hello world!
這個(gè)加密方案簡(jiǎn)單,常用于站內(nèi)加密,比如訂單號(hào)用戶號(hào)之類的信息需要作為參數(shù),可是又不想讓用戶在前端看到則可以用這個(gè)加密方案
DES對(duì)稱加密
以des-cbc/pksc5填充為例結(jié)果base64編碼
<?php
class CryptDes
{
private $key;
public function __construct($key)
{
$this->key = $key;
}
/**
* des加密
* @desc des加密CBC模式,PKCS5Padding填充
* @param $str
* @return string
*/
public function encrypt($str)
{
$str = $this->pkcs5Pad($str, 8);
if (strlen($str) % 8) {
$str = str_pad($str,
strlen($str) + 8 - strlen($str) % 8, "\0");
}
return base64_encode(openssl_encrypt($str, 'DES-ECB', substr($this->key, 0, 8), OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
}
/**
* des解密
* @param $str
* @return string
*/
public function decrypt($str)
{
$decode_str = openssl_decrypt(base64_decode($str), 'DES-ECB', substr($this->key, 0, 8), OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
return $this->pkcs5Unpad($decode_str);
}
/**
* PKCS5Padding填充
* @param $text
* @param $blocksize
* @return string
*/
private function pkcs5Pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
/**
* PKCS5Padding填充逆向
* @param $text
* @return bool|string
*/
private function pkcs5Unpad($text)
{
$pad = ord($text{strlen($text) - 1});
if ($pad > strlen($text))
return false;
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad)
return false;
return substr($text, 0, -1 * $pad);
}
}
$des = new CryptDes('9#HL&sk8s5Fw#q&8');
$str = 'hello world!';
$encode = $des->encrypt($str);
$decode = $des->decrypt($encode);
print_r([
'str' => $str,
'encode' => $encode,
'decode' => $decode,
]);
輸出
Array
(
[str] => hello world!
[encode] => y1pB2YaGeaBkNui7Wu5ReA==
[decode] => hello world!
)
RSA非對(duì)稱加密
RSA非對(duì)稱加密需要生成一個(gè)公鑰和一個(gè)私鑰.用法也是眾說(shuō)紛紜,有人說(shuō)保留公鑰,將私鑰提供給機(jī)構(gòu),有人說(shuō)保留私鑰,將公鑰提供給機(jī)構(gòu),本人傾向于后者.
用法,將公鑰提供給機(jī)構(gòu),像機(jī)構(gòu)發(fā)起請(qǐng)求時(shí)用自己私鑰加密,這是解決了證明我是我的問(wèn)題(請(qǐng)求確實(shí)由自己發(fā)起)機(jī)構(gòu)返回內(nèi)容或發(fā)起請(qǐng)求同理,將他們的公鑰給我們.代碼示例如下
<?php
/**
* Created by PhpStorm.
* User: mc
* Date: 18/4/3
* Time: 下午6:01
*/
class RSA
{
public $encrypt_len;
public $public_key;
public $private_key;
public function __construct($encrypt_len, $public_key, $private_key)
{
$this->encrypt_len = $encrypt_len;
$this->public_key = $public_key;
$this->private_key = $private_key;
}
/**
* 私鑰加密
* @param $data_content
* @return string
*/
public function encryptedByPrivateKey($data_content)
{
$data_content = base64_encode($data_content);
$encrypted = "";
$totalLen = strlen($data_content);
$encrypt_pos = 0;
while ($encrypt_pos < $totalLen) {
openssl_private_encrypt(substr($data_content, $encrypt_pos, $this->encrypt_len), $encrypt_data, $this->private_key);
$encrypted .= bin2hex($encrypt_data);
$encrypt_pos += $this->encrypt_len;
}
return $encrypted;
}
/**
* 公鑰解密
* @param $encrypted
* @return bool|string
*/
public function decryptByPublicKey($encrypted)
{
$decrypt = "";
$totalLen = strlen($encrypted);
$decryptPos = 0;
while ($decryptPos < $totalLen) {
openssl_public_decrypt(hex2bin(substr($encrypted, $decryptPos, $this->encrypt_len * 8)), $decryptData, $this->public_key);
$decrypt .= $decryptData;
$decryptPos += $this->encrypt_len * 8;
}
//openssl_public_decrypt($encrypted, $decryptData, $this->public_key);
$decrypt = base64_decode($decrypt);
return $decrypt;
}
}
$public_key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6NILnfGpOGTSJztKAZ8fGLLV7
Ad6PUPIeCwHz9qQ87fbEp0/eHTm2e+LgJRseRerTYLeLplqxDSqJgDToPBQOdtIQ
EqBw/C7abBskscTss+PCEjI+IHdxT1BDMoH45ofPfasizLV4wZ3WWJJhmt/gxJH3
benGi5ZJ4ksBPpJXowIDAQAB
-----END PUBLIC KEY-----'
;
$private_key = '-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALo0gud8ak4ZNInO
0oBnx8YstXsB3o9Q8h4LAfP2pDzt9sSnT94dObZ74uAlGx5F6tNgt4umWrENKomA
NOg8FA520hASoHD8LtpsGySxxOyz48ISMj4gd3FPUEMygfjmh899qyLMtXjBndZY
kmGa3+DEkfdt6caLlkniSwE+klejAgMBAAECgYEAjEhPbtKezCPVHxWAJVkKetTo
DLoF0HctUVD9sazZY0XsKY/bbf0ao86FyFRsL8yA86rj3QQBQ24l492A/o10lX21
R4u4Dc3EdtNnoY0FmAcoFRU2tHHMgknkI1tFsvKbWiCUnGiWlv98Db+OcVjQkH28
eHYZK6UPEeUagydmmKECQQD2STIWfyYSSqwpo1FVfOUgrFIyYSbB2sFCKe7CluIa
oAxYxXi8HA6Eu3eDOUUqD/yRIdNTFuGKXWaDGFbB6sQ5AkEAwYyqljpRSBmPv0GY
yc00MUsuD4TimvL8J0oz9kEFzyOfnhCUdVD2j95Z1k++EZYdQ7lcg4JX0X8eOulp
gpESuwJAdOr6pENoR3a7lGi7y+GmxIQJ4XDNfWnkJQzTE/2dCRbBxcK5NlP7cHeu
nNUrSHSeaiesst1B5PXCHKoJRbW1wQJAHPK3COUMByabk1VyTqx8Y+sEppmPcvFo
uU+l2ez7u3FujCuaqLlFR1tQQHeIzASRt/FfXuP90n2aveDvQPIFxQJAA1Aa0ZFh
Jyqj6hMGSPBivL3dVzV3XMEumoAyDfYsxJ4ub30F+UmvXri6Zhu3FAW+akuoHAhO
l7WbvdsSSeVKTA==
-----END PRIVATE KEY-----';
$encrypt_len = 32;
$rsa = new RSA($encrypt_len, $public_key, $private_key);
$test_content = 'Hello World';
$encode_content = $rsa->encryptedByPrivateKey($test_content);
$decode_content = $rsa->decryptByPublicKey($encode_content);
echo $decode_content;
輸出
cafbs7cVaaE+YZd0gMmq2Ys4fV0m6MMrdQcxw1wmSXDMCMiEOrRHKA4
hello world!
結(jié)束語(yǔ)
上面是對(duì)接機(jī)構(gòu)常用到的加密方案,當(dāng)然這只是簡(jiǎn)單距離實(shí)際應(yīng)用當(dāng)中還有許多的變通之處.常見(jiàn)的如ip限制進(jìn)一步確保數(shù)據(jù)安全.