當前位置:
首頁 > 知識 > 循序漸進學加密

循序漸進學加密

還記得上初二的那年夏天,班裡來了一個新同學,他就住在我家對面的樓里,於是我們一起上學放學,很快便成了最要好的朋友。我們決定發明一套神秘的溝通方式,任何人看到都不可能猜到它的真實含義。我們第一個想到的就是漢語拼音,但很顯然光把一個句子變成漢語拼音是不夠的,於是我們把26個英文字母用簡譜的方式從低音到高音排起來,就得到了一個簡單的密碼本:

把「 」用這個密碼本變換之後就得到了這樣的結果:

小時候玩這個遊戲樂此不疲,覺得非常有趣。上大學後,有幸聽盧開澄教授講《計算機密碼學》,才知道原來我們小時候玩的這個遊戲遠遠不能稱之為加密。那麼到底什麼是加密呢?

什麼是加密?

把字元串 經過 變換之後,得到了 ,有人說這是 加密。

把字元串 經過 變換之後,得到了 ,有人說這是 加密。

從嚴格意義上來說,不管是 還是 甚至更複雜一些的 都不能稱之為加密。

一句話,沒有密鑰的演算法都不能叫加密。

編碼(Encoding)是把字符集中的字元編碼為指定集合中某一對象(例如:比特模式、自然數序列、8位位元組或者電脈衝),以便文本在計算機中存儲和通過通信網路的傳遞的方法,常見的例子包括將拉丁字母表編碼成摩爾斯電碼和 。 只是一種編碼方式。

雜湊(Hashing)是電腦科學中一種對資料的處理方法,通過某種特定的函數/演算法(稱為雜湊函數/演算法)將要檢索的項與用來檢索的索引(稱為雜湊,或者雜湊值)關聯起來,生成一種便於搜索的資料結構(稱為雜湊表)。雜湊演算法常被用來保護存在資料庫中的密碼字元串,由於雜湊演算法所計算出來的雜湊值具有不可逆(無法逆向演算回原本的數值)的性質,因此可有效的保護密碼。常用的雜湊演算法包括 , , 等。

加密(Encryption)是將明文信息改變為難以讀取的密文內容,使之不可讀的過程。只有擁有解密方法的對象,經由解密過程,才能將密文還原為正常可讀的內容。加密分為對稱加密和非對稱加密,對稱加密的常用演算法包括 , 等,非對稱加密演算法包括 ,橢圓曲線演算法等。

在古典加密演算法當中,加密演算法和密鑰都是不能公開的,一旦泄露就有被破解的風險,我們可以用詞頻推算等方法獲知明文。 年美國 公司研製的 演算法( )是人類歷史上第一個公開加密演算法但不公開密鑰的加密方法,後來成為美國軍方和政府機構的標準加密演算法。 年升級成為 演算法( ),我們今天就從 開始入手學習加密和解密。

準備工具

通常情況下,加解密都只需要在服務端完成就夠了,這也是網上大多數教程和樣例代碼的情況,但在某種特殊情況下,你需要用一種語言加密而用另一種語言解密的時候,最好有一個中立的公正的第三方結果集來驗證你的加密結果,否則一旦出錯,你都不知道是加密演算法出錯了,還是解密演算法出錯了,對此我們是有慘痛教訓的,特別是如果一個公司里,寫加密的是前端,用的是 語言,而寫解密的是後端,用的是 語言或者 語言或者 語言,則雙方更需要有這樣一個客觀公正的平台,否則你們之間必然會陷入永無休止的互相指責的境地,前端說自己沒有錯,是後端解密解錯了,後端說解密沒有錯,是前端加密寫錯了,而事實上是雙方都是菜鳥,對密碼學一知半解,在這種情況下浪費的時間就更多。

在線AES加密解密就是這樣的一個工具網站,你可以在上面驗證你的加密結果,如果你加密得到的結果和它的結果完全一致,就說明你的加密演算法沒有問題,否則你就去調整,直到和它的結果完全一致為止。反之亦然,如果它能從一個密文解密解出來,而你的代碼解不出來,那麼一定是你的演算法有問題,而不可能是數據的問題。

我們先在這個網站上對一個簡單的字元串 進行加密。

下面我們對網站上的所有選項逐個解釋一下:

加密模式:這裡我們選擇的是 ( )模式。這是 所有模式中最簡單也是最不被人推薦的一種模式,因為它的固定的明文對應的是固定的密文,很容易被破解。但是既然是練習的話,就讓我們先從最簡單的開始。

填充:在這裡我們選擇 標準的 。

數據塊:我們選擇 位,因為 端解密演算法目前只支持 ,所以我們先從 位開始。

密鑰:因為我們前面選擇了 位的數據塊,所以這裡我們用 個位元組來處理,我們先簡單地填入 個,其實你也可以填寫任意字元,比如 或者其它,只要是 個位元組即可。理論上來說,不是個位元組也可以用來當密鑰,優秀的演算法會自動補齊,但是為了簡單起見,我們先填入 個 。

偏移量:置空。因為是 模式,不需要 偏移量。

輸出:我們選擇 編碼方式。

字符集:這裡因為我們只加密英文字母和阿拉伯數字,所以選擇 和 都是一樣的。

好了,現在我們知道按照以上選項設置好之後的代碼如果加密 的話,應該輸出 ,如果不是這個結果,那就是加密端的問題。

AES-ECB

AES-ECB的Javascript加密

為了完成 加密,我們並不需要自己手寫一個 演算法,不需要去重複造輪子。但如何選擇 的加密庫是個很有意思的挑戰。我們嘗試了很多方法,一開始我們嘗試了aes-js這個庫,但它不支持 演算法,後來我們看到Web Crypto API這種瀏覽器自帶的加密庫,原生支持 和 ,但它的 實現和 不兼容,最終我們還是選擇了Forge這個庫,它天生支持 的各種子集,並且它的 也能和 完美配合。

使用 編寫的 代碼實現 加密的代碼就是下面這些:

的 預設就是 ,所以不用特別設置。運行它之後你就會得到正確的加密結果。

AES-ECB的Java解密

接下來我們看看Java端的解密代碼該如何寫:

注意這裡我們用到的是 ,上面加密的時候不是用的是 嗎?怎麼這裡變成 了呢?

我們先來了解一下什麼是 。 的全稱是 (公鑰加密標準),這是 實驗室制定的一系列的公鑰密碼編譯標準,比較著名的有 , , , 這四個,它們分別管理的是不同的內容。在這裡我們只是用它來填充,所以我們只關注 和 就夠了。那麼 和 有什麼區別呢?其實在填充方面它們兩個的演算法是一樣的, 是 的一個子集,區別在於 是 位元組固定的,而 可以是 到 之間的任意位元組。但用在 演算法上,因為 標準規定塊大小必須是 位元組或者 位元組或者 位元組,不可能用 的 位元組,所以 演算法只能用 填充。但是由於 早期工程師犯的一個命名上的錯誤,他們把 填充演算法的名稱設定為 ,而實際實現中實現的是 ,所以我們在 端開發解密的時候需要使用 。

AES-CBC

談完了不安全的 ,我們來做一下相對安全一些的 模式。

AES-CBC的Javascript加密

直接上代碼:

跟上面的 差不多,唯一區別只是在 函數里定義了一個 。

AES-CBC的Java解密

下面是 代碼:

也是同樣,跟上面用 時的模式幾乎一模一樣,只是增加了一個 ,用來生成 ,在 裡面增加了一個 參數,除此之外完全相同,就這樣我們就已經實現了一個簡單的 模式。

RSA

但是以上兩種做法都明顯是非常不安全的,因為我們把加密用的密鑰和 參數都直接暴露在了前端,為此我們需要一種更加安全的加密方法—— 。因為 是非對稱加密,即使我們把加密用的公鑰完全暴露在前端也不必擔心,別人即使截獲了我們的密文,但因為他們沒有解密密鑰,是無法解出我們的明文的。

生成密鑰對

要用 加密,首先我們需要生成一個公鑰和一個私鑰,我們可以直接執行命令 。它會問我們密鑰文件保存的文件夾,注意一定要單獨找一個文件夾存放,不要放在預設文件夾下,否則你日常使用的 公鑰和私鑰就都被覆蓋了。

得到公鑰文件之後,由於這個公鑰文件是 格式的,而我們的 庫要求一個 格式的公鑰,所以這裡我們需要把它轉換成 格式(也就是 格式):

RSA的Javascript加密

得到 格式的公鑰之後,我們來看一下 的代碼:

一句話就完成整個加密過程了,這就是 的強大之處。

RSA的Java解密

接下來我們看解密。

對於私鑰,因為 只支持 ,而我們用 生成的私鑰是 的,所以還需要用以下命令把 的私鑰轉換為 的私鑰:

得到 格式的私鑰之後,我們把這個文件的頭和尾去掉,然後放入以下 代碼:

和上面的 解密類似,只是增加了 讀取 格式私鑰的部分,這樣我們就完成了 端的 解密。

以上我們用最簡單的方式實現了 端加密, 端解密的過程,感興趣的朋友可以在這裡下載完整的代碼親自驗證一下:

https://github.com/fengerzh/encdec

作者:張京

https://segmentfault.com/a/1190000019437132

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 JavaScript 的精彩文章:

近半月來前端到底發生了哪些變化?
重構項目,你真的準備好了嗎

TAG:JavaScript |