一般狀況下,在部署https的時候,是基於ssl單向認證的,也就是說只要客戶端認證服務器,而服務器不須要認證客戶端。php
但在一些安全性較高的場景,如銀行,金融等領域,一般會要求進行客戶端認證。從而實現ssl的雙向認證。nginx
因爲nginx的ssl_client_certificate參數只能指定一個客戶端公鑰,若是增長一個客戶端進行通訊就要從新配一個server。
n:1的模式是經過CA的級聯證書模式實現的,首先本身生成一套CA根級證書,再借助其生成二級證書做爲client證書。
此時client私鑰簽名不只能夠經過對應的client公鑰驗證,還可經過根證書的公鑰進行驗證。windows
看到這裏應該豁然開朗了吧,下面簡單介紹下具體怎麼操做:瀏覽器
通常狀況下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
修改openssl配置服務器
vi /etc/pki/tls/openssl.cnf
找到這句註釋掉,替換爲下面那句curl
#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
保存退出工具
生成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工具貌似對它有要求。post
生成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
說明:
一、這裏生成的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
二、-days 參數可根據須要設置證書的有效期,例如默認365天
生成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
說明:
一、這裏就必須使用級聯證書,而且能夠重複該步驟,建立多套client證書
二、生成crt時可能會遇到以下報錯:
openssl TXT_DB error number 2 failed to update database
可參照這裏進行操做。
我使用的是方法一,即將index.txt.attr中unique_subject = no
這裏只列出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
因爲是雙向認證,直接經過瀏覽器訪問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機構頒佈(一般很貴哦~),忽略它既可。
這裏只列出關鍵的須要設置的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
首先須要構建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保持成本地文件進行調用;
可是此次測試卻沒問題,不用另存爲本地文件,直接遠程獲取便可。
原本認爲是以前的證書有問題,可是使用以前的那套證書依然能夠,非常詭異~~~~~