web-dev-qa-db-ja.com

中間CA(チェーン)によって署名されたNGinx SSL証明書認証

中間CAによって証明書が署名されているnginxでクライアント証明書認証を有効にしようとしています。自己署名ルートCAによって署名された証明書を使用すると、これを正常に機能させることができます。ただし、署名CAが中間CAの場合、これは機能しません。

私の単純なサーバーセクションは次のようになります。

server {
    listen       443;
    server_name  _;

    ssl                  on;
    ssl_certificate      cert.pem;
    ssl_certificate_key  cert.key;

    ssl_session_timeout  5m;

    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers   on;

    ssl_client_certificate ca.pem;
    ssl_verify_client on;
    ssl_verify_depth 1;

    location / {
        root   html;
        index  index.html index.htm;
    }
}

Ca.pemのコンテンツについては、onlyを使用して中間CAを試し、中間CA証明書とルートCA証明書を連結しました。つまり、次のようなものです。

cp intermediate.crt ca.pem
cat root.crt >> ca.pem

また、同じCAチェーンを使用する場合、証明書がopensslの観点から有効であることを検証しました。

openssl verify -CAfile /etc/nginx/ca.pem certs/client.crt 
certs/client.crt: OK

Ssl_verify_depthを明示的に1(上記のように)に設定し、さらに0(その数値の正確な意味がわからない)に設定することを試みましたが、それでも同じエラーが発生します。

中間CAのすべてのバリアントで発生するエラーは、「400 Bad Request」、より具体的には「SSL証明書エラー」です(正確にはどういう意味かわかりません)。

たぶんnginxは中間証明書の証明書チェーンをサポートしていないのでしょうか?どんな助けも大歓迎です!

37
Hans L

編集:この「問題」もありました。解決策と説明は本文の下部にあります

Nginxは中間証明書をサポートしていないようです。私の証明書は自己作成しました:(RootCAは自己署名され、IntrermediateCA1はRootCAによって署名されますなど)

RootCA -> IntermediateCA1 -> Client1 
RootCA -> IntermediateCA2 -> Client2

「Client1」証明書の所有者のみにサイトへのアクセスを許可するために、nginx「IntermediateCA1」で使用したい。

IntermediateCA1およびRootCAで「ssl_client_certificate」ファイルに配置し、「ssl_verify_depth 2」(またはそれ以上)を設定すると、クライアントは証明書を使用して両方のサイトにログインできますClient1およびClient2(Client1のみ)。同じ結果は、RootCAのみで「ssl_client_certificate」ファイルに保存した場合です。両方のクライアントがログインできます。

IntermediateCA1のみで「ssl_client_certificate」ファイルに入れ、「ssl_verify_depth 1」(または「2」以上-関係なく)を設定すると、ログインできない、エラー400が表示されます。デバッグモードではログが表示されます。

verify:0, error:20, depth:1, subject:"/C=PL/CN=IntermediateCA1/[email protected]",issuer: "/C=PL/CN=RootCA/[email protected]"
verify:0, error:27, depth:1, subject:"/C=PL/CN=IntermediateCA1/[email protected]",issuer: "/C=PL/CN=RootCA/[email protected]"
verify:1, error:27, depth:0, subject:"/C=PL/CN=Client1/[email protected]",issuer: "/C=PL/CN=IntermediateCA1/[email protected]"
(..)
client SSL certificate verify error: (27:certificate not trusted) while reading client request headers, (..)

これはバグだと思う。 Ubuntu、nginx 1.1.19および1.2.7-1〜dotdeb.1、openssl 1.0.1でテスト済み。 nginx 1.3には、クライアント証明書の使用に関するオプションがいくつかありますが、この問題の解決策は見当たりません。

現在、クライアント1と2を分離する唯一の方法は、自己署名された2つのRootCAを作成することですが、これは回避策にすぎません。

編集1:ここでこの問題を報告しました: http://trac.nginx.org/nginx/ticket/301 =

Edit 2 "*OK、それはバグではなく、機能です;)*

ここに応答があります: http://trac.nginx.org/nginx/ticket/301それは動作しています、あなたssl_client_i_dnが何であるかを確認する必要があります(発行者の代わりに、証明書のサブジェクト、または http://wiki.nginx.org/HttpSslModule#Built-in_variables を使用することもできます

これが証明書の検証の仕組みです。証明書は信頼できるルートまで検証する必要があります。チェーンを(中間ではなく)信頼されたルートに構築できない場合-検証は失敗します。ルートを信頼している場合-ルートによって直接または間接的に署名されたすべての証明書が正常に検証されます。

検証の深さの制限は、クライアント証明書を直接発行された証明書のみに制限する場合に使用できますが、これはDoS防止に関するものであり、明らかに、中間1のみに検証を制限するために使用することはできません(ただし、中間2ではありません)。

ここで必要なのは、検証結果に基づいたauthorizationレイヤーです-つまり、クライアントの証明書発行者が中間であるかどうかを確認することができます1。最も簡単な解決策は、発行者のDNが許可されたDNと一致しない場合、リクエストを拒否することです。このようなもの(完全に未テスト):

[自分で編集して、私の構成で正しく動作しています]

server {
    listen 443 ssl;

    ssl_certificate ...
    ssl_certificate_key ...

    ssl_client_certificate /path/to/ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    if ($ssl_client_i_dn != "/C=PL/CN=IntermediateCA1/[email protected]") {
        return 403;
    }
}
47
Jack

ssl_verify_depthディレクティブを増やしてみましたか? ドキュメント say:

(it) sets a verification depth in the client certificates chain.

ただし、検証の深さは1です。

Ssl_verify_depthを明示的に1(上記のように)に設定し、さらに0(その数値の正確な意味がわからない)に設定することを試みましたが、それでも同じエラーが発生します。

したがって、2または3を試してください。

PS: この問題が言及されているすべての箇所で、中間CA証明書とサーバー証明書を組み合わせるように指示されています。 1つのファイルに( @ vikas-nalwar推奨 でした)検証の順序(ただし、順序が重要かどうかはわかりません)および大まかに言えばssl_verify_depthを証明書の数に設定しますバンドル内。

11
Andrew D.

サーバー側でクライアント検証を有効にしたいと考えています。その場合、チェーンにクライアント証明書があることはわかりません。以下をまったく同じ順序で試してください。 certchain.pemを使用します。

  cat client.crt > certchain.pem
  cat intermediate.crt >> certchain.pem
  cat root.crt >> certchain.pem
2
Drona


ssl_client_certificate /etc/nginx/ssl/ca-bundle-client.crt; ssl_verify_client optional_no_ca; ssl_verify_depth 2;

optional_no_caのある2行目は重要な部分です

2
Tal

私はnginx/1.13.2、つまり.

  • 2つの中間CAに署名した1つのルートCAがあります
  • 両方の中間体がそれぞれクライアントに署名しました
  • cat client-intermediate1.crt ca-client.crt > ca.chained1.crtcat client-intermediate2.crt ca-client.crt > ca.chained2.crtcat ca.chained1.crt ca.chained2.crt > ca.multiple.intermediate.crtのような証明書を連結します

  • ca.chained1.crtをssl_client_certificateとしてのみ配置すると、ca1.crtのみが接続できます。ca.chained2.crt/ client2.crtの場合も同様です。

  • ca.multiple.intermediate.crtを使用すると、両方のクライアントが接続できます

中間物を取り消すには、ca.multiple.intermediate.crtから証明書チェーンを削除するだけです

関連する構成は次のとおりです。また、高いセキュリティ設定があります

# minimum settings for ssl client auth 
ssl_client_certificate /etc/ssl/ca.multiple.intermediate.crt;
ssl_verify_client on;
ssl_verify_depth 2;

# ssl high security settings (as of writing this post)
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

証明書CNを解析してバックエンドに渡す場合は、server {..ブロックの外側にこれを追加します

# parse out CN
map $ssl_client_s_dn $ssl_client_s_dn_cn {
    default "should_not_happen";
    ~CN=(?<CN>[^,]+) $CN;
}

ブロック内で使用できます

# add headers for backend containing SSL DN/CN
add_header X-SSL-client-s-dn $ssl_client_s_dn;
add_header X-SSL-client-s-dn_cn $ssl_client_s_dn_cn;
1
pHiL

別の簡単な方法は、証明書(ドメイン証明書を含む)を単一のファイルに連結し、サーバーとnginx confファイルで使用することです

cat www.example.com.crt bundle.crt> www.example.com.chained.crt

最初にサーバー証明書を使用し、次にCAサーバー証明書のみを使用することを常に忘れないでください

詳細については http://nginx.org/en/docs/http/configuring_https_servers.html#chains をご覧ください。

0
Naveen