场景介绍
xxx.com 为正常域名 配置 正常SSL
api.xxx.com 配置双向验证(自签名的CA基本没费用)
nginx 配置
在宝塔站点配置文件增加
如下内容
server
{
listen 443 ssl http2;
server_nameapi.xxx.com;
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot//www/api;
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
#HTTP_TO_HTTPS_START
if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}
#HTTP_TO_HTTPS_END
ssl_certificate /www/wwwroot/xxx/www/ca_cert/xxx.cn_chain.crt;
ssl_certificate_key /www/wwwroot/xxx/www/ca_cert/xxx.cn_key.key;
ssl_client_certificate /www/wwwroot/xxx/www/ca_cert/ca.crt;
ssl_verify_client on;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000";
#SSL-END
#ERROR-PAGE-START 错误页配置,可以注释、删除或修改
#error_page 400 /404.html;
#error_page 502 /502.html;
#客户端证书验证过程中发生错误
error_page 495 /495.html;
#客户没有出示所需的证书
error_page 496 /496.html;
#常规请求已发送到 HTTPS 端口
error_page 497 /497.html;
#ERROR-PAGE-END
#PHP-INFO-START PHP引用配置,可以注释或修改
include enable-php-73.conf;
#PHP-INFO-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log /dev/null;
access_log /dev/null;
}
location ~ .*\.(js|css)?$
{
expires 12h;
error_log /dev/null;
access_log /dev/null;
}
}
错误页(自定义)
<html>
<head><title>497 The SSL certificate error</title></head>
<body bgcolor="white">
<center><h1>497 </h1></center>
<center>常规请求已发送到 HTTPS 端口</center>
<hr><center>OenSSL</center>
</body>
</html>
<html>
<head><title>496 The SSL certificate error</title></head>
<body bgcolor="white">
<center><h1>496 </h1></center>
<center>客户没有出示所需的证书</center>
<hr><center>OenSSL</center>
</body>
</html>
<html>
<head><title>496 The SSL certificate error</title></head>
<body bgcolor="white">
<center><h1>496 </h1></center>
<center>客户没有出示所需的证书</center>
<hr><center>OenSSL</center>
</body>
</html>
php Curl 请求示例
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:私钥存放路径
'allow_self_signed'=>true, // 是否允许自签名证书
'cafile'=>'ca.crt', // 根证书
*/
function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
{
$ch = curl_init();
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_SSLCERT, './client.crt');
curl_setopt($ch, CURLOPT_SSLKEY, './client.key');
curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //允许302
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
$data = curl_exec($ch);
if (!curl_errno($ch)) {
$info = curl_getinfo($ch);
} else {
echo 'Curl error: ' . curl_error($ch);
}
curl_close($ch);
return $data;
}
浏览器配置
导入客户端证书
导入到证书 个人区域即可(导入成功个人区域可见,否则可能是证书或者密码错误)
完成后 打开浏览器访问网页即可 (刷新可能无效,如打开请先关闭)
如果是自签CA发布需要将CA证书导入到受信任的根证书颁发机构
此时浏览器将不再提示证书不受信任
shell 辅助脚本
生成CA证书以及CA目录
- create_ca_cert.sh 仅首次运行
- 生成后需要修改nginx配置文件
ssl_client_certificate ssl_certs/demoCA/cacert.pem;
ssl_crl ssl_certs/demoCA/private/ca.crl;
ssl_verify_client on;
#!/bin/bash -e
# 创建CA根证书
# 非交互式方式创建以下内容:
# 国家名(2个字母的代号)
C=CN
# 省
ST=Shannxi
# 市
L=Xian
# 公司名
O=My Company
# 组织或部门名
OU=技术部
# 服务器FQDN或颁发者名
CN=www.example.com
# 邮箱地址
emailAddress=admin@example.com
mkdir -p ./demoCA/{private,newcerts}
touch ./demoCA/index.txt
[ ! -f ./demoCA/seria ] && echo 01 > ./demoCA/serial
[ ! -f ./demoCA/crlnumber ] && echo 01 > ./demoCA/crlnumber
[ ! -f ./demoCA/cacert.pem ] && openssl req -utf8 -new -x509 -days 36500 -newkey rsa:2048 -nodes -keyout ./demoCA/private/cakey.pem -out ./demoCA/cacert.pem -subj "/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${emailAddress}"
[ ! -f ./demoCA/private/ca.crl ] && openssl ca -crldays 36500 -gencrl -out "./demoCA/private/ca.crl"
创建客户端证书,并用CA证书签证
- create_client_cert.sh
#!/bin/bash -e
show_help() {
echo "$0 [-h|-?|--help] [--ou ou] [--cn cn] [--email email]"
echo "-h|-?|--help 显示帮助"
echo "--ou 设置组织或部门名,如: 技术部"
echo "--cn 设置FQDN或所有者名,如: 冯宇"
echo "--email 设置FQDN或所有者邮件,如: fengyu@example.com"
}
while [[ $# -gt 0 ]]
do
case $1 in
-h|-\?|--help)
show_help
exit 0
;;
--ou)
OU="${2}"
shift
;;
--cn)
CN="${2}"
shift
;;
--email)
emailAddress="${2}"
shift
;;
--)
shift
break
;;
*)
echo -e "Error: $0 invalid option '$1'\nTry '$0 --help' for more information.\n" >&2
exit 1
;;
esac
shift
done
# 创建客户端证书
# 非交互式方式创建以下内容:
# 国家名(2个字母的代号)
C=CN
# 省
ST=Shannxi
# 市
L=Xian
# 公司名
O=My Company
# 组织或部门名
OU=${OU:-测试部门}
# 服务器FQDN或授予者名
CN=${CN:-demo}
# 邮箱地址
emailAddress=${emailAddress:-demo@example.com}
mkdir -p "${CN}"
[ ! -f "${CN}/${CN}.key" ] && openssl req -utf8 -nodes -newkey rsa:2048 -keyout "${CN}/${CN}.key" -new -days 36500 -out "${CN}/${CN}.csr" -subj "/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${emailAddress}"
[ ! -f "${CN}/${CN}.crt" ] && openssl ca -utf8 -batch -days 36500 -in "${CN}/${CN}.csr" -out "${CN}/${CN}.crt"
[ ! -f "${CN}/${CN}.p12" ] && openssl pkcs12 -export -clcerts -CApath ./demoCA/ -inkey "${CN}/${CN}.key" -in "${CN}/${CN}.crt" -certfile "./demoCA/cacert.pem" -passout pass: -out "${CN}/${CN}.p12"
吊销一个签证过的证书
- revoke_cert.sh
openssl ca -revoke "${1}/${1}.crt"
openssl ca -gencrl -out "./demoCA/private/ca.crl"