首頁 > 後端開發 > PHP問題 > php如何實現https雙向認證

php如何實現https雙向認證

藏色散人
發布: 2023-03-04 08:46:01
原創
4591 人瀏覽過

php實作https的方法:先修改openssl設定;然後建立CA根級證書,並將憑證密碼設定長度大於或等於6位元;接著建立server憑證和client憑證;最後設定nginx並進行php curl測試即可。

php如何實現https雙向認證

推薦:《PHP影片教學

php實作https(tls/ssl)雙向認證

通常情況下,部署https的時候,是基於ssl單向認證的,也就是說只要客戶端認證伺服器,而伺服器不需要認證客戶端。

但在一些安全性較高的場景,如銀行,金融等領域,通常會要求進行客戶端認證。從而實現ssl的雙向認證。

由於nginx的ssl_client_certificate參數只能指定一個客戶端公鑰,如果增加一個客戶端進行通訊就要重新配配一個server。

n:1的模式是透過CA的級聯證書模式實現的,首先自己產生一套CA根級證書,再藉助其產生二級證書作為client證書。

此時client私鑰簽章不僅可以透過對應的client公鑰驗證,還可透過根憑證的公鑰進行驗證。

看到這裡應該豁然開朗了吧,下面簡單介紹下具體怎麼操作:

1 準備工作

#1.1 openssl目錄準備

一般情況下openssl的設定檔都在這個目錄/etc/pki/tls,so:

mkdir /etc/pki/ca_linvo
cd /etc/pki/ca_linvo
mkdir root server client newcerts
echo 01 > serial
echo 01 > crlnumber
touch index.txt
登入後複製

1.2 openssl設定準備

修改openssl設定

#vi /etc/pki/ tls/openssl.cnf

找到這句註解掉,替換為下面那句

#default_ca      = CA_default
default_ca      = CA_linvo
登入後複製

把[ CA_default ]整個部分拷貝一份,改成上面的名字[ CA_linvo ]

修改裡面的如下參數:

dir = /etc/pki/ca_linvo
certificate = $dir/root/ca.crt
private_key = $dir/root/ca.key
登入後複製

儲存退出

2 建立CA根級憑證

生成key:openssl genrsa -out /etc/pki/ca_linvo/root/ca.key
生成csr:openssl req -new -key /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/root/ca.csr
生成crt:openssl x509 -req -days 3650 -in /etc/pki/ca_linvo/root/ca.csr -signkey /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/root/ca.crt
生成crl:openssl ca -gencrl -out /etc/pki/ca_linvo/root/ca.crl -crldays 7
登入後複製

產生的根級憑證檔案都在/etc/pki/ ca_linvo/root/目錄下

注意:建立憑證時,建議憑證密碼設定長度>=6位,因為Java的keytool工具似乎對它有要求。

3 建立server憑證

生成key:openssl genrsa -out /etc/pki/ca_linvo/server/server.key
生成csr:openssl req -new -key /etc/pki/ca_linvo/server/server.key -out /etc/pki/ca_linvo/server/server.csr
生成crt:openssl ca -in /etc/pki/ca_linvo/server/server.csr -cert /etc/pki/ca_linvo/root/ca.crt -keyfile /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/server/server.crt -days 3650
登入後複製

說明:

1、這裡產生的crt是剛才ca根級憑證下的級聯證書,其實server憑證主要用於配置正常單向的https,所以不使用級聯模式也可以:

openssl rsa -in /etc/pki/ca_linvo/server/server.key -out /etc/pki/ca_linvo/server/server.key
openssl x509 -req -in /etc/pki/ca_linvo/server/server.csr -signkey /etc/pki/ca_linvo/server/server.key -out /etc/pki/ca_linvo/server/server.crt -days 3650
登入後複製

2、-days 參數可依需求設定憑證的有效期限,例如預設365天

4 建立client憑證

生成key:openssl genrsa -des3 -out /etc/pki/ca_linvo/client/client.key 1024
生成csr:openssl req -new -key /etc/pki/ca_linvo/client/client.key -out /etc/pki/ca_linvo/client/client.csr
生成crt:openssl ca -in /etc/pki/ca_linvo/client/client.csr -cert /etc/pki/ca_linvo/root/ca.crt -keyfile /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/client/client.crt -days 3650
登入後複製

說明:

1、這裡就必須使用級聯證書,並且可以重複該步驟,創建多套client證書

2、生成crt時可能會遇到如下報錯誤:

openssl TXT_DB error number 2 failed to update database
登入後複製

可參考這裡進行操作。

我使用的是方法一,即將index.txt.attr中unique_subject = no

5 設定nginx

這裡只列出server段的關鍵部分:

ssl_certificate  /etc/pki/ca_linvo/server/server.crt;#server公钥
ssl_certificate_key  /etc/pki/ca_linvo/server/server.key;#server私钥
ssl_client_certificate   /etc/pki/ca_linvo/root/ca.crt;#根级证书公钥,用于验证各个二级client
ssl_verify_client on;
登入後複製

重啟Nginx

6 測試

6.1 瀏覽器測試

由於是雙向認證,直接透過瀏覽器存取https位址是被告知400 Bad Request( No required SSL certificate was sent)的,需要在本機安裝client憑證。

windows上安裝的憑證需要pfx格式,也叫p12格式,產生方式如下:

openssl pkcs12 -export -inkey /etc/pki/ca_linvo/client/client.key -in /etc/pki/ca_linvo/client/client.crt -out /etc/pki/ca_linvo/client/client.pfx
登入後複製

然後考到windows中雙擊即可進行安裝,安裝時會提示輸入產生憑證時設定的密碼。

安裝成功後,重啟瀏覽器輸入網址訪問,瀏覽器可能會提示你選擇證書,選擇剛才安裝的那個證書即可。

此時有些瀏覽器會提示使用者該憑證不受信任,地址不安全之類,這是因為我們的server憑證是我們自己頒發的,而非真正的權威CA機構頒布(通常很貴哦~),忽略它既可。

6.2 php curl測試

這裡只列出關鍵的需要設定的curl參數:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书,不是CA机构颁布的也没关系  
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); // 检查证书中是否设置域名,如果不想验证也可设为0  
curl_setopt($ch, CURLOPT_VERBOSE, '1'); //debug模式,方便出错调试  
curl_setopt($ch, CURLOPT_SSLCERT, CLIENT_CRT); //client.crt文件路径,这里我用常量代替  
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, CRT_PWD); //client证书密码  
curl_setopt($ch, CURLOPT_SSLKEY, CLIENT_KEY); //client.key文件路径  
 
CURLOPT_TIMEOUT:超时时间
CURLOPT_RETURNTRANSFER:是否要求返回数据
CURLOPT_SSL_VERIFYPEER:是否检测服务器的证书是否由正规浏览器认证过的授权CA颁发的
CURLOPT_SSL_VERIFYHOST:是否检测服务器的域名与证书上的是否一致
CURLOPT_SSLCERTTYPE:证书类型,"PEM" (default), "DER", and"ENG".
CURLOPT_SSLCERT:证书存放路径
CURLOPT_SSLCERTPASSWD:证书密码,没有可以留空
CURLOPT_SSLKEYTYPE:私钥类型,"PEM" (default), "DER", and"ENG".
CURLOPT_SSLKEY:私钥存放路径
 
 
function curl_post_ssl($url, $vars, $second=30,$aHeader=array())
{
    $ch = curl_init();
    //curl_setopt($ch,CURLOPT_VERBOSE,'1');
    curl_setopt($ch,CURLOPT_TIMEOUT,$second);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch,CURLOPT_URL,$url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);
    curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
    curl_setopt($ch,CURLOPT_SSLCERT,'/data/cert/php.pem');
    curl_setopt($ch,CURLOPT_SSLCERTPASSWD,'1234');
    curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
    curl_setopt($ch,CURLOPT_SSLKEY,'/data/cert/php_private.pem');
 
    if( count($aHeader) >= 1 ){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
    }
 
    curl_setopt($ch,CURLOPT_POST, 1);
    curl_setopt($ch,CURLOPT_POSTFIELDS,$vars);
    $data = curl_exec($ch);
    curl_close($ch);
    if($data)
            return $data;
    else   
            return false;
}
登入後複製

驗證失敗,nginx的錯誤日誌中,會有如下資訊

2017/06/05 17:45:07 [crit] 16084#0: *27458991 SSL_do_handshake() failed (SSL: error:04067084:rsa routines:RSA_EAY_PUBLIC_DECRYPT:data too large for modulus e
rror:1408807A:SSL routines:ssl3_get_cert_verify:bad rsa signature) while SSL handshaking, client: 116.255.208.194, server: 0.0.0.0:443
登入後複製

6.3 php soap測試

首先需要建立client的pem格式證書,透過openssl指令也可以,但因為我們已經有了crt和key,所以手動合併也很簡單:

新建一個文件,把crt中-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----之間的base64內容(包括這兩條分割線)拷貝進去,然後把key中-----BEGIN RSA PRIVATE KEY-----和-----END RSA PRIVATE KEY-----之間的內容也複製進去,然後儲存為client.pem即可。

其實更省事的話可以如下命令,直接合併兩個文件:

cat /etc/pki/ca_linvo/client/client.crt /etc/pki/ca_linvo/client/client.key > /etc/pki/ca_linvo/client/client.pem
登入後複製

有了pem文件,下面可以使用php內建的SoapClient進行調用,建構子需要設定第二個參數:

$header = array(          
    'local_cert' => CLIENT_PEM, //client.pem文件路径  
    'passphrase' => CRT_PWD //client证书密码  
    );  
$client = new SoapClient(FILE_WSDL, $header); //FILE_WSDL为要访问的https地址
登入後複製

上一篇部落格最後說到local_cert設定成遠端路徑的話會報錯,好像是因為第一次獲取wsdl時並沒有使用client憑證的原因,需要將wsdl保持成本地文件進行呼叫;

但是這次測試卻沒問題,不用另存為本地文件,直接遠端取得即可。

本來認為是之前的憑證有問題,但使用之前的那套憑證依然可以,很是詭異~~~~~

以上是php如何實現https雙向認證的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板