今天來討論概念上最好理解的編碼與加密。嚴格說起來編碼跟加密目的相差很多,但行為或過程有點類似,所以放在一起講。
編碼(encode)是把資訊轉換另一種格式以便處理或傳輸,解碼(decode)則是編碼的相反過程。
@startuml
Alice -> Encoder: Plaintext
Encoder -> Decoder: Encoded text via internet
Decoder -> Bob: Plaintext
@enduml
在 web 身分驗證上,最常使用的編碼正是 RFC 3986 section 2.1 提到的 Percent-encoding,又名 URL encoding。除了是 URI 的編碼方法,同時這也是 HTTP 提交表單時,預設的 MIME application/x-www-form-urlencoded
的編碼方法。
另一個也是大家耳熟能詳的 RFC 4648 - The Base16, Base32, and Base64 Data Encodings 裡面提到的 Base64 encoding。
兩個編碼方法都有機會讓字元變多,比方說:
Plaintext: HTTP/1.1
URL encode: HTTP%2F1.1
Base64 encode: SFRUUC8xLjE=
好端端的為什麼要把看得懂的字編成看不懂的亂碼呢?主要理由是為了符合其他規範,比方說上例的資料是 HTTP/1.1
,而 /
是 RFC 3986 定義有特殊意義的保留字元,這時就需要 URL encode 來幫助把想傳的內容轉換成可以接受的字元,才能符合 URI 規範放到網路上傳輸。Base64 encode 則是能把二進位內容如簽章,轉成文字以利於傳輸。
在簡介密碼學時,有簡單提過加密概念,而加密有分兩種類型:「對稱式加密」與「非對稱式加密」。對稱式加密指的是使用相同的金鑰(key)做加密與解密,非對稱式加密指的是有一組成對的金鑰,一個用來做加密,另一個用來做解密。兩者比較表如下:
類型 | 金鑰管理 | 效率 | 常見的演算法 |
---|---|---|---|
對稱式加密 | 困難 | 快 | DES、AES |
非對稱式加密 | 方便 | 慢 | RSA |
以下針對 AES 與 RSA 做個簡單說明。
AES 全名為 Advanced Encryption Standard,它是一個區塊加密的演算法,所謂的「區塊加密」就是把明文拆成多個區段,然後分別加密再組合起來。AES 有分成幾種模式,像是 ECB
、CBC
、CFB
等。其中 CBC 是 Laravel Encryption 所使用的模式,以下以這個模式來做說明。
CBC 一樣也是縮寫,全名為 Cipher Block Chaining。它的加密運作原理簡單來說,是每個明文的區塊跟前一組密文做互斥或運算。那第一個明文該怎麼辦?因此它需要一個初始化向量(Initialization Vector,簡稱 IV)。可想而知,不同的 IV 加密後的字串是不一樣的,而同時這也是解密的必要參數之一。
因為加密與解密同時需要 IV 與 Key,曾看過有人把 IV 跟 Key 做相同的管理,比方說把 IV 設成固定值,然後跟 Key 藏在同個地方。但在實務上,IV 應該要隨機產生,並且它是可以公開的。Laravel Encryption 使用加密時,正是會將 IV 公開。如加密後的密文如下:
eyJpdiI6ImRxdDc1WFV4UERXY0k4M0gxc2VkK2c9PSIsInZhbHVlIjoiNkNyWnRiU0lJMUhDeTBuNXRVdXdQWGJ4REpQZHF0NmtxbWRVTlZHMGJRaz0iLCJtYWMiOiI3OWM1ZTBiYjZlMGVmNDQ3Zjg0M2M5MjBiOWJjMmEzMGUyOWUyOGIxMDgyZDI3NjU4NDcyNWQ0ZDA0MWFjMjljIn0=
可以推測這是 Base64,解碼後會變成 JSON:
{
"iv":"dqt75XUxPDWcI83H1sed+g==",
"value":"6CrZtbSII1HCy0n5tUuwPXbxDJPdqt6kqmdUNVG0bQk=",
"mac":"79c5e0bb6e0ef447f843c920b9bc2a30e29e28b1082d276584725d4d041ac29c"
}
其中 iv
即為剛提到的初始化相量,value
即為 AES CBC 加密後的內容,MAC 則是簡介密碼學時提到做為驗證來源的一小段訊息。
RSA 加密演算法由三個人一起開發的,這個縮寫是取自三位作者的名字。RSA 概念很簡單:兩個很大的質數 p 與 q 相乘出 N 很容易,但從 N 要找回原本 p 與 q 是極度困難的任務。
RSA 需要三個參數:e1、e2 和剛剛的 N。e1 與 e2 是互有關係的,會先取 e1 與 (p-1)(q-1) 互質,接著再找出 (e2 * e1) mod ((p-1)(q-1)) = 1。最後 (N, e1) 為公鑰,(N, e2) 為私鑰。
一般是使用公鑰加密,私鑰解密,而運算方法如下:
plaintext = cipher ^e2 mod n
cipher = plaintext ^e1 mod n
這兩個也可以交換使用:
plaintext = cipher ^e1 mod n
cipher = plaintext ^e2 mod n
RSA 的安全性建立在:目前沒有多項式時間的演算法,可以分解出大整數的因數。因此只要質數越大,時間就會變得非常不合理的高,同時也會認為攻擊者對此束手無策。雖然是如此,但在 2009 年時,768 bit 長度的大數已成功被分解,而目前建議的長度都是 1024 bit 上,其實也是岌岌可危,只能再把長度提升至 2048 bit。
在簡介密碼學時,有提到密碼學的基礎是數學,最近筆者在研究這些演算法著實的吃了很多苦頭,但也了解到了不少東西。寫出來的東西有點差強人意,請大家多多包涵與指教。