JackHttp -- 淺談編碼、加密(對稱加密,非對稱加密,Hash算法)

如果你還不清楚 JackHttp 是什麼,請戳這裏!!!


JackHttp 是一個網絡框架系列,爲什麼還要分享編碼和加密呢?主要有如下幾個原因:

  1. HTTP 在網絡傳輸過程中是明文的。
  2. HTTP 在網絡傳輸過程中內容有可能被中間商篡改的。
  3. 瀏覽器,服務器都要求 HTTP 2.0 基於 HTTPS,而 HTTPS 使用到對稱加密,非對稱加密,Hash 算法。
  4. 編碼和加密在我們 URL 文字編碼、HTTPS 連接,TCP 連接中都經常使用。

帶着這 4 個原因,開始我們今天的內容。

編碼

很遺憾,說起來你可能不信,編碼的定義從計算機發展至今,還沒有一個完整的官方定義,(如果你有找到,請一定要告知我,感謝!)
但藉助一些相對權威的機構定義及自己對編碼的理解,淺談一些編碼相關的知識。

什麼是編碼?

編碼是任意一種數據從一種形式或格式轉換爲另一種形式或格式,並且支持轉換回來的過程。

Base64

好像很熟悉,但具體讓我們說說 Base64 是啥?可能又說不上來,那麼我問幾個問題,大家思考下。

  1. Base64 是編碼嗎?
  2. Base64 是加密嗎?(平時應該聽過 Base64 加密這種說法吧)
  3. Base64 轉換後的數據會變的更加安全嗎?
  4. 使用 Base64 能提高效率嗎?
  5. 爲什麼使用 Base64 ,Base64 的使用場景有哪些?

希望讀者可以簡單思考後,再閱讀下文。

什麼是 Base64?

將二進制數據轉換成由 64 個字符組成的字符串編碼算法。

具體由以下 64 個字符組成。
在這裏插入圖片描述
包含 “大寫的 A -> Z 26 個字母 , 小寫的 a -> z 26 個字母, 阿拉伯數字 0 -> 9 10個數字,運算符 + /” 共 64 個可見字符 。

瞭解到這裏你可能會比較奇怪,爲什麼一定要用 64 個字符呢?
Base128 ,Base256 , Base32 不行嗎?

ok,那麼我們來弄明白 Base64 的編碼原理,以上問題都將得到答案。

Base64 編碼原理

例如我們需要對字符串 “Jack” 進行 Base64 編碼,步驟如下:

  1. 將 "Jack " 拆分成 "J "、"a "、"c "、"k " 4 個字符,並獲取到每個字符的 ASCII 碼值。
    通過 ASCII 碼對照表 我們可以獲取到 "J "、"a "、"c "、“k " 的 ASCII 碼值對應爲"74”、"97 "、“99”、"107 "。

  2. Base64 是對二進制數據進行轉換,所以我們需要將 ASCII 碼值轉換成對應的二進制數據,“01001010”、"01100001 "、“01100011”、“01101011”。

  3. 對二進制數據進行讀取,爲了確保轉換後的任意數據都能用 64 個字符表表示出來,我們對二進制數據進行排列組合,如下圖:
    在這裏插入圖片描述
    組合後會發現,64 個字母的字母表,最多可對應 26,也就是 Base64 一次轉換讀取最多隻能讀取 6 Bit,否則就有可能造成部分轉換後的字符無法正常顯示,出現亂碼,轉換後數據如下圖:
    在這裏插入圖片描述

    解釋: 一個英文字母佔一個字節,一個字節佔 8 Bit。

  4. 將轉換後獲得的二進制數據轉換成 Base64 字符表中的“十進制索引”,然後對應 Base64 字符表索引取出索引值,以此獲取最終轉換後的數據。在這裏插入圖片描述

    注意:最後 1 位 Bit 數據讀取如果如果長度< 6 ,則補 0 ,例子中最後的 “11”,轉成十進制前的數據補碼後的值爲"110000 "。

以上就是Base64 最核心的轉碼原理,OK,說的好像是那麼回事,驗證下,看下結果是不是對的。

    public static void main(String[] args) {
        String encoderStr = "Jack";
        BASE64Encoder encoder = new BASE64Encoder();
        System.out.println("Jack Base64 轉換後的結果:" + 
        encoder.encode(encoderStr.getBytes()));
    }

輸出的結果

Jack Base64 轉換後的結果:SmFjaw==

結果確實跟我們推導出來的一樣,但是輸出的結果後面還跟着2個 " = = " 是什麼意思呢?
其實他僅僅是一個位數補齊標記,是爲了方便後面做解碼的時候使用的,我們觀察上面的轉碼過程發現,編碼前每 3 個字節的 3* 8 = 24Bit 解碼後剛好 24 / 6 = 4 個字節。

Base64 轉碼前每 3 個字節轉碼後會變成 4 個字節,而 4個字節是 Base64 編碼的最小單元,也就是說我們任何一個數據進行 Base64 編碼後都是 4 的倍數,而我們本例中對應字符表解碼出來的數據是“SmFjaw”只有 6 個字節,怎麼辦? Base64 就想到用 “= =” 進行補齊,解碼時以 4 個字節爲最小單元進行解碼,明白了吧?

Base64 解碼原理

完全的逆向,通過字符表獲取 “SmFjaw==” 索引再轉換成 6 Bit 的二進制數據,然後在逆向轉成 8 Bit 二進制數據,最後通過 ASCII 碼值得到轉換前的 “Jack” 。

Base64 問題解答

弄明白了原理,我們再來回答之前提出的問題。

爲什麼一定要用 64 個字符呢?Base128 ,Base256 , Base32 不行嗎?爲什麼定義 Base64?
  1. 按照 Base64 的原理,如果一次讀取 8 Bit,那麼轉換後的數據,不就不會改變原數據的長度了嗎?爲什麼不用 Base256?
    因爲 ASCII 的基礎碼是用 7 Bit 組成的,最多隻有 128 個字符, 而要讀取 8 Bit 至少需要 256 個可見字符才能完全表示,從文本轉成 ASCII 碼值數量都不夠,這顯然行不通。

    拓展:有人想到過用顏色來代表不同的 Base64 算法,把 Base64 分成不同的 4 組顏色,聽上去確實可行,但其實並沒有什麼意義,對解碼效率不會有任何提升。

  2. 而不使用 Base128 的原因也大同小異,在 ASCII 字符集中雖然有 128 個字符,但並不是所有的字符都是可見的,例如 “NUT”,“SOH” 等,如果用 128 個字符在轉換後也就會出現我們所看到的亂碼

  3. 那麼爲什麼不用 Base32 呢?
    效率嘛,能讀取 6 Bit ,爲什麼還要去讀取 5 Bit 甚至更少呢,這不浪費效率和帶寬嘛。

這也是爲什麼會定義出 Base64 的核心原因。

Base64 是編碼嗎?

很明顯是的,他可以使數據從一種格式變爲另一種格式且可以解碼回原始數據。

Base64 是加密嗎?(平時應該聽過 Base64 加密這種說法吧)

講完加密我在回答你,請關注文末。

Base64 轉換後的數據會變的更加安全嗎?

搞懂了原理,應該清楚了吧? 整個編碼和解碼的流程都是公開可見且很容易推敲,怎麼會更安全呢?安全個錘子。

使用 Base64 能提高效率嗎?

Base64 編碼每個最小單元都會把 3 個字節轉換成 4 個字節,文本大小增加 33%,更高效?假設我們現在有一個 2.4M 的圖片,服務器對其 Base64 編碼後再傳輸,會編碼成一個 3.2M 的文本。
Are you kiding me? 大小增加了 800K,你跟我說更高效了?這得多浪費帶寬,因此你們公司的後臺開發人員不到萬不得已的情況下,還在使用 Base64 對圖片進行編碼傳輸,你可以開始懟他了,開個玩笑。

那麼 Base64 的使用場景有哪些呢?

再電子郵件剛開始發展的時候,只能傳輸英文,但隨着時代的發展,電子郵件需要支持更多國家的語言以及傳輸圖片,甚至是視頻文件,而當時的服務器、網關並不支持傳輸圖片和視頻,怎麼辦?Base64 就誕生了。
因此 Base64 他的應用場景主要有以下幾點:

  1. 我們所開發的系統並不支持大文件傳輸,例如:圖片、視頻。系統在沒有更好選擇的情況下,需要對其進行 Base64 編碼轉換成文本,再進行網絡傳輸。
  2. 對小的文件或文本數據進行 Base64 編碼,以達到不同端的兼容性。小文本的編碼性能損失可以降到最低。

ok,之所以這麼詳細的介紹 Base64 編碼,是因爲大部分常見的編碼原理基本和 Base64 一致,只是不同的編碼其的核心算法會有些改變,你搞懂了 Base64 的原理,弄清楚什麼是編碼,在反過推敲其它的編碼原理,將可以舉一反三,so easy!

最後我在羅列一下我們常見的編碼還有哪些:

編碼 說明 算法
URL Encoding 將 URL 中保留的字符使用“%”進行編碼 核心算法跟 Base64 一致
壓縮和解壓縮 把數據換一種形式來存儲,以達到減小存儲內存的目的 DEFLATE(gzip、zip)、JEPG、MP3、MP4
媒體數據的編碼 把二進制圖片數據轉換成 JPG、PNG的格式 JPG、PNG
字符集編碼 計算機要準確的處理各種字符集文字,就需要進行字符編碼,以便計算機能夠識別和存儲各種文字 ASCII、ISO-8859-1、Unicode、UTF-8、UTF-16、GBK、GB2312、GB18030

什麼是加密?

加密是以某種特殊的算法改變原有的數據,使我們無法理解其明文的含義,並通過祕鑰可以還原到原始數據的過程。

加密的誕生

加密算法的前十今生,如要要開始追溯,得從古代戰爭–古典密碼學說起。
在古代戰爭中,情報作爲戰事中最大的要素,決定着一場戰爭的勝負。
在大型戰爭中,由於部隊較多,指揮無法直接對每支部隊下達命令,所以常常需要信使來傳遞重要的軍事情報。
可是,你要知道,依靠信使來傳遞軍情並不安全:一旦信使被敵軍抓獲,重要的軍事情報就完全被敵方知悉了。
屆時,借用三國曹操的一句話“爾等無需反抗,下馬受死”。

在這樣的背景下,就誕生了人類歷史上第一種加密算法 – 凱撒密碼,他使用的是一種
位移式加密,就像這樣:
在這裏插入圖片描述
把每個字母向後移動 5 位,然後在交給信使。軍隊拿到後在移回來 5 位,得到真實的數據。
還是拿三國舉例:

現在曹操發送軍令攻打 “Jin Gong Jingzhou” 轉碼後的數據給到信使就變成了 “OTS LTSL ONSLEMTZ” 。即使信使被抓了,也看不懂吧?在這個故事中,解密的祕鑰就是“將字母位移 5 位”。

但隨着時間的發展,敵人慢慢的也會破解這段不懂的祕鑰,因此再今後的發展過程中,加密的算法變的越來越複雜,破解加密的算法也同步再發展壯大,也就是道高一尺,魔高一尺。

直到有一天,計算機的出現,徹底打破了加密算法的平衡,在計算機的強大的計算面前,之前定義的算法都將灰飛煙滅。於是,更高級的加密算法應運而生。

現代密碼學的時代開啓,基於當時的使用場景和需求,首先誕生的現代密碼學是對稱加密算法。

對稱加密

對稱加密核心原理:

使用祕鑰和加密算法對數據進行轉換,得到加密後的看不懂的密文數據,使用祕鑰和解密算法對密文進行逆向轉換,得到原始數據。

在這裏插入圖片描述
經典的對稱加密算法有 2 種:
DES(Data Encryption Standard): 數據加密標準,速度較快。
AES(Advanced Encryption Standard): 高級加密標準,是當今的加密算法標準,速度快,安全級別高。

他們的底層原理基本是一樣的,AES 的誕生主要是因爲 DES 的祕鑰太短,很容易被暴利破解。(暴利破解指的是用枚舉法一個一個祕鑰的嘗試)
因此 DES 發展至今基本被棄用了,大部分的對稱加密,目前都使用 AES 。
但隨着時間和計算機的發展,AES 也會有被棄用的那一天,取而代之的將是更加安全的加密標準。

AES 算法基礎原理

AES 算法原理是基於排列和置換運算。排列是對數據重新進行安排,置換是將一個數據單元替換爲另一個。同時 AES 還會使用幾種不同的方法來執行排列和置換運算,提高破解難度。
(它可以使用 128、192 和 256 位不同的密鑰長度分組加密和解密數據。)

對稱加密的弊端

  • 雖然對稱加密的算法公開、計算量小、加密速度快、加密效率高。但我們發現由於通信雙方用到的是同一個密鑰,如果其中一方的密鑰遭泄露,那麼整個通信就會被破解。

  • 每個用戶與其他用戶使用對稱加密算法時,都需要使用僅限雙方知道的唯一密鑰,隨着每個用戶通信對象的增加,用戶所擁有的密鑰數量會越來越大,因此密鑰管理成爲用戶的負擔。

  • 對稱加密無法用於簽名與簽名驗證。

在這種弊端下於是衍生出非對稱加密。

非對稱加密

非對稱加密與對稱加密最大的區別是,非對稱加密擁有兩把鑰匙,一把公鑰,一把私鑰。

非對稱加密核心原理:

使用公鑰和加密算法對數據進行轉換,得到加密後的看不懂的密文數據,使用私鑰和相同的加密算法對密文進行逆向轉換,得到原始數據。
在這裏插入圖片描述

非對稱加密的優勢?

  • 我們來想想,現在我們團隊開發了一個平臺,會有很多客戶跟我們服務器進行通信,考慮到數據安全,如果使用對稱加密,我們就需要給每一個用戶發送一個私鑰,通信時,在取出對應的私鑰解密獲取原數據,對吧?這將沒有任何問題,但假設我們的用戶達到了 1 個億,我們是不是需要維護1 億個私鑰?而且用戶的私鑰由於XX原因,泄漏了,導致我們之前通信的數據被修改,造成損失,責任怎麼算?公說公有理婆說婆有理,是不是這樣?但如果我們使用非對稱加密,我們可以把私鑰握在手裏,公鑰發給任何人, 1 億個用戶都可以用我們同一個公鑰對數據進行加密,私鑰只有我們團隊有,因此我們通信只要出了問題,責任就很清楚,是我們團隊泄漏的私鑰,並且我們只需要維護 1 個私鑰,是不是減輕的很多負擔,對吧?只要我們的私鑰不被泄漏,加密算法足夠安全,這個加密流程是很難被破解的。

簽名與簽名驗證

由於非對稱加密優勢,人們想到了另外一個場景,就是簽名和認證,用私鑰和加密算法對數據進行簽名,然後把公鑰公開的大家,去對我的原始數據進行簽名認證。

在這裏插入圖片描述
簽名和認證在我們的開發過程中非常普片,例如:

  1. 在安全層 SSL/TLS 連接過程中(後面文章會講到)。
  2. 我們安裝包在發佈時需要簽名, APK 安裝時會對簽名進行驗證。
  3. 在我們實際開發中會結合(加密+簽名)來加密數據並解密後驗證數據的真實性,是否被篡改。

經典的非對稱加密算法有 3 種:

  • RSA:由美國麻省理工學院三位學者 Rivest、Shamir 及 Adleman 研發的密碼系統,是一個支持變長密鑰的公共密鑰算法,需要加密的文件塊的長度也是可變的。
  • DSA(Digital Signature Algorithm):數字簽名算法,是一種標準的 DSS(數字簽名標準,此算法只用於簽名,比較專一)。
  • ECC(Elliptic Curves Cryptography):橢圓曲線密碼編碼學,常用於比特幣數據加密使用。

非對稱加密算法的弊端

加密算法及其複雜,安全性依賴算法與密鑰,而且加密和解密效率很低。一個詞概括:安全低效。

Hash 算法

定義:

把任意長度的數據,通過散列算法,變換成固定長度的數據(通常情況下很小)。

注意: Hash 算法是不可逆的,所以 Hash 算法既不是編碼,也不屬於加密算法,注意啦!

什麼意思呢,我們在上班的時候,公司會給我們申請一個唯一的工號吧?(我們認爲工號是不會被註銷的)有的公司還會錄指紋打卡對吧?那麼申請工號這個過程就能算作是一個 Hash 算法,他把我們整個人的體貌、行爲特徵抽象到這個工號上了,這個工號所做的所有操作,都能證明是你的做的。

經典的算法也是 2 種

  • MD5(Message Digest Algorithm 5):是 RSA 算法同一個團隊研發的,一種單向散列算法。
  • SHA(Secure Hash Algorithm):可以對任意長度的數據運算生成一個 160 位的數值。

Hash算法有什麼作用呢?

1. 數據完整性驗證
舉例:我們現在寫了一部很經典的小說,大小 2G 左右,在網站上公開提供用戶下載,過了一段時間發現用戶經常下載到盜版並且有時候下載出來的小說文本可能會丟包,少一些章節,怎麼辦?

驗證數據完整性嘛?剛你不是講了簽名嗎?ok,我們使用簽名應該怎麼做?

  1. 在某平臺提供小說源文件下載地址。
  2. 對小說源文件通過私鑰進行簽名,然後把簽名文件、公鑰、加密算法放在下載地址下方,提供用戶進行源文件驗證,如果和原數據一致,說明是正版的。
  3. 用戶下載小說源文件。
  4. 用戶下載簽名文件,並獲取公鑰、加密算法對簽名文件進行解密,得到源文件看是否和源文件一致。

看似好像沒有什麼問題(如果你發現了那更好了),但是,,,凡事都還有個但是,這個源文件可是有 2G 左右,這會導致它的簽名的文件也會有 2G 左右,甚至更大。What? 你不是在逗我吧,讓我下載一個 2G的文件驗證源文件?另外一個問題,你讓我用戶怎麼確認源文件小說是不是盜版,一個章節一個章節的核對嗎? 這時候 Hash 算法的價值就體驗出來了,我們如何優化剛纔的步驟呢?

  1. 在某平臺提供小說源文件下載地址。
  2. 對小說源文件進行 MD5/SHA1 算法得到一個固定的摘要值(這個值非常小,大概只有20B 左右)然後在通過私鑰對摘要值進行簽名,此時的簽名文件就非常小了,再把簽名文件、公鑰、加密算法放在下載地址旁提供給用戶下載,最後在寫一個 README 告知源文件使用的是什麼 Hash 算法(MD5/SHA1)。
  3. 用戶下載小說源文件。
  4. 用戶下載 MD5/SHA1 後的簽名文件,獲取公開的公鑰、加密算法對簽名文件進行解密,得到之前簽名的那個摘要值,根據 README 中告知 Hash 算法,對源數據進行此 Hash 算法也得到一個 摘要值,如果這個值跟剛纔解密得到的值是一樣的,說明源文件是正版並且不可能被串改,因爲源文件只要有任何一點點改動,得到的 Hash 摘要值都會有很明顯的變化,稱之爲雪崩效應。

注意:這個例子的過程非常重要,如果你沒看懂,希望你可以多看幾遍,這對你理解對稱加密、非對稱加密、 Hash 算法非常有幫助,這個邏輯是也是 SSL/TLS 證書認證的核心。

2. 快速查找,提高效率(hashCode(),HashMap)

這也是 Hash 算法非常重要的一個作用了,搞明白這個作用前,我們可以來了解下 HashCode()。

爲什麼每次我們重寫 equals() 時,一定要重寫 hashCode() 呢?

首先我們要有一個概念,hashCode() 也是 Hash 算法的一種,他獲取的是我們當前這個對象的摘要值(也可以理解成是特徵值),他是用來對身份判定的,並且很輕量,以此達到快速識別的目的

快速識別的核心邏輯總結是下面這2句話:

hashCode() 相同的對象,其 equals() 可能相同,hashCode() 不同的對象,equals() 肯定不同。

基於這個核心邏輯,當我們在做查找的時候,可以先用輕量的摘要值對數據進行一次過濾,因爲 equals() 是非常耗時的(會拿這個對象的屬性值,方法等逐一比較。),這樣就不用每次都去比較 equals() 是不是相等的,以此達到提高效率的作用。

而如果我們不重寫 hashCode() 的話,會發生什麼呢? 數據碰撞
當我們不重寫 hashCode() 時,會默認調用 Object 的 hashCode() 方法,如果此時我們在通過 HashMap/HashSet 等 put 數據時,就會出現這樣一個現象,本來這個對象的 hashCode() 是不相等的,結果由於我們沒有重寫, 導致發生數據碰撞,結果後進入的數據會替換掉重複的那麼 hashCode() 對應的 value 值,查找的時候也是這個原理

3. 隱私保密

比較有代表性的就是對密碼、登錄授權信息進行 MD5/SHA1 加密,加密前,大部分服務器開發人員還會對你的實際數據做一些本地的加密算法後,再進行 MD5/SHA1 加密存儲。

如果我們直接在數據庫明文存我們的密碼等敏感信息,數據庫一旦被盜,那將是毀滅性的。而我們對密碼等敏感數據做 MD5/SHA1 後,存儲到數據庫的數據是不可逆的,壞人就算拿到你的數據庫也不會對用戶隱私造成損失。如果本地加密後,再進行 MD5/SHA1 那就更安全了,你就算暴利破解(這裏的暴利破解指的是枚舉所有的可能去做 MD5/SHA1 算法,看得到的數據是否和數據庫一致,以此來破解你的密碼),但是如果服務器做了一次本地的加密後,你就算破解了 MD5/SHA1 這層,得到的也是服務器本地加密後的那個數據,再不知道本地服務器加密算法的情況下,壞人拿到這個數據也毫無意義。

Base64 是不是加密

終於說完了,讀完本文,我們清楚了什麼是編碼,什麼是加密。回答本文最後一個問題 “Base64 是不是加密?” 前,我們對比下編碼、加密異同點

相同點:

  • 編碼的過程都是一個可逆的過程,既從 A -> B,並且可以從 B -> A

不同點:

  • 編碼過程不需要祕鑰,而加密過程必須需要祕鑰解密。
  • 編碼過程注重轉換,而加密過程注重安全和性能。

因此 Base64 不屬於加密,因爲加密是需要祕鑰的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章