Nginx证书配置实战

572 阅读3分钟

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}

生成服务器证书

  1. 生成公私钥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

生成客户端证书

  1. 生成公私钥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_clienton,表示开启认证客户端。

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证书并且信任证书

    image-20220711150754730

双向认证配置

服务器端

双向认证设置ssl_verify_clienton,表示开启认证客户端,而且需要通过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证书并且信任

    image-20220711150754730

  • 安装client.pfx证书,安装格式一定要是包含私钥格式的证书:pkcs12/pfx,安装时会提示输入证书密码

image-20220712182027074

在浏览器中访问test.it,会自动提示选择证书,浏览器之所以能找到client证书,是因为服务器端通过信任的ca证书找到颁发的子证书

image-20220712182210293

使用postman做为客户端访问server

打开postman偏好设置,打开Certificates标签页,导入相应的证书

image-20220711162221319

image-20220711162253608

  • hosts文件中配置域名映射
# 外网ip对应的域名
127.0.0.1 test.it