因此更多時候我們會採用 『非對稱加密演算法』 !透過公鑰與私鑰的方式能讓資料加密更安全,更有保障!
具體的做法如下…
什麼是 RSA 加密演算法?
Wiki 上定義的 RSA加密演算法是一種非對稱加密演算法 (Wiki 真是偷懶好東西)
簡單來說,加密時需要透過 公鑰 進行,而解密時需要利用 私鑰 進行解密!
也就是說,就算 Client 端遭到破解,造成 公鑰 流出,也不會有任何危害。
‼️ 注意:透過 RSA 加密後的內容(密文),每次都會不同!
因為 演算法 (PKCS PADDING) 的關係,每次加密後的結果都不同,但透過同組公、私鑰都能正常解密.
(PKCS PADDING 會在加密前對明文內容加入亂數填充 Padding – wiki)
如何產生公鑰與私鑰?
在這裡,需要利用 openssl 產出一 私鑰(private key)
你需要在 CLI (command-line interface) 裡使用 opensll 輸入下面指令,windows 的使用者請先安裝 openssl (這次先不另外講解這部份…)
可以透過兩種方式建立 公鑰與私鑰,則一即可..
方法一:
>openssl genrsa -out private_1024.key 1024
使用 RSA 1024 位元加密,產出 Private Key
接著再使用剛剛產出的 private key 建立 public key
>openssl rsa -in private.key -out public_1024.pem -outform PEM -pubout
方法二: (產出的副檔名,不是那麼重要)
>sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout ~/php/private_2048.key -out ~/php/public_2048.crt
上述方式較為嚴僅,可以設定更多細節,可以參考(https://blog.gtwang.org/linux/nginx-create-and-install-ssl-certificate-on-ubuntu-linux/)
一些參數說明….(也可以選擇要不要再加入密碼保護 )
- req:使用 X.509 Certificate Signing Request(CSR) Management 產生憑證。
- x509:建立自行簽署的憑證。
- nodes:不要使用密碼保護,因為這個憑證是 NGINX 伺服器要使用的,如果設定密碼的話,會讓伺服器每次在啟動時書需要輸入密碼
- days 3650:設定憑證的使用期限,單位是天,如果不想時常重新產生憑證,可以設長一點。
- newkey rsa:2048:同時產生新的 RSA 2048 位元的金鑰。
- keyout:設定金鑰儲存的位置。
- out:設定憑證儲存的位置。
分別產出 私鑰 (Private Key) 和公鑰 (Public Key) ,也確認兩者都放到正確位置就可以開始實做&測試加解密了!
若要將公鑰交給 iOS 或 Android 端,他們會需要 .pem 或 .der 檔,可以參考下面的文章進行轉換
OpenSSL 操作筆記 – 檔案格式轉換
PHP 實做 RSA, 公私鑰非對稱加解密 – 範例
用簡單的 Plain PHP 做範例…
// 加密前原始資料-信用卡資料
$fullText = '{"card_number":"1234567812345678","expiry_date":"202012","cvc2":"000"}';
// 設定公、私鑰檔名
const PRIVATE_KEY = 'private_1024.key';
const PUBLIC_KEY = 'public_1024.pem';
// 如果你是用第二種方法產生
// const PRIVATE_KEY = 'private_2048.key';
// const PUBLIC_KEY = 'public_2048.crt';
// 如果設定密碼
// const PASSPHRASE = 'my_pasword';
function public_encrypt($plain_text)
{
$fp = fopen(PUBLIC_KEY, "r");
$pub_key = fread($fp, 8192);
fclose($fp);
$pub_key_res = openssl_get_publickey($pub_key);
if(!$pub_key_res) {
throw new Exception('Public Key invalid');
}
openssl_public_encrypt($plain_text, $crypt_text, $pub_key_res, OPENSSL_PKCS1_OAEP_PADDING);
openssl_free_key($pub_key_res);
return base64_encode($crypt_text); // 加密後的內容為 binary 透過 base64_encode() 轉換為 string 方便傳輸
}
function private_decrypt($encrypted_text)
{
$fp = fopen(PRIVATE_KEY, "r");
$priv_key = fread($fp, 8192);
fclose($fp);
$private_key_res = openssl_get_privatekey($priv_key);
// $private_key_res = openssl_get_privatekey($priv_key, PASSPHRASE); // 如果使用密碼
if(!$private_key_res) {
throw new Exception('Private Key invalid');
}
// 先將密文做 base64_decode() 解釋
openssl_private_decrypt(base64_decode($encrypted_text), $decrypted, $private_key_res, OPENSSL_PKCS1_OAEP_PADDING);
openssl_free_key($private_key_res);
return $decrypted;
}
// 將資料進行加密
$r = public_encrypt($fullText);
var_dump($r);
// 將資料進行解密
$r = private_decrypt($r);
var_dump($r);
非常簡單吧~
另外… 使用 1024, 2048 更甚至是 3072 加密時所需的加、解密耗時
1024
加解密約 0.0001~0.0006 秒
失敗時約 0.005~0.007 秒
3072
加解密約 0.0003~0.0007 秒
失敗時約 0.006~0.009 秒
做個參考~
PHP openssl 內建函式說明
官方手冊:openssl_public_encrypt
官方手冊:openssl_private_decrypt
上述兩個函式第四個參數 padding 模式可以指定加、解密使用的演算法
(OPENSSL_PKCS1_PADDING, OPENSSL_SSLV23_PADDING, OPENSSL_PKCS1_OAEP_PADDING, OPENSSL_NO_PADDING)
預設為:OPENSSL_PKCS1_PADDING
目前比較推薦 OPENSSL_PKCS1_OAEP_PADDING
iOS, Android 串接 – 注意事項
- Server 端產出 private key + public key 後,先自行測試 publice key 加密、private key 解密
- 將 public key 提供給 iOS 或 Android 端
- 提供必要資訊
padding mode:OPENSSL_PKCS1_OAEP_PADDING - 建議提供一個 API 做為 server 與 client 兩者之間的加、解密測試用(方便開發!)
e.g. client 同時傳入自己處理好的密文
與明文
server 解密後與明文做比對,相同 回傳 true,反之 回傳 false
這樣能加快兩方的開發速度
‼️ 注意: 產生金鑰時的長度, RSA 1024 或 RSA 2048 會影響可被加密的明文長度
e.g 1024 bit 的金鑰能加密的明文只有約 128 bytes (實際上再少一些,詳情請看 參考文章 – 密文與明文長度)
因此如果預期明文的長度比較長,建議最初生成 RSA 金鑰的時候設定大一點 2048, 4096…
不好意思 我想請問 我產生的 private_1024.key 要放在電腦的哪個地方??
放在任何位置都可以哦,只是你 PHP 能讀取的地方就可以了。
通常和專案放在一塊,比較方便測試和讀取。
安全一點的話,可以放到 .ssh 這類,安全性比較高的目錄。