介绍了SSL双向认证的⼀些基本问题,以及使⽤Nginx+PHP基于它搭建https的Webservice。之前的⽅式只是实现1:1的模式,昨天同事继续实现了n:1的模式,这⾥我再整理记录下。
由于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_linvocd /etc/pki/ca_linvo
mkdir root server client newcertsecho 01 > serialecho 01 > crlnumbertouch index.txt
1.2 openssl配置准备
修改openssl配置vi /etc/pki/tls/openssl.cnf
找到这句注释掉,替换为下⾯那句#default_ca = CA_defaultdefault_ca = CA_linvo
把整个部分拷贝⼀份,改成上⾯的名字[ CA_linvo ]修改⾥⾯的如下参数:dir = /etc/pki/ca_linvocertificate = $dir/root/ca.crtprivate_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;#根级证书公钥,⽤于验证各个⼆级clientssl_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参数:
1. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书,不是CA机构颁布的也没关系 2. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); // 检查证书中是否设置域名,如果不想验证也可设为0 3. curl_setopt($ch, CURLOPT_VERBOSE, '1'); //debug模式,⽅便出错调试
4. curl_setopt($ch, CURLOPT_SSLCERT, CLIENT_CRT); //client.crt⽂件路径,这⾥我⽤常量代替 5. curl_setopt($ch, CURLOPT_SSLCERTPASSWD, CRT_PWD); //client证书密码 6. curl_setopt($ch, CURLOPT_SSLKEY, CLIENT_KEY); //client.key⽂件路径 如果出现⽩板页没有返回信息,⼀般是证书或密码没有设置正确的问题,请检查。
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进⾏调⽤,构造函数需要设置第⼆个参数:1. $header = array(
2. 'local_cert' => CLIENT_PEM, //client.pem⽂件路径 3. 'passphrase' => CRT_PWD //client证书密码 4. );
5. $client = new SoapClient(FILE_WSDL, $header); //FILE_WSDL为要访问的https地址
上⼀篇博客⾥最后说到local_cert设置成远程路径的话会报错,好像是因为第⼀次获取wsdl时并没有使⽤client证书的原因,需要将wsdl保持成本地⽂件进⾏调⽤;
但是这次测试却没问题,不⽤另存为本地⽂件,直接远程获取即可。
本来认为是之前的证书有问题,但是使⽤之前的那套证书依然可以,很是诡异~~~~~
因篇幅问题不能全部显示,请点此查看更多更全内容