1. 程式人生 > >php openssl擴充套件學習以及基本使用

php openssl擴充套件學習以及基本使用

https://www.cnblogs.com/baocheng/p/5910448.html

先了解一下基本概念:

公鑰/私鑰/簽名/驗證簽名/加密/解密/對稱加密/非對稱加密

公鑰與私鑰是通過一種演算法得到的一個金鑰對(即一個公鑰和一個私鑰),公鑰是金鑰對中公開的部分,私鑰則是非公開的部分。公鑰通常用於加密會話金鑰、驗證數字簽名,或加密可以用相應的私鑰解密的資料.

當然,公鑰和私鑰都可以用來加密資料,用另一個解開。這裡有兩種情況,公鑰加密私鑰解密的情況被稱為加密解密;私鑰加密資料,公鑰解密一般被稱為簽名和驗證簽名.

公鑰和私鑰是一一對應的,公鑰加密的資料只有它相對應的私鑰可以解開,當你想跟A通訊時,和A交換公鑰,你要發給A的資料就用A的公鑰加密,他收到資料後用自己的私鑰解密,同理A傳送訊息時也是這種操作,這樣最大程度保證了安全性。

但是我們如何知道和我溝通的就真的是A嗎?所以就要用上簽名,用自己的私鑰對資料進行簽名傳送給A,這個資料就只有配對的公鑰可以解開,而這個私鑰只有我有,所以如果配對的公鑰解開了資料,就說明這資料是我發的,相反,則不是.這個被稱為簽名,A用我的公鑰解密資料叫做驗證簽名。

加密時用一個密碼加密檔案,然後解密也用同樣的密碼,這個是對稱加密。而有些加密時,加密用的一個密碼,而解密用另外一組密碼,這個叫非對稱加密,意思就是加密解密的密碼不一樣

openssl擴充套件提供了很多的函式,這裡介紹一下常用的函式:

對稱加密函式:

加密:string openssl_encrypt ( string $data , string $method , string $password)

其中 d a t a
data為其要加密的資料,
method是加密要使用的方法, p a s s w o r d 使 password是要使用的密匙,函式返回加密後的資料, method列表可以使用openssl_get_cipher_methods()來獲取

解密:string openssl_encrypt ( string $data , string $method , string $password)

非對稱加密函式:

openssl_get_publickey();   別名openssl_pkey_get_public(); // 從證書匯出公匙;
openssl_get_privatekey(); 別名openssl_pkey_get_private(); // 從證書匯出私匙;

openssl_public_encrypt(string $data , string &$crypted , mixed $key [, int $padding = OPENSSL\_PKCS1\_PADDING ] )

使用公匙加密資料,其中 d a t a data是要加密的資料; crypted是一個引用變數,加密後的資料會被放入這個變數中; k e y b i t key是要傳入的公匙資料;由於被加密資料分組時,有可能不會正好為加密位數bit的整數倍,所以需要 padding(填充補齊),$padding的可選項有 OPENSSL_PKCS1_PADDING, OPENSSL_NO_PADDING,分別為PKCS1填充,或不使用填充;

與此方法相對的還有(傳入引數一致):

openssl_private_encrypt(); // 使用私匙加密;
openssl_private_decrypt(); // 使用私匙解密;
openssl_public_decrypt(); // 使用公匙解密;

簽名和驗籤函式:

bool openssl_sign ( string $data , string &$signature , mixed $priv_key_id [, mixed $signature_alg = OPENSSL_ALGO_SHA1 ] )
int openssl_verify ( string $data , string $signature , mixed $pub_key_id [, mixed $signature_alg = OPENSSL_ALGO_SHA1 ] )

簽名函式: d a t a data為要簽名的資料; signature為簽名結果的引用變數; p r i v k e y i d 使 ; priv_key_id為簽名所使用的私匙; signature_alg為簽名要使用的演算法,其演算法列表可以使用openssl_get_md_methods ()得到

這裡我針對上述常用函式封裝了一個加密解密類,實現了基本的加密解密簽名驗籤功能,程式碼如下:

class OpensslClass {


private $dn;
private $privkeypass = '111111'; //私鑰密碼 
private $numberofdays = 365; //有效時長 
private $cerpath = "./sign/test.cer"; //生成證書路徑 
private $pfxpath = "./sign/test.pfx"; //金鑰檔案路徑 
private $prikeypath = "./key/privkey.pem"; //私鑰檔案
private $pubkeypath = "./key/pubkey.key"; //公鑰檔案
public function __construct(array $dn) 
{
$this->dn = $dn;
}
/**
* 對稱加密
* @param string 明文
* @param string key
* @param string 加密方式
* @return array [密文, 偽隨機位元組串]
*/
public function symmetric_encrypt($plaintext, $key, $cipher = 'aes-128-cbc')
{
if (in_array($cipher, openssl_get_cipher_methods()))//判斷傳遞的加密演算法是否在可用的加密演算法的列表中
{
$ivlen = openssl_cipher_iv_length($cipher); //獲取密碼初始化向量(iv)長度
$iv = openssl_random_pseudo_bytes($ivlen); //生成一個偽隨機位元組串 string ,位元組數由 length 引數指定
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv);
return [$ciphertext,$iv];
} else {
return '加密方式傳遞錯誤';
}
}
//對稱解密
/**
* 對稱解密
* @param string $ciphertext 密文
* @param string $key 金鑰
* @param string $iv 偽隨機位元組串
* @param string $cipher 加密方式
* @return string 明文
*/
public function symmetric_decrypt($ciphertext, $key, $iv, $cipher = 'aes-128-cbc')
{
if (in_array($cipher, openssl_get_cipher_methods()))
{
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv);
return $original_plaintext;
} else {
return '加密方式傳遞錯誤';
}
}
/**
* 獲取私鑰公鑰
* @param array $configargs 配置
* @return bool [description]
*/
public function getPrivateKeyAndPublicKey($configargs = array())
{

if (empty($configargs)) {
$configargs = array(
'private_key_bits' => 1024, // Size of Key.
'private_key_type' => OPENSSL_KEYTYPE_RSA
);
}
//$res返回false的時候,檢查發現,是window系統缺少了openssl環境變數,解決方法如下:
$opensslConfigPath = "D:/phpstudy/PHPTutorial/Apache/conf/openssl.cnf"; //apache路徑下的openssl.conf檔案路徑
$res = openssl_pkey_new($configargs);////生成一個新的私鑰 openssl_pkey_new ([ array $configargs ] ) configargs引數微調金鑰的生成(比如private_key_bits 指定應該使用多少位來生成私鑰)
if(!$res) {
$configargs['config'] = $opensslConfigPath;
$res = openssl_pkey_new($configargs);
} 
openssl_pkey_export($res, $privKey, null, $configargs);//將一個金鑰的可輸出表示轉換為字串
$file = fopen($this->prikeypath, 'w');
fwrite($file, trim($privKey));
fclose($file);
$pubkey=openssl_pkey_get_details($res);
$pubkey=$pubkey["key"];
$file = fopen($this->pubkeypath, 'w');
fwrite($file, trim($pubkey));
fclose($file);
return true;
}
//非對稱私鑰加密
public function asymmetric_private_encrypt($plaintext)
{
$pkey = openssl_pkey_get_private(file_get_contents($this->prikeypath));
openssl_private_encrypt($plaintext,$crypttext,$pkey); 
$crypttext = base64_encode($crypttext);//加密後的內容通常含有特殊字元,需要編碼轉換下,在網路間通過url傳輸時要注意base64編碼是否是url安全的
return $crypttext;
}
//非對稱公鑰解密
public function asymmetric_public_decrypt($crypttext)
{
$pubkey = file_get_contents($this->pubkeypath);
$res = openssl_pkey_get_public($pubkey);
openssl_public_decrypt(base64_decode($crypttext), $decrypttext, $res);
return $decrypttext;
}
//非對稱公鑰加密
public function asymmetric_public_encrypt($plaintext)
{
$pkey = openssl_pkey_get_public(file_get_contents($this->pubkeypath));
openssl_public_encrypt($plaintext,$crypttext,$pkey); 
$crypttext = base64_encode($crypttext);//加密後的內容通常含有特殊字元,需要編碼轉換下,在網路間通過url傳輸時要注意base64編碼是否是url安全的
return $crypttext;
}
//非對稱私鑰解密
public function asymmetric_private_decrypt($crypttext)
{
$pubkey = file_get_contents($this->prikeypath);
$res = openssl_pkey_get_private($pubkey);
openssl_private_decrypt(base64_decode($crypttext), $decrypttext, $res);
return $decrypttext;
}
//證書生成
public function getCertificate()
{
$configargs = array(
'config' => "D:/phpstudy/PHPTutorial/Apache/conf/openssl.cnf",
'private_key_bits' => 1024, // Size of Key.
'private_key_type' => OPENSSL_KEYTYPE_RSA
);
//生成證書 
$privkey = openssl_pkey_new($configargs); //生成一個新的私鑰 openssl_pkey_new ([ array $configargs ] ) configargs引數微調金鑰的生成(比如private_key_bits 指定應該使用多少位來生成私鑰)
$csr = openssl_csr_new($this->dn, $privkey,$configargs); //根據dn提供的資訊生成新的CSR(證書籤名請求) privkey 應該被設定為由openssl_pkey_new()函式預先生成(或者以其他方式從openssl_pkey函式集中獲得)的私鑰。該金鑰的相應公共部分將用於簽署CSR.
$sscert = openssl_csr_sign($csr, null, $privkey, $this->numberofdays,$configargs); //用另一個證書籤署 CSR (或者本身) 並且生成一個證書 從給定的 CSR 生成一個x509證書資源
openssl_x509_export($sscert, $csrkey); //匯出證書$csrkey 將 x509 以PEM編碼的格式匯出到名為 output 的字串型別的變數中 公鑰證書 只有公鑰
openssl_pkcs12_export($sscert, $privatekey, $privkey, $this->privkeypass); //匯出金鑰$privatekey 
//生成證書檔案 
$fp = fopen($this->cerpath, "w"); 
fwrite($fp, $csrkey); 
fclose($fp); 
//生成金鑰檔案 
$fp = fopen($this->pfxpath, "w"); 
fwrite($fp, $privatekey); 
fclose($fp); 
return true;
}
/**
* 簽名
* @param string $data 明文
* @return string 加密資訊
*/
public function sign($data)
{
$priv_key = file_get_contents($this->pfxpath); //獲取金鑰檔案內容 
//私鑰加密 
openssl_pkcs12_read($priv_key, $certs, $this->privkeypass); //讀取公鑰、私鑰 
$prikeyid = $certs['pkey']; //私鑰 
openssl_sign($data, $signMsg, $prikeyid,OPENSSL_ALGO_SHA1); //註冊生成加密資訊 
$signMsg = base64_encode($signMsg); //base64轉碼加密資訊 加密後的內容通常含有特殊字元,需要編碼轉換下,在網路間通過url傳輸時要注意base64編碼是否是url安全的
return $signMsg;
}
/**
* 公鑰驗證簽名
* @param string $data 明文
* @param string $signMsg 加密資訊
* @return bool 是否驗證通過
*/
public function verify($data,$signMsg)
{
$priv_key = file_get_contents($this->pfxpath); 
//公鑰解密 
$unsignMsg=base64_decode($signMsg);//base64解碼加密資訊 
openssl_pkcs12_read($priv_key, $certs, $this->privkeypass); //讀取公鑰、私鑰 
$pubkeyid = $certs['cert']; //公鑰 
$res = openssl_verify($data, $unsignMsg, $pubkeyid); //驗證 
return $res; //輸出驗證結果,1:驗證成功,0:驗證失敗 
}
}

測試簽名驗籤:

$dn = array( 
"countryName" => 'XX', //所在國家名稱 
"stateOrProvinceName" => 'State', //所在省份名稱 
"localityName" => 'SomewhereCity', //所在城市名稱 
"organizationName" => 'MySelf', //註冊人姓名 
"organizationalUnitName" => 'Whatever', //組織名稱 
"commonName" => 'mySelf', //公共名稱 
"emailAddress" => '[email protected]' //郵箱 
); 
$test = new OpensslClass($dn);
$str = '這是一個驗證簽名測試文字';

$test->init();

$signtext = $test->sign($str);
echo '加密資訊 :'.$signtext;
echo '<br/>';
echo '驗證結果 :'.$test->verify($str,$signtext);

結果:

在這裡插入圖片描述

測試加密解密:

$test = new OpensslClass($dn);
$str = '這是一個驗證加密解密測試文字';
$test->getPrivateKeyAndPublicKey();
$entext = $test->asymmetric_public_encrypt($str);
$detext = $test->asymmetric_private_decrypt($entext);
echo $entext.'<br />';
echo $detext;

結果:

在這裡插入圖片描述

openssl還提供了很多函式,其中涉及了幾個名詞,pkcs12,pkcs7,x509,圍繞這幾個名詞提供了許多函式,我在網上查閱了一些部落格,這裡介紹一下它們的含義作用以及區別:

x509,公鑰證書,只有公鑰。
pkcs7,簽名或加密。可以往裡面塞x509,同時沒有簽名或加密內容。
pkcs12,含有私鑰,同時可以有公鑰,有口令保護。
pkcs7的作用就是電子信封。
X509是基本規範
pkcs7和pkcs12是兩個實現規範,pkcs7是數字信封,pkcs12是帶有私鑰的證書規範。
x509是數字證書的規範,pkcs7和pkcs12是兩種封裝形式。比如說同樣的電影,有的是avi格式,有的是mpg,大概就這個意思。

pkcs7一般是把證書分成兩個檔案,一個公鑰一個私鑰,有PEM和DER兩種編碼方式。PEM比較多見,就是純文字的,pkcs7一般是分發公鑰用,看到的就是一串可見字串,副檔名經常是.crt,.cer,.key等。DER是二進位制編碼。
pkcs12是把證書壓成一個檔案,.pfx 。主要是考慮分發證書,私鑰是要絕對保密的,不能隨便以文字方式散播。所以pkcs7格式不適合分發。.pfx中可以加密碼保護,所以相對安全些。
在實踐中要中,使用者證書都是放在USB Key中分發,伺服器證書經常還是以檔案方式分發。伺服器證書和使用者證書,都是X509證書,就是裡面的屬性有區別。

X509 是證書規範
pkcs7 是訊息語法 (常用於數字簽名與加密)
pkcs12 個人訊息交換與打包語法 (如.PFX .pkcs12)打包成帶公鑰與私鑰