上篇文章,我們了解了關于對稱和非對稱加密的一些相關的理論知識,也學習了使用 OpenSSL 來進行對稱加密的操作,今天,我們就更進一步,學習 OpenSSL 中的非對稱加密是如何實作的,
生成私鑰
通過之前的學習,我們知道非對稱加密是分別需要一個公鑰和一個私鑰的,我們就先來生成一個私鑰,也就是存放在我們這一端一個密鑰,請記住,在任何時候,私鑰都是不能給別人的哦!
$config = array(
"private_key_bits" => 4096, // 指定應該使用多少位來生成私鑰
);
$res = openssl_pkey_new($config); // 根據配置資訊生成私鑰
openssl_pkey_export($res, $privateKey); // 將一個密鑰的可輸出表示轉換為字串
var_dump($privateKey);
// -----BEGIN PRIVATE KEY-----
// MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDFMLW+9t3fNX4C
// YBuV0ILSyPAdSYVXtE4CLv32OvNk9yQZgF2nL/ZuIbBGRcYo2Hf5B31doGrAFDGu
// NoTR+WA7CBjKROFr/+yValsMFIeiKNtttWMkmBciysDJoEoyd6wjDD+kcHQdoJVo
// ……
// -----END PRIVATE KEY-----
非常簡單的一個函式 openssl_pkey_new() ,它接收一個引數,這個引數是可配置項并且是可選引數,生成的結果是一個私鑰句柄,不是我們能直接讀取的內容,所以我們再使用 openssl_pkey_export() 來提取可輸出的字串,
注釋中的內容就是我們生成的私鑰資訊了,私鑰資訊一般會相對多些,所以省略了后面的內容,
抽取公鑰
接下來就是生成公鑰了,其實,公鑰是從私鑰中抽取出來的,所以我們使用進行加解密的時候,都可以使用私鑰或者公鑰互相操作,
$publicKey = openssl_pkey_get_details($res); // 抽取公鑰資訊
var_dump($publicKey);
// array(4) {
// ["bits"]=>
// int(4096)
// ["key"]=>
// string(800) "-----BEGIN PUBLIC KEY-----
// MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtOIImDdS0W0vAr5Ra1+E
// hR2AJwQQwxntYKgTku8EmJRBX2vU+x8th8W8SnoGiVM/sOItG0HIe4Egf1UxoZHt
// gI6r+jpAp7JbTN0sD/VTPDE09F21+hFGjIVBqrkcLPjuEbf7+tjmgAx8cG8WLGId
// G8Hsub70kRANKJe1bCXIBUggRFk0sQGllxA/hxiG5wANqHTrdpJgJba+ahSi2+4H
// UWnyCV1O3AaPyz6a12HNUsG4Eio/tWv/hOB9POt6nAqwPHuIbhp56i5bv1ijMJZM
// jwRen5f/kwdZ01Ig2fi0uBoTR2y/EEaus7xBYpF/gGzZ/uM7cNUXcDyG5YluM/4R
// MEv4msPMVGB72izItED+C6Cqftxl98iBFRDc+PISFbRSgOU/HsuBhKkM5SYzyi3I
// Ypaej25++qLPqcA+EDr3JNDhNZ0GOhofCRtPq4dsr7iLLLRnZ0TnhIYe9wAbmO49
// uthABNBkM54bG+omOfY4Bkn5n39CKpELbhIiXgOd+lA684XUS/2Aw3Dvelc9Gbag
// oIFvb/wljPYsd0Zmd64CXBpTWbfwXC8K4vCKvFLjytcz2Yp4T6fVjbLT5RA6u8su
// E0WwE4QTFNKhnM5OvfiMN+NMc3Y/esVfcin3eyvotdz4N6Tt45dkybkf6aQE3Scg
// E/JBLIEEA+gjGTveY4cNUiECAwEAAQ==
// -----END PUBLIC KEY-----
// "
// ["rsa"]=>
// ……
$publicKey = $publicKey['key'];
使用 openssl_pkey_get_details() 抽取出來的內容包含很多內容,不過我們所需要的最主要的內容就是 key 下面的這個公鑰,
大家再回過頭來好好看一下公鑰和私鑰的內容,是不是和我們去申請的 HTTPS 證書中的公私鑰內容長得一樣,而且也和我們自己在系統中使用 openssl 命令列生成的本機的密鑰證書一樣,它們本身就是一樣的東西啦,只是在不同的場景應用的不同而已,HTTPS 證書除了非對稱加密的密鑰之外,還包含有 CA 資訊,如果 CA 不通過,瀏覽器也會認為證書是無效的,因此,我們使用自己生成的證書來充當 HTTPS 證書是不可以的,而本身生成的一般會用在 SSH 免密登錄上,或者是 GitHub 的免密代碼倉庫操作上,
加密解密資料
好了,公鑰和私鑰都生成完成了,那么我們就要進行最重要的加密和解密操作了,
$data = 'https://www.cnblogs.com/zyblog-coder/p/測驗非對稱加密';
// 公鑰加密資料
openssl_public_encrypt($data, $encrypted, $publicKey);
var_dump($encrypted);
// string(512) "??E??2??~??\d????q?O?=(??Y???3L????0?,?J????s?V??V?G~'?20???@??6?d?????#Z]?.??<Z??8G?????-??M?0](2??+$?*????\e?7????|SUw?#rFb?8"?s4K?B?Y?'?\S???~!<?"???!U??S(???S ???e??r??/???c??L?YL?'?E*S??[?J?"?n??`(?oF$?|kC?*j_y?E?D?O????H5???6?t?TY????b5l^)?`?v?>?1??a??r???D????????@?S?>?t|???匓?z~K?,???y??G???
// yXZ?L#??c `rj睅,nX???@{7?:?qy??nv?o§?@?@,?n&???I?~??z6???oe!8,T?????;??6?J@A??f????S]??!????2?b??+O???o?<?
// ????-?+et??})?KG??$???,?Z|?"
// 私鑰解密資料
openssl_private_decrypt($encrypted, $decrypted, $privateKey);
var_dump($decrypted);
// string(21) "測驗非對稱加密"
在這里,我們使用的就是最標準的公鑰加密,私鑰解密來進行的測驗,其實反過來也是可以的,OpenSSL 分別都為我們提供了公鑰的加解密和私鑰的加解密函式,
就像上篇文章的圖示那樣,對方獲得我們的公鑰,然后加密資料傳輸過來,我們通過自己的私鑰解密資料獲得原文,而我方也可以獲得對方的公鑰,并將回傳的資料加密后傳輸給對方,然后對方使用自己的私鑰進行解密獲得我們傳遞給它的原文資料,
而 HTTPS 是通過 CA 頒發的證書來獲取公鑰的,瀏覽器通過公鑰加密請求資料傳輸給服務器,服務器也是通過相同的原理來向瀏覽器客戶端發送密文資料,因此,在資料傳輸程序中,使用 HTTPS 的傳輸會更加地安全,即使被截獲了,對方也沒有證書提供的密鑰來進行解密,這就是現在所有 App 和 小程式 應用都要求使用 HTTPS 的原因,當然,我們如果做網站開發也最好使用 HTTPS ,就連百度對 HTTPS 的收錄也有相應的調整,
簽名及驗證
接下來我們再接觸一個簽名的概念,當兩端進行通信時,我們怎么知道當前傳輸過來的資料一定是對端發送過來的的呢,中間有沒有黑客進行了篡改呢?這個就可以通過簽名機制來進行驗證,
// 利用私鑰生成簽名
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
var_dump($signature);
// ??<?~?k??N????M?тq??;??h?d????m?3?nj??/i???U?_?E2z???>B?N?WM!TN?c?F?/??,5?|???%??c~O)"
// ?? >??)y?fn??q??}
// ?`
// ?z??{??_D?s`?????|y?,g>R?D??%?
// ?g?0?@Λ|??|z}???bZI2,????~Q_???I?LW~???G&???f?|eq?s?D???L???bC'D??~8:?Z????\?9]C?Kd~F96?S? 0??y>?(T??S}??1?謃T
// ?!??!!???<??fM?o7?3????????? 8ZR<Vya4????V??W?????L?QZbv??7??v`}???v ??0`?OF??F??@?$b?PBI?o\?v???D???"
// 公鑰驗證簽名
$r = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);
var_dump($r);
// int(1)
我們通過 openssl_sign() 來生成一個對原始資料的私鑰簽名,然后就可以使用 openssl_verify() 通過公鑰驗證資料簽名是否一致,
在使用的時候,發送方通過自己的私鑰生成簽名,由于簽名內容是亂碼的,我們可以將它 base64_encode() 一下,然后連同加密資料一起傳遞給接收方,然后接收方使用公鑰并根據簽名內容來驗證原文資料是否被篡改過,
// 發送方簽名
$resquestSign = base64_encode($signature);
// 假設通過網路請求發送了資料
// ……
// 接收到獲得簽名及原始資料
// $signature = $_POST['sign'];
// openssl_private_decrypt($_POST['data'], $data, $privateKey);
$responseSign = base64_decode($signature);
// 驗證資料有沒有被篡改
$r = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);
var_dump($r);
// int(1)
// 假設被篡改
$data = 'https://www.cnblogs.com/zyblog-coder/p/我被修改了';
$r = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);
var_dump($r);
// int(0)
總結
今天的內容是不是感覺比對稱加密復雜了許多,特別新引入的簽名的這個概念,其實很多證書相關的內容都會和資料簽名有關系,也就是說,看似簡單的一個 HTTPS ,其實瀏覽器和服務端的 openssl 幫我們做了很多事情,遠不止你去 CA 申請一套證書然后在 Nginx 配好那么簡單,那么,接下來,我們將要學習的就是生成證書相關的內容了,系好安全帶,車還要繼續飆,
測驗代碼:
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%BA%8C%EF%BC%89%EF%BC%9A%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86.php
參考檔案:
https://www.php.net/manual/zh/function.openssl-pkey-new.php
https://www.php.net/manual/zh/function.openssl-pkey-get-details.php
https://www.php.net/manual/zh/function.openssl-pkey-export.php
https://www.php.net/manual/zh/function.openssl-public-encrypt.php
https://www.php.net/manual/zh/function.openssl-private-decrypt.php
https://www.php.net/manual/zh/function.openssl-sign.php
https://www.php.net/manual/zh/function.openssl-verify.php
關注公眾號:【硬核專案經理】獲取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、專案管理學習資料
知乎、公眾號、抖音、頭條搜索【硬核專案經理】
B站ID:482780532
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/285576.html
標籤:PHP
