接口签名与数据加密

接口签名与数据加密

前言

公司业务主要是和第三方机构合作,遇到过各种各样的加密,今天就来简单讲解一下常见的加密方式

简单签名

有些机构的加密方式简单一些,常见遇到会用md5和sha1.

$sign_key = '5eb63bbbe01eeed093cb22bb8f5acdc3'; // 机构提供
$post_data = [
    'order_no' => 'A2018040323437434',
    'time' => time(),
    'status' => 'hello world',
];
$post_data['sign'] = md5($post_data['order_no'] . $post_data['time'] . $sign_key); // 防止一个签名重复使用,且可以在接口处验证时间,时间与当前时间差大则说明请求伪造

上面的$post_data就是一些机构的简单签名方式,一般由机构提供key和签名规则,常见和key和时间戳识别符联合加密,也有将整个请求参数用递归方式拼接加密.sha1大致与上方相同.这种签名方式仅仅是对接口做了加密,可是请求数据和返回数据还是十分清晰的,一旦让别人捕获,可以很简单的识别出其中的关键信息.于是就有了数据可逆加密

数据可逆加密

这里就介绍两种常见的可逆加密

discuz提供的一个方案

/**
 * 加解密函数
 * @param $string
 * @param string $operation
 * @param string $key
 * @param int $expiry
 * @return bool|string
 */
function encrypt($string, $operation = 'DECODE', $key = 'dhyuerwbcytwbzghn', $expiry = 0)
{
    // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
    $ckey_length = 4;

    // 密匙
    $key = md5($key);

    // 密匙a会参与加解密
    $keya = md5(substr($key, 0, 16));
    // 密匙b会用来做数据完整性验证
    $keyb = md5(substr($key, 16, 16));
    // 密匙c用于变化生成的密文
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length):
        substr(md5(microtime()), -$ckey_length)) : '';
    // 参与运算的密匙
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);
    // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),
    //解密时会通过这个密匙验证数据完整性
    // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
    $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();
    // 产生密匙簿
    for($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    }
    // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
    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;
        // 从密匙簿得出密匙进行异或,再转成字符
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if($operation == 'DECODE') {
        // 验证数据有效性,请看未加密明文的格式
        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 {
        // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
        // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用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!

这个加密方案简单,常用于站内加密,比如订单号用户号之类的信息需要作为参数,可是又不想让用户在前端看到则可以用这个加密方案

DES对称加密

以des-cbc/pksc5填充为例结果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非对称加密

RSA非对称加密需要生成一个公钥和一个私钥.用法也是众说纷纭,有人说保留公钥,将私钥提供给机构,有人说保留私钥,将公钥提供给机构,本人倾向于后者.
用法,将公钥提供给机构,像机构发起请求时用自己私钥加密,这是解决了证明我是我的问题(请求确实由自己发起)机构返回内容或发起请求同理,将他们的公钥给我们.代码示例如下

<?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!

结束语

上面是对接机构常用到的加密方案,当然这只是简单距离实际应用当中还有许多的变通之处.常见的如ip限制进一步确保数据安全.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容