Nginxでクライアント認証を行う

サーバー認証

まずはじめに、サーバー認証を行いHTTPSで通信できるようにする。

サーバー認証に使用するファイルの保存場所を作成する

1
2
mkdir /etc/nginx/server_certificates
cd /etc/nginx/server_certificates

CA(Certification Authority)鍵を作成する

1
2
3
4
5
6
7
$ openssl genrsa -des3 -out server.key 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
................................++++
..++++
e is 65537 (0x010001)
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:

CA(Certification Authority)証明書を作成する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ openssl req -new -x509 -days 365 -key server.key -out server.crt
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Shibuya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example, Inc.
Organizational Unit Name (eg, section) []:Infra
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

パスワードファイルを作成する

CA鍵のパスワードをNginxに渡すためパスワードファイル/etc/nginx/server_certificates/ssl_pass_file.txtを作成する。

1
p4ssw0rd

NginXの設定

設定ファイル/etc/nginx/conf.d/default.confに以下を追記する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server {
listen 443 ssl default_server;

## HTTPS化
ssl_certificate "/etc/nginx/server_certificates/server.crt";
ssl_certificate_key "/etc/nginx/server_certificates/server.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP;
ssl_prefer_server_ciphers on;
# パスワードファイルを読み込む
ssl_password_file /etc/nginx/server_certificates/ssl_pass_file.txt;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

クライアント認証

クライアント認証に使用するファイルの保存場所を作成する

1
2
mkdir /etc/nginx/client_certificates
cd /etc/nginx/client_certificates

CA(Certification Authority)鍵を作成する

1
2
3
4
5
6
7
$ openssl genrsa -des3 -out ca.key 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
.........................................................................++++
......++++
e is 65537 (0x010001)
Enter pass phrase for ca.key:
Verifying - Enter pass phrase for ca.key:

CA(Certification Authority)証明書を作成する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ openssl req -new -x509 -days 365 -key ca.key -out ca.crt
Enter pass phrase for ca.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Shibuya-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example, Inc.
Organizational Unit Name (eg, section) []:Development section
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

クライアント証明書を作成する

1
2
3
4
5
6
7
$ openssl genrsa -des3 -out user.key 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
......................................................................................................................++++
.........................................................................................................................................................................................................++++
e is 65537 (0x010001)
Enter pass phrase for user.key:
Verifying - Enter pass phrase for user.key:

クライアント証明書に署名する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ openssl req -new -key user.key -out user.csr
Enter pass phrase for user.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Shibuya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example, Inc.
Organizational Unit Name (eg, section) []:Customer Support
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

CSR(Certificate Signing Request)に署名する

1
2
3
4
5
$ openssl x509 -req -days 365 -in user.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out user.crt
Signature ok
subject=C = JP, ST = Tokyo, L = Shibuya, O = "Example, Inc.", OU = Customer Support
Getting CA Private Key
Enter pass phrase for ca.key:

PKCS(Public Key Cryptography Standards)を作成する

1
2
3
4
$ openssl pkcs12 -export -out user.pfx -inkey user.key -in user.crt -certfile ca.crt
Enter pass phrase for user.key:
Enter Export Password:
Verifying - Enter Export Password:

NginXの設定

設定ファイル/etc/nginx/conf.d/default.confに以下を追記する

1
2
ssl_client_certificate "/etc/nginx/client_certificates/ca.crt";
ssl_verify_client on;

最終的な設定ファイルは以下のようになる

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
server {
listen 80 default_server;
listen [::]:80 default_server;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server {
listen 443 ssl default_server;

## HTTPS化
ssl_certificate "/etc/nginx/server_certificates/server.crt";
ssl_certificate_key "/etc/nginx/server_certificates/server.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP;
ssl_prefer_server_ciphers on;
#
ssl_password_file /etc/nginx/server_certificates/ssl_pass_file.txt;

## クライアント認証
ssl_client_certificate "/etc/nginx/client_certificates/ca.crt";
ssl_verify_client on;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}

確認

クライアント証明書をインポートしていない場合

オレオレ認証なので–insecureオプションを付与してcurlコマンドを実行する。

1
2
3
4
5
6
7
8
9
$ curl --insecure https://localhost/
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.19.6</center>
</body>
</html>

サーバーログにはHTTPステータスコード400のエラーが記録される。

1
nginx01  | 192.168.64.1 - - [07/Mar/2022:02:57:45 +0000] "GET / HTTP/1.1" 400 237 "-" "curl/7.74.0" "-"

クライアント証明書をインポートした場合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ curl --insecure --key ./client_certificates/user.key --cert ./client_certificates/user.crt https
:/localhost/
Enter PEM pass phrase:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Certified</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>

<![endif]-->
<style>
article, aside, dialog, figure, footer, header,
hgroup, menu, nav, section { display: block; }
</style>
</head>
<body>
<p>Hello World</p>
</body>
</html>