nginx配置单向认证与双向认证

nginx配置单向认证与双向认证

1. 生成所需证书

将以下下代码保存为create_cert.sh并执行, 可以按需要修改 PROJECT_NAME 和 CN 等属性。

# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
# * Neither the name of the axTLS project nor the names of its
#   contributors may be used to endorse or promote products derived
#   from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

#
# Generate the certificates and keys for testing.
#


PROJECT_NAME="TLS Nginx Test"

# Generate the openssl configuration files.
cat > ca_cert.conf << EOF  
[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no

[ req_distinguished_name ]
 O                      = $PROJECT_NAME Dodgy Certificate Authority
EOF

cat > server_cert.conf << EOF  
[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no

[ req_distinguished_name ]
 O                      = $PROJECT_NAME
 CN                     = 10.42.166.214
EOF

cat > client_cert.conf << EOF  
[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no

[ req_distinguished_name ]
 O                      = $PROJECT_NAME Device Certificate
 CN                     = 10.42.166.214
EOF

mkdir ca
mkdir server
mkdir client
mkdir certDER

# private key generation
openssl genrsa -out ca.key 1024
openssl genrsa -out server.key 1024
openssl genrsa -out client.key 1024

# cert requests
openssl req -out ca.req -key ca.key -new \
            -config ./ca_cert.conf
openssl req -out server.req -key server.key -new \
            -config ./server_cert.conf 
openssl req -out client.req -key client.key -new \
            -config ./client_cert.conf 

# generate the actual certs.
openssl x509 -req -in ca.req -out ca.crt \
            -sha1 -days 5000 -signkey ca.key
openssl x509 -req -in server.req -out server.crt \
            -sha1 -CAcreateserial -days 5000 \
            -CA ca.crt -CAkey ca.key
openssl x509 -req -in client.req -out client.crt \
            -sha1 -CAcreateserial -days 5000 \
            -CA ca.crt -CAkey ca.key

openssl x509 -in ca.crt -outform DER -out ca.der
openssl x509 -in server.crt -outform DER -out server.der
openssl x509 -in client.crt -outform DER -out client.der

mv ca.crt ca.key ca/
mv server.crt server.key server/
mv client.crt client.key client/

mv ca.der server.der client.der certDER/

rm *.conf
rm *.req
rm *.srl 

生成证书后的目录结构为:

create_cert.sh
ca/
  ca.crt
  ca.key
certDER/
  ca.der
  client.der
  server.der
client/
  client.crt
  client.key
server/
  server.crt
  server.key

2. nginx单向认证配置

其中97端口是无认证,98端口是单向认证的配置

server {
    listen       97;

    location /hello {
        default_type 'application/json';
        return 200 '{"status":"success","text":"No authentication"}';
    }
}

server {
    listen       98;

    ssl on;
	ssl_certificate      /opt/cert/server/server.crt;
	ssl_certificate_key  /opt/cert/server/server.key;

	ssl_session_timeout 5m;
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
	ssl_prefer_server_ciphers on;

    location /hello {
        default_type 'application/json';
        return 200 '{"status":"success","text":"One-way authentication"}';
    }
}

3. nginx双向认证配置

server {
    listen       99;

    ssl on;
    ssl_certificate      /opt/cert/server/server.crt;
	ssl_certificate_key  /opt/cert/server/server.key;

    ssl_client_certificate /opt/cert/ca/ca.crt;
    ssl_verify_client on;

    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers on;

    location /hello {
        default_type 'application/json';
        return 200 '{"status":"success","text":"Two-way authentication"}';
    }
}

4. python client连接测试

import urllib.request
import ssl
import json

def no_authentication(url):
    headers = {
        'Content-Type': 'application/json',
    }

    request = urllib.request.Request(url, headers=headers)
    response = urllib.request.urlopen(request)
    print (json.loads(response.read().decode('utf8')))

def one_way_authentication(url, cafile):
    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    context.load_verify_locations(cafile)

    headers = {
        'Content-Type': 'application/json',
    }

    request = urllib.request.Request(url, headers=headers)
    response = urllib.request.urlopen(request, context=context)
    print (json.loads(response.read().decode('utf8')))

def two_way_authentication(url, cafile, certfile, keyfile):
    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    context.load_cert_chain(certfile, keyfile)
    context.load_verify_locations(cafile)

    headers = {
        'Content-Type': 'application/json',
    }

    request = urllib.request.Request(url, headers=headers)
    response = urllib.request.urlopen(request, context=context)
    print (json.loads(response.read().decode('utf8')))

# 证书文件
cafile   = 'ca.crt'
certfile = 'client.crt'
keyfile  = 'client.key'

# 无认证
no_authentication("http://127.0.0.1:97/hello")

# 单向认证
one_way_authentication("https://127.0.0.1:98/hello", cafile)

# 双向认证
two_way_authentication("https://127.0.0.1:99/hello", cafile, certfile, keyfile)

执行后输入结果为:

{'status': 'success', 'text': 'No authentication'}
{'status': 'success', 'text': 'One-way authentication'}
{'status': 'success', 'text': 'Two-way authentication'}

参考资料

  1. python关于SSL/TLS认证的实现, https://blog.csdn.net/vip97yigang/article/details/84721027
  2. Nginx单向认证和双向认证安装配置, https://blog.csdn.net/liucy007/article/details/86719902
Table of Contents