Nginx证书配置实战
创建证书
生成CA证书
- 生成CA证书
openssl req -new -x509 -keyout ca.key -out ca.crt -sha256 -days 365 -passout pass:${PASSWD} -subj "/C=cn/ST=beijing/L=beijing/O=aspire/OU=aspire/CN=ca.it"
- 将CA证书加到客户端信任库,用来客户端认证服务器身份时,通过CA证书校验服务器证书的有效性
keytool -keystore client.truststore -alias caroot -import -file ca.crt -storepass ${PASSWD}
- 将CA证书加到服务器端信任库,用来服务器端认证客户端身份时,通过CA证书校验客户端证书的有效性
keytool -keystore server.truststore -alias caroot -import -file ca.crt -storepass ${PASSWD}
生成服务器证书
- 生成公私钥keypair
openssl genrsa -passout pass:${PASSWD} -out server.key 2048
2. 生成待签名文件
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=SHANXI/L=XI'AN/O=TW/OU=IT/CN=${HOSTNAME}"
3. 使用CA证书签名CSR文件,生成server证书
Create X509 V3 certificate extension config file
cat>server.ext<<EOF
[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = test.it
IP.1 = 127.0.0.1
EOF
签名生成server证书
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -passin pass:${PASSWD} -CAcreateserial -out server.crt -sha256 -extensions v3_req -extfile server.ext
4. 将openssl生成的证书转换为pkcs12格式
# 转换为pkcs12密钥库
openssl pkcs12 -export -passout pass:${PASSWD} -in server.crt -inkey server.key -out server.p12 -name server -chain -CAfile ca.crt -caname rootca
将pkcs12证书导入到jks类型的server.keystore中,如果在application.yml中指定的类型为pkcs12,则无需此步骤,直接指定pkcs12类型的证书即可
keytool -importkeystore \
-deststorepass ${PASSWD} -destkeypass ${PASSWD} -destkeystore server.keystore \
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass ${PASSWD} \
-alias server
生成客户端证书
- 生成公私钥keypair
openssl genrsa -des3 -passout pass:${PASSWD} -out client.key 4096
2. 生成待签名文件
openssl req -new -key client.key -passin pass:${PASSWD} -out client.csr -subj "/C=CN/ST=SHANXI/L=XI'AN/O=TW/OU=IT/CN=client"
3. 使用CA证书签名CSR文件,生成server证书
openssl x509 -req -days 365 -in client.csr -passin pass:${PASSWD} -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt -sha256
4. 将openssl生成的证书转换为pkcs12格式
openssl pkcs12 -export -passout pass:${PASSWD} -in client.crt -inkey client.key -passin pass:${PASSWD} -out client.p12 -name client -chain -CAfile ca.crt -caname caroot
5. 将pkcs12证书导入到jks类型的client.keystore中,根据客户端需要的keystore类型,选择是否需要此步骤,一般postman是需要此步骤的
keytool -importkeystore \
-deststorepass ${PASSWD} -destkeypass ${PASSWD} -destkeystore client.keystore \
-srckeystore client.p12 -srcstoretype PKCS12 -srcstorepass ${PASSWD} \
-alias client
脚本
#!/bin/bash
#define
PASSWD=localhost
MAIN_HOSTNAME=ifeikuai.com
HOSTNAME=beta.ifeikuai.com
#precondition
rm -f ca*
rm -f client.*
rm -f server.*
echo "create "
echo "######### ca"
openssl req -new -x509 -keyout ca.key -out ca.crt -sha256 -days 365 -passout pass:${PASSWD} -subj "/C=cn/ST=beijing/L=beijing/O=aspire/OU=aspire/CN=$MAIN_HOSTNAME"
openssl x509 -in ca.crt -out ca.pem -outform PEM
keytool -keystore client.truststore -alias caroot -import -file ca.crt -storepass ${PASSWD} -noprompt
keytool -keystore server.truststore -alias caroot -import -file ca.crt -storepass ${PASSWD} -noprompt
echo "######### server"
openssl genrsa -passout pass:${PASSWD} -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=SHANXI/L=XI'AN/O=TW/OU=IT/CN=${HOSTNAME}"
cat>server.ext<<EOF
[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = *.${MAIN_HOSTNAME}
IP.1 = 127.0.0.1
EOF
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -passin pass:${PASSWD} -CAcreateserial -out server.crt -sha256 -extensions v3_req -extfile server.ext
openssl x509 -in server.crt -out server.pem -outform PEM
# 转换为pkcs12密钥库
openssl pkcs12 -export -passout pass:${PASSWD} -in server.crt -inkey server.key -out server.p12 -name server -chain -CAfile ca.crt -caname rootca
# 导入到server.keystore中
keytool -importkeystore \
-deststorepass ${PASSWD} -destkeypass ${PASSWD} -destkeystore server.keystore \
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass ${PASSWD} \
-alias server
echo "######### client"
openssl genrsa -des3 -passout pass:${PASSWD} -out client.key 4096
openssl req -new -key client.key -passin pass:${PASSWD} -out client.csr -subj "/C=CN/ST=SHANXI/L=XI'AN/O=TW/OU=IT/CN=${HOSTNAME}"
openssl x509 -req -days 365 -in client.csr -passin pass:${PASSWD} -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt -sha256
openssl x509 -in client.crt -out client.pem -outform PEM
openssl pkcs12 -export -passout pass:${PASSWD} -in client.pem -inkey client.key -passin pass:${PASSWD} -out client.p12 -name client -chain -CAfile ca.crt -caname caroot
# 导入到client.keystore中
keytool -importkeystore \
-deststorepass ${PASSWD} -destkeypass ${PASSWD} -destkeystore client.keystore \
-srckeystore client.p12 -srcstoretype PKCS12 -srcstorepass ${PASSWD} \
-alias client
单向认证配置(客户端校验服务器端证书)
服务器端
nginx.conf配置
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
# parse CN in certificate
map $ssl_client_s_dn $ssl_client_s_dn_cn {
default "";
~,CN=(?<CN>[^,]+) $CN;
}
server {
listen 443 ssl;
server_name test.it;
# This is the server SSL certificate
ssl_certificate certs/server.pem;
# This is the server certificate key
ssl_certificate_key certs/server.key;
# Enables mutual TLS/two way SSL to verify the client
#ssl_verify_client on;
ssl_verify_client optional;
# Number of intermediate certificates to verify. Good explanation of
# certificate chaining can be found at
# https://cheapsslsecurity.com/p/what-is-ssl-certificate-chain/
ssl_verify_depth 2;
# Important:
# This is the CA cert against which the client/user will be validated
# In our case since the Server and the Client certificate is
# generated from the same CA, we use the ca.crt
# But in actual production, the Client certificate might be
# created from a different CA
# ssl_client_certificate certs/ca.pem;
error_log /Users/10100100010b/Downloads/error.log debug;
location / {
index index.html index.htm;
}
}
include servers/*;
}
如果是单项认证,ssl_verify_client配置需要设置为optional,双向认证设置ssl_verify_client为on,表示开启认证客户端。
map $ssl_client_s_dn $ssl_client_s_dn_cn {
default "";
~,CN=(?<CN>[^,]+) $CN;
}
上面这段配置表示通过nginx ssl模块获取到ssl_client_s_dn变量,该变量存储SDN信息,通过正则匹配到CN信息,将CN信息赋值给ssl_client_s_dn_cn变量,通过该变量我们可以做进一步的校验,比如说某client的CN那么不是预期的CN,我们可以抛出一个401错误。
客户端配置
使用浏览器或者postman做为客户端访问server
-
客户端需要安装CA证书并且信任证书
双向认证配置
服务器端
双向认证设置ssl_verify_client为on,表示开启认证客户端,而且需要通过ssl_client_certificate配置ca证书路径,当浏览器访问时会通过ca证书加载安装的客户端证书
客户端认证增加如下脚本
if ($ssl_client_verify != "SUCCESS") {
return 403;
}
# validate CN in client certificate match
if ($ssl_client_s_dn_cn !~ "test.it") {
return 401;
}
客户端配置
使用浏览器做为客户端访问server
-
客户端需要安装CA证书并且信任
-
安装client.pfx证书,安装格式一定要是包含私钥格式的证书:pkcs12/pfx,安装时会提示输入证书密码
在浏览器中访问test.it,会自动提示选择证书,浏览器之所以能找到client证书,是因为服务器端通过信任的ca证书找到颁发的子证书
使用postman做为客户端访问server
打开postman偏好设置,打开Certificates标签页,导入相应的证书
- hosts文件中配置域名映射
# 外网ip对应的域名
127.0.0.1 test.it