那麼,它和 Hash 類別的加密有什麼不同嗎? Hash 類別的加密是單向的不可逆轉的加密,加密後的內容是16進制的Hash 串,我們只能透過彩虹表去反推明文內容,所以只要加上鹽值或多套兩層加密,就非常難逆向破解出來了。因此,Hash 加密通常會用於用戶的密碼保存上,即使資料庫洩露了用戶密碼仍然是安全的。而 OpenSSL 這種類型的對稱/非對稱加密則是可以透過某個關鍵字或憑證來進行正向加密和逆向解密的,原文
都是可以得到的。下面我們就來具體說說對稱和非對稱加密的問題。
什麼是對稱和非對稱加密
對稱加密,通常是透過一個 key(金鑰) 來對原文進行加密。也就是說,不管是服務端還是客戶端或是其它的任何對端,在兩端通訊時,它們傳輸的加密內容都必須要使用相同的 key 來進行加/解密操作。兩端都必須同時儲存這樣一個 key 。估計大家也想到了,現在不管是 web 開發還是 app 開發,程式碼都是可以反編譯查看到原始碼的。如果使用對稱加密的話,key 是很容易被取得的。不過,對稱加密的好處是速度非常快,而且不會消耗資源。
非對稱加密則是兩端持有不同的 key 。就像我們平常見到的最多的 https 證書,就是分別有 公鑰 和 私鑰 這兩個概念。一般我們會使用 公鑰 進行加密,然後使用 私鑰 進行解密,通常 公鑰 都是公開並發送給對方的,而私鑰是保存在自己這裡的。也就是說,對方向我們發送資料的時候,使用我們給它的公鑰將資料加密,資料在傳輸過程中就非常安全,因為中間沒有別人有可以解密這段資料的私鑰,直到我們接收到資料後使用自己的私鑰進行解密後就得到了原文資料。由於兩邊的金鑰內容並不相同,所以相對於對稱加密來說,非對稱加密的安全性就高了許多。雖然說非對稱加密的演算法和複雜度都比對稱加密提升了好幾個檔次,但相對於對稱加密的優勢,在非對稱加密中,速度和效能也就成了它的瓶頸,特別是資料量大的情況下。另外,非對稱加密的數學原理是大數難分解問題,也就是越大的數字越難進行因子分解,如果某個演算法能在短時間內破解這個問題的話,那麼恭喜你,現代加密演算法的基礎天花板就被你捅破了。
對稱加密常用的演算法有:AES 、DES 、3DES 、 IDEA 、 RC2 、 RC5 等,比較常用的是 AES 和 DES 。
非對稱加密常用的演算法有:RSA 、Elgamal 、ECC 等,RSA 非常常用且普遍,SSL 和一些憑證演算法都是基於 RSA 。
為了系統安全我們該怎麼辦?
那麼,我們有沒有折衷的方式來使用這兩種加密能力呢?當然有了,也是非常經典的技術:數位信封。
其實意思非常簡單,就是利用這兩種加密方式各自的優點。非對稱加密的安全性高,但速度慢,而且資料量越大速度越慢,那麼我們就用它來加密對稱加密的 key ,通常這個 key 不會很大。然後實際的資料實體使用這個對稱加密的 key 來進行對稱加密提升速度。這樣,當我們傳送給客戶端時,就包含兩個內容,一個是非對稱加密進行加密的 key ,一個使用對稱加密進行加密的資料內容。用戶端拿到資訊後,先使用非對稱加密的金鑰解碼出對稱加密的 key ,然後再使用這個 key 來解密最終的資料內容。是不是說得很暈?我們透過一張圖來看,或許大家就一目了然了。
其中,公鑰和私鑰就不用多解釋了。會話金鑰就是我們的對稱加密演算法的金鑰 key 。結合上面對數位信封傳輸過程的解釋,大家應該就能看懂了吧。
OpenSSL 擴充的對稱加密
好了,介紹這麼多理論知識,接下來還是回歸正題了,我們在PHP 中如何實現對稱和非對稱加密呢?非常簡單,使用 OpenSSL 擴充功能就可以了。這個擴充也是隨 PHP 原始碼一起發佈的,編譯安裝的時候加上 --with-openssl 就可以了。當然,它也是需要係統環境中安裝 OpenSSL 軟體的,在各類作業系統中基本上都已經直接有了,如果沒有的話就自己安裝一下即可。最簡單的,在作業系統命令列看看有沒有 openssl 指令就可以看出目前系統有沒有安裝 OpenSSL 相關的軟體。
[root@localhost ~]# openssl version OpenSSL 1.1.1 FIPS 11 Sep 2018
今天,我們主要學習的還是比較簡單的對稱加密相關的函數。
對稱加/解密實作
$data = '测试对称加密'; $key = '加密用的key'; $algorithm = 'DES-EDE-CFB'; $ivlen = openssl_cipher_iv_length($algorithm); $iv = openssl_random_pseudo_bytes($ivlen); $password = openssl_encrypt($data, $algorithm, $key, 0, $iv); echo $password, PHP_EOL; // 4PvOc75QkIJ184/RULdOTeO8 echo openssl_decrypt($password, $algorithm, $key, 0, $iv), PHP_EOL; // 测试对称加密 // Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended
openssl_encrypt() 是加密數據,它需要原文、演算法和金鑰三個參數,後面的參數是可選的,但現在是推薦自己來定義iv (向量) 參數,所以如果沒有iv 參數的話,會報一個警告訊息。我們使用 openssl_cipher_iv_length() 來取得目前演算法所需的 iv 長度,然後使用 openssl_random_pseudo_bytes() 函數來產生一個隨機的符合演算法長度的 iv 內容。
中間那個0 的參數是指定標記的位元或值,它有兩個可選常數:OPENSSL_RAW_DATA 和OPENSSL_ZERO_PADDING ,如果設定為OPENSSL_RAW_DATA 加密後的資料將按照原樣傳回(二進位亂碼內容),如果設定為OPENSSL_ZERO_PADDING ,加密後的資料將傳回為base64 之後的內容。
openssl_decrypt() 用來對資料進行解密,所需的參數基本上和加密函數一致,只是原文資料換成了加密資料。
在對稱加密中,我們也有一個 AEAD 密碼模式(GCM 或 CCM) ,在使用此模式的演算法時,我們需要多一參數。
$algorithm = 'aes-128-gcm'; $password = openssl_encrypt($data, $algorithm, $key, 0, $iv, $tags); echo $password, PHP_EOL; // dPYsR+sdP56rQ99CNxciah+N echo openssl_decrypt($password, $algorithm, $key, 0, $iv, $tags), PHP_EOL; // 测试对称加密
這個 $tags 是一個引用類型的參數,也就是加密後會賦值到這個變數中,解密的時候也需要相同的這個驗證標籤。
從加密解密的過程來看,如果我們要將這些資訊保存在資料庫中,或者進行傳輸解密時,我們至少要保存或傳輸這幾個字段,加密使用的iv ,加密使用的演算法,以及AEAD 模式的話加密所使用的驗證標籤,否則資料無法解密。
對稱加密演算法查詢
print_r(openssl_get_cipher_methods()); // Array // ( // [0] => AES-128-CBC // [1] => AES-128-CBC-HMAC-SHA1 // [2] => AES-128-CFB // [3] => AES-128-CFB1 // [4] => AES-128-CFB8 // [5] => AES-128-CTR // [6] => AES-128-ECB // [7] => AES-128-OFB // [8] => AES-128-XTS // [9] => AES-192-CBC // [10] => AES-192-CFB // [11] => AES-192-CFB1 // [12] => AES-192-CFB8 // …… // )
在上面加/解密測試中所選取的演算法就是從這個函數中找出來的,這個函數就是顯示目前環境下所有支援的演算法清單。
總結
這篇文章的內容有比較多的理論相關的知識,大家還是要多消化。使用 OpenSSL 實作加/解密的功能其實還是比較簡單的,畢竟東西都已經幫我們封裝好了,我們只要按照文件來呼叫函數就可以了。學習,還是要理論結合實際,當然,更重要的是自己多動手!
測試程式碼:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202007/source/PHP%E7%9A%84OpenSSL%E5%8A%A0%E5%AF%86%E6%89%A9%E5%B1%95%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86.php
推薦學習:php影片教學