PHP OpenSSL擴(kuò)展 - 非對(duì)稱加密

PHP 在進(jìn)入7.x 時(shí)代后,默認(rèn)就不再附帶 mcrypt 擴(kuò)展,mcrypt 將被 openssl_* 一族函數(shù)所替代。所以,對(duì)于 PHPer 來說,有必要學(xué)習(xí)一下 PHP 的 OpenSSL 擴(kuò)展。

上一篇文章《PHP中OpenSSL擴(kuò)展 - 對(duì)稱加密》 ,介紹了 OpenSSL 擴(kuò)展中對(duì)稱加密的使用方法,本文將介紹非對(duì)稱加密的使用方法。

PHP 的 OpenSSL 擴(kuò)展中,非對(duì)稱加密的相關(guān)函數(shù)有:

  • openssl_pkey_new
  • openssl_pkey_export
  • openssl_pkey_export_to_file
  • openssl_pkey_get_details
  • openssl_pkey_free
  • openssl_pkey_get_private
  • openssl_pkey_get_public
  • openssl_get_privatekey
  • openssl_get_publickey
  • openssl_private_decrypt
  • openssl_private_encrypt
  • openssl_public_decrypt
  • openssl_public_encrypt

別被這么多函數(shù)嚇倒,經(jīng)過本文的講解,你會(huì)發(fā)現(xiàn)非對(duì)稱加密的過程并不繁瑣。讓我們通過實(shí)例來講解每個(gè)函數(shù)的作用。

1. 生成密鑰對(duì)

首先,想要進(jìn)行非對(duì)稱加密 / 解密,你得有一對(duì)公鑰(Public key)和私鑰(Private key)。在Linux環(huán)境下,公鑰私鑰可以用 openssl 命令生成。PHP的 OpenSSL 擴(kuò)展中,openssl_pkey_new() 函數(shù)可以完成同樣的事:

<?php
// 生成私鑰
$privateKey = openssl_pkey_new();
openssl_pkey_export($privateKey, $out);
echo $out;

上面兩行代碼生成了一個(gè)私鑰,并導(dǎo)出到了 $out 變量中。

延伸一下,如果你打印 $out 變量,會(huì)看見一個(gè)由大小寫字母和數(shù)字組成的“亂碼塊”,外層被-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----包裹著。這其實(shí)是 PEM 格式的私鑰,亂碼塊是被 Base64 編碼的二進(jìn)制數(shù)據(jù)。

注意到現(xiàn)在只生成了一個(gè)私鑰,那么公鑰在哪呢?OpenSSL擴(kuò)展并沒有生成公鑰的函數(shù),公鑰是從私鑰當(dāng)中提取出來的,使用 openssl_pkey_get_details() 函數(shù):

從私鑰提取公鑰
<?php
$privateKey = openssl_pkey_new();
$detail = openssl_pkey_get_details($privateKey);
$publicKeyString = $detail['key'];
echo $publicKeyString;

openssl_pkey_get_details() 接受一個(gè)私鑰對(duì)象,返回一個(gè) array 包含私鑰中附帶的相關(guān)信息,比如 RSA 的 n、e1、e2 值。。。不用深究這幾個(gè)值,他們已經(jīng)是密碼學(xué)原理才能解釋的東西了。我們只關(guān)心分析結(jié)果的 key 值,key 值就是提取出來的公鑰啦。

我們將公鑰私鑰分別保存到磁盤上:

<?php
// 如果密鑰已經(jīng)是PEM格式的了,那就直接寫到磁盤上
file_put_contents('public.key', $publicKeyString);

// 否則需要用 openssl_pkey_export()
// 或者openssl_pkey_export_to_file()
// 轉(zhuǎn)換成PEM格式
openssl_pkey_export_to_file($privateKey, 'private.key');

有了一對(duì)公鑰、私鑰之后,就可以進(jìn)行非對(duì)稱加密了。注意 公鑰 可以分發(fā)給別人用的,而 私鑰 只能你自己知道,否則非對(duì)稱加密系統(tǒng)就完全失效了。

2. 非對(duì)稱 加密 與 解密

在不管是加密還是解密,都要先讀取密鑰。上一節(jié)我們保存在磁盤上的密鑰是PEM格式的,不能直接用,需要用 openssl_pkey_get_public()openssl_pkey_get_private() 讀取:

<?php
$privateKey = openssl_pkey_get_private(file_get_contents('private.key'));
$publicKey = openssl_pkey_get_public(file_get_contents('public.key'));

另外還有兩個(gè)函數(shù):openssl_get_privatekey()openssl_get_publickey(),只是上述兩個(gè)函數(shù)的別名,完成的功能相同。

介紹了一大堆,終于到了真正的加密解密了。因?yàn)槭欠菍?duì)稱加密,所以公鑰和私鑰是交錯(cuò)使用的:公鑰加密的數(shù)據(jù)用密鑰解密,同樣,私鑰加密的數(shù)據(jù)用公鑰解密。

公鑰加密私鑰解密:
// 加密
$publicKey = openssl_pkey_get_public(file_get_contents('public.key'));
openssl_public_encrypt('PHP是世界上最好的語言!', $encrypted, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
echo $encrypted . PHP_EOL;

// 解密
$privateKey = openssl_pkey_get_private(file_get_contents('private.key'));
openssl_private_decrypt($encrypted, $decrypted, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
echo $decrypted . PHP_EOL;

結(jié)果:

??qz?icG?!??N?.?0QM]_+B??i=輬???>1N`Z????¨Z9qr??<z?H???dy??я3T??G?q?HE???SAxd綧h` ??6????£n??Q1?q??????
PHP是世界上最好的語言!

可以看到經(jīng)過加密后,明文已經(jīng)變成完全無法閱讀的亂碼了。經(jīng)過解密后又變回了原文。公鑰加密私鑰解密滿足了最常見的數(shù)據(jù)保密的需求,別人用你的公鑰加密的數(shù)據(jù),只能用你自己的私鑰解開。

私鑰加密公鑰解密
// 加密
$privateKey = openssl_pkey_get_private(file_get_contents('private.key'));
openssl_private_encrypt('PHP是世界上最好的語言!', $encrypted, $privateKey);
echo $encrypted . PHP_EOL;

// 解密
$publicKey = openssl_pkey_get_public(file_get_contents('public.key'));
openssl_public_decrypt($encrypted, $decrypted, $publicKey);
echo $decrypted . PHP_EOL;

輸出的結(jié)果與公鑰加密類似,就不再贅述了。私鑰加密公鑰解密一般用于簽名,因?yàn)橛媚愕乃借€加密的內(nèi)容,大家只能用你的公鑰解開,從而保證了加密的信息確實(shí)是由你發(fā)出的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容