Chapter 18.自制可逆等位字符串加密解密(編碼解碼)

歡迎來到「我是真的狗雜談世界」,關(guān)注不迷路

背景

最近做的項目多次遇到了分享邀請的需求點,即需要在接受邀請時能識別到邀請者的信息,又需要考慮信息敏感性,沒找到成熟的三方實現(xiàn),于是自己思考實現(xiàn)了兩套。

思路方案

不能直接將邀請信息用于傳遞,需要對信息(一般是字符串,不是字符串也可以轉(zhuǎn)換為字符串)進行加密處理,或者說編碼處理。但同時需要滿足一下要求:

要求

  • 可逆:加密(編碼)后的密文應當能通過解密(解碼)獲得原文,否則就無法獲得邀請者信息了;
  • 長度:加密(編碼)后的密文應該盡可能與原文長度相當,可以略多(如果能更少也好,不過那需要涉及壓縮了,不是今天的重點);
  • 內(nèi)容:加密(編碼)后的密文應當是可預知的字符集合,如果可設(shè)置更好;
  • 算法可公開(意味著存在額外依賴):不單單依靠一個固定算法,即便算法公開仍舊能保證安全性,否則算法一旦被破解也就沒什么了;
  • 復雜度適當:太復雜的一般對計算要求很高,對開發(fā)(本人)成本也高,能滿足目前的項目需求即可(我絕對不會說我懶);

常見算法

常見的一些加密解密、編碼解碼算法:

  • 單向:md系列、sha1;
  • 對稱:des、aes;
  • 非對稱:rsa、dsa;
  • 簡單編碼:base64;

移位法

本質(zhì)

  • 維護全量字符可能的順序,對給定字符串的每個字符按照其位置進行移位轉(zhuǎn)換,得到結(jié)果;
  • 位置到字符移位偏移量通過一套外部輸入的規(guī)則來指定;
  • 上述兩步相當于將字符枚舉、順序、偏移量規(guī)則三套作為算法變量交由用戶控制,在不能完全知道三個信息的情況下,即便知道算法,也能保障密文安全性;

防篡改

  • 雖然用移位法可以保障用戶無法通過密文還原原文,但用戶可以用大量無序密文來攻擊,造成大量垃圾數(shù)據(jù)或暴力猜測到一些信息,因此還需要支持能校驗密文是否合法的功能,也就是防篡改。
  • 通用的思路是額外加一點校驗數(shù)據(jù),由原文構(gòu)造而成加入到密文中,解密(解碼)時校驗一遍作為驗證即可,比如網(wǎng)絡IP和TCP層的累加和就是這種思路。

實現(xiàn)

    public function encrypt(string $value): string
    {
        if (0 == $this->sortCount) {
            throw new EncrypterException('未設(shè)置字符排布陣列');
        }
        if (0 == $this->shiftingCount) {
            throw new EncrypterException('未設(shè)置編碼密鑰');
        }
        $encrypted = '';
        for ($i = 0; $i < strlen($value); $i++) {
            if (!key_exists($value[$i], $this->sortList)) {
                throw new EncrypterException('發(fā)現(xiàn)不期待的字符');
            }
            $index = $this->sortList[$value[$i]];
            $index += $this->shiftingList[$i % $this->shiftingCount];
            $index %= $this->sortCount;
            $encrypted .= $this->sort[$index];
        }

        // 添加校驗位
        if ($this->checkSum) {
            $sum = 0;
            for ($i = 0; $i < strlen($value); $i++) {
                $sum += ord($value[$i]);
            }
            $sum %= $this->sortCount;
            $start = $this->sort[$sum];
            $end = $this->sort[$this->sortCount - $sum - 1];
            $encrypted = $start . $encrypted . $end;
        }

        return $encrypted;
    }
    public function decrypt(string $value): string
    {
        if (0 == $this->sortCount) {
            throw new EncrypterException('未設(shè)置字符排布陣列');
        }
        if (0 == $this->shiftingCount) {
            throw new EncrypterException('未設(shè)置編碼密鑰');
        }

        // 拿出校驗位
        if ($this->checkSum) {
            if (strlen($value) < 2) {
                throw new EncrypterException('解密校驗失敗');
            }
            $start = $value[0];
            $end = $value[strlen($value) - 1];
            $value = substr($value, 1, -1);
        }

        $decrypted = '';
        for ($i = 0; $i < strlen($value); $i++) {
            if (!key_exists($value[$i], $this->sortList)) {
                throw new EncrypterException('發(fā)現(xiàn)不期待的字符');
            }
            $index = $this->sortList[$value[$i]];
            $index -= $this->shiftingList[$i % $this->shiftingCount];
            while ($index < 0) {
                $index += $this->sortCount;
            }
            $decrypted .= $this->sort[$index];
        }

        // 校驗校驗位
        if ($this->checkSum) {
            $sum = 0;
            for ($i = 0; $i < strlen($decrypted); $i++) {
                $sum += ord($decrypted[$i]);
            }
            $sum %= $this->sortCount;
            if ($start != $this->sort[$sum] || $end != $this->sort[$this->sortCount - $sum - 1]) {
                throw new EncrypterException('解密校驗失敗');
            }
        }

        return $decrypted;
    }

特點分析

  • 需要知道原文全部字符構(gòu)成可能;
  • 原文和密文的字符構(gòu)成是同一個集合;
  • 密文與原文長度相等,即使加入校驗位也僅多2位;
  • 適用于前端、用戶可見可感知的傳遞場景;

異或法

本質(zhì)

  • 利用abb=a,也就是兩次異或復位的特性(字符可轉(zhuǎn)換成一個數(shù)值也就是一個多位的二進制,單位異或的特性在多位場景下同樣成立)
  • 同樣與原文、密文每個字符進行異或操作的字符應該與其位置規(guī)則有關(guān),同移位法相關(guān)規(guī)則

可讀性

由于異或后的值可能超過輸入原值,字符轉(zhuǎn)換時可能轉(zhuǎn)換為非常用字符影響閱讀,因此可以選擇嵌套一層base64加解密方便閱讀。

實現(xiàn)

    public function encrypt(string $value): string
    {
        $value = $this->handle($value);
        if ($this->base64) {
            $value = base64_encode($value);
        }
        return $value;
    }
    public function decrypt(string $value): string
    {
        if ($this->base64) {
            $value = base64_decode($value);
        }
        return $this->handle($value);
    }
    protected function handle(string $string): string
    {
        $result = '';
        for ($i = 0; $i < strlen($string); $i++) {
            $t = $this->secret[$i % $this->secretCount];
            $result .= chr(ord($string[$i]) ^ ord($t));
        }
        return $result;
    }

分析

  • 即便不知道原文字符全部可能也可以使用這套算法;
  • 但原文和密文的字符構(gòu)成不是同一個集合;
  • base64后密文一般較原文長,具體見base64編碼算法規(guī)則;
  • 適用于接口間、服務間數(shù)據(jù)傳輸場景;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • [toc] 常見編碼 1.ASCII編碼 ASCII編碼大致可以分作三部分組成: 第一部分是:ASCII非打印控制...
    William92閱讀 4,270評論 0 1
  • 簡介 本文總結(jié)了在爬蟲中常見的各種加密算法、編碼算法的原理、在 JavaScript 中和 Python 中的基本...
    K哥爬蟲閱讀 643評論 0 0
  • 軟件的加密與解密是一個迷人的研究領(lǐng)域,它幾乎可以與任意一種計算機技術(shù)緊密結(jié)合——密碼學、程序設(shè)計語言、操作系統(tǒng)、數(shù)...
    架構(gòu)通天之路閱讀 770評論 0 0
  • 前言:很多人認為MD5是一種加密算法,其實嚴格來說MD5是為計算機安全領(lǐng)域廣泛使用的一種散列函數(shù),用以提供消息的完...
    Orz013閱讀 1,984評論 0 0
  • IOS應用安全-加解密算法簡述 導讀客戶端經(jīng)常遇到需要對數(shù)據(jù)進行加密的情況,那應該如何加密,選用什么樣的加密算法,...
    蘿卜_7fad閱讀 983評論 0 0

友情鏈接更多精彩內(nèi)容