2018年10月4日

[筆記] MySQL Config File Encryption and Decryption

上一篇提到 MySQL 在 5.6 版後提供 mysql_config_editor 這支 utility 讓使用者可以產生/修改加密後的 config file (或者叫做 option file),但是很奇怪的是官方提供的 API 不管是 C 或 Python 都不吃加密後的 config file。後來經過多方查詢研究後發現 python 有一個 package 叫 myloginpath 可以把加密後的 config file 解成明文,基於 python 沒秘密,所以這邊就來整理一下這弱到爆的加密方式

首先他的加密方法是採用 ECB mode 的 AES 128 bit 來加密,加密的方式簡單來說如下,假設 config file 的明文格式是這樣:

plain text line 1
plain text line 2
...
plain text line n

基本上就是每一行各自拿出來做加密。然而因為 AES 是 block ciper,基本上會要求每個 block 就是要 16 bytes。所以如果每一行的字元數如果不是 16 的倍數時會在最後面補上 pading byte,只是最後一個 pading byte 是有意義的:他記錄了這次送去 AES 加密的明文有多少 pading bytes,後面我們解密完後就要先讀最後這個 byte 來得知要拿掉多少 pading byte。加密後的格式如下:

[4 bytes - pading bytes][20 bytes - processed key][4 bytes - cipher text 1's length][cipher text 1][4 bytes - cipher text 2's length][cipher text 2]...[4 bytes - cipher text n's length][cipher text n]

首先檔案開始的前 4 bytes 無意義直接拿掉,再來的 20 byte 就是經過處理的 AES key,所以我們必須要先把這 20 bytes 還原成原本 AES 加密用的 128bit key。還原方法還滿簡單的,假設那 20 bytes 的資料如下:

b0 b1 b2 b3 ... b16 b17 b18 b19

那麼原始的 key 就是

b0⊕b16 b1⊕b17 b2⊕b18 b3⊕b19 b4 ... b14 b15

拿到 key 之後,剩下的就是
  1. 每一行明文加密後的長度 (4 bytes)
  2. 每一行明文加密後的密文 (長度看上面那個讀出來數值是多少)
如此一直重複,所以要還原的話,方法就是
  1. 先讀 4 bytes,看密文長度多少 (基於 AES 的特性,這個數值會是 16 的倍數)
  2. 假設密文長度是 16 bytes,那就讀出 16 bytes 後交由 AES 解密
  3. 基於加密前時會在後面補上 pading bytes,所以解密後要把 pading bytes 拿掉才是原本的明文
    1. 從 AES 解密後的最後一個 byte 讀出這一組明文有多少 pading bytes
    2. 假設 pading bytes 的數值是 7,那就表示明文長度是 9 bytes,所以把最後的 7 bytes 直接拿掉即可
就這樣一直反覆解密下去直到資料都處理完畢即可。以一開始明文有 n 行來說,這個步驟就會重複 n 次。

然後我還是要抱怨一下 MySQL 官方原生的 API 不好好做讓使用者非得搞這種 workaround 是怎樣。

沒有留言:

張貼留言