接口簽名與數(shù)據(jù)加密

接口簽名與數(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ù)安全.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市萨西,隨后出現(xiàn)的幾起案子有鹿,更是在濱河造成了極大的恐慌,老刑警劉巖谎脯,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葱跋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡源梭,警方通過(guò)查閱死者的電腦和手機(jī)娱俺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)废麻,“玉大人荠卷,你說(shuō)我怎么就攤上這事≈蚶ⅲ” “怎么了油宜?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵掂碱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我慎冤,道長(zhǎng)疼燥,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任蚁堤,我火速辦了婚禮醉者,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘披诗。我一直安慰自己撬即,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布呈队。 她就那樣靜靜地躺著搞莺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掂咒。 梳的紋絲不亂的頭發(fā)上才沧,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音绍刮,去河邊找鬼温圆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛孩革,可吹牛的內(nèi)容都是我干的岁歉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼膝蜈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锅移!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起饱搏,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤非剃,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后推沸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體备绽,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鬓催,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肺素。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宇驾,死狀恐怖倍靡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情课舍,我是刑警寧澤塌西,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布蜗顽,位于F島的核電站,受9級(jí)特大地震影響雨让,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忿等,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一栖忠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贸街,春花似錦庵寞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逸尖,卻和暖如春古沥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娇跟。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工岩齿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苞俘。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓盹沈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親吃谣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乞封,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容