一般状况下,在部署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保持成本地文件进行调用;
可是此次测试却没问题,不用另存为本地文件,直接远程获取便可。
原本认为是以前的证书有问题,可是使用以前的那套证书依然能够,非常诡异~~~~~