web-dev-qa-db-fra.com

Authentification of certificate SSL NGinx

J'essaie d'activer l'authentification par certificat client dans nginx où les certificats ont été signés par une autorité de certification intermédiaire. Je parviens à faire en sorte que cela fonctionne correctement avec un certificat signé par une autorité de certification racine auto-signée; Toutefois, cela ne fonctionne pas lorsque l'autorité de certification signataire est une autorité de certification intermédiaire.

Ma section serveur simple ressemble à ceci:

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;
    }
}

Pour le contenu de ca.pem, j'ai essayé d'utiliser uniquement l'autorité de certification intermédiaire et de concaténer également le certificat de l'autorité de certification intermédiaire et le certificat de l'autorité de certification racine, c'est-à-dire quelque chose comme:

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

J'ai également validé que le certificat est valide du point de vue de openssl lors de l'utilisation de cette même chaîne d'autorités de certification:

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

J'ai expérimenté avec la définition explicite de ssl_verify_depth sur 1 (comme ci-dessus) et même sur 0 (vous ne savez pas exactement ce que ce nombre signifie), mais vous obtenez toujours la même erreur.

L'erreur que j'obtiens dans toutes les variantes de l'autorité de certification intermed est "400 Bad Request" et plus précisément "l'erreur de certificat SSL" (vous ne savez pas exactement ce que cela signifie).

Peut-être que nginx ne prend tout simplement pas en charge les chaînes de cert pour les certificats intermédiaires? Toute aide grandement appréciée!

31
Hans L

Edit: J'avais aussi ce "problème", la solution et l'explication se trouvent au bas du texte

Il semblait que nginx ne supporte pas les certificats intermédiaires. Mes certificats auto-créés: (RootCA est auto-signé, IntrermediateCA1 est signé par RootCA, etc.)

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

Je souhaite utiliser nginx "IntermediateCA1" pour autoriser l'accès au site uniquement au propriétaire du certificat "Client1".

Lorsque je mets dans le fichier "ssl_client_certificate" avec IntermediateCA1 et RootCA et que je définis "ssl_verify_depth 2" (ou plus), les clients peuvent se connecter au site à l'aide du certificat Client1 et de Client2 (uniquement Client1). Le résultat est identique lorsque je mets dans le fichier "ssl_client_certificate" avec only RootCA - les deux clients peuvent se connecter.

Lorsque je mets dans le fichier "ssl_client_certificate" avec only IntermediateCA1, et que je définis "ssl_verify_depth 1" (ou "2" ou plus - peu importe), il est impossible de se connecter, l'erreur 400 s'affiche. Je vois les journaux:

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, (..)

Je pense que c'est un bug. Testé sur Ubuntu, nginx 1.1.19 et 1.2.7-1 ~ dotdeb.1, openssl 1.0.1 . Je vois que nginx 1.3 ne dispose que de peu d’autres options pour utiliser les certificats clients, mais je ne verrais pas de solution à ce problème. .

Actuellement, le seul moyen de séparer les clients 1 et 2 est de créer deux RootCAs autosignés, mais il ne s'agit que d'une solution de contournement.

Edit 1: J'ai signalé ce problème ici: http://trac.nginx.org/nginx/ticket/301

Edit 2 " * D'accord, ce n'est pas un bug, c'est une fonctionnalité;)} * *

Je reçois la réponse ici: http://trac.nginx.org/nginx/ticket/301Cela fonctionne, vous devez seulement vérifier quel est votre ssl_client_i_dn (. Au lieu d'émetteur, vous pouvez utilisez également le sujet du certificat, ou ce que vous voulez de http://wiki.nginx.org/HttpSslModule#Built-in_variables

Voici comment fonctionne la vérification du certificat: le certificat doit être vérifié jusqu'à une racine de confiance. Si la chaîne ne peut pas être construite sur un fiable root (not intermediary) - la vérification échoue. Si vous faites confiance à root - all les certificats signés par elle, directement ou indirectement, seront vérifié avec succès. 

La profondeur de vérification limite peut être utilisée si vous vouloir limiter les certificats clients à des certificats émis directement seulement, mais il est plus question de prévention des dénis de service et, évidemment, cela ne peut pas être utilisé pour limiter verificate à intermédiaire1 uniquement (mais pas intermédiaire2). 

Ce que vous voulez ici, c'est une couche de {autorisation} _ sur le résultat de la vérification - c’est-à-dire que vous voudrez peut-être vérifier le .__ de ce client. émetteur de certificat est intermédiaire1. La solution la plus simple serait de rejeter les demandes si le nom distinctif de l'émetteur ne correspond pas à celui autorisé, par exemple . quelque chose comme ceci (complètement non testé):

[Éditer par moi, cela fonctionne correctement dans ma configuration]

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;
    }
}
44
Jack

Avez-vous essayé d'augmenter la directive ssl_verify_depth? Docs dire:

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

Mais votre profondeur de vérification est 1. Vous dites:

J'ai expérimenté avec la définition explicite de ssl_verify_depth sur 1 (comme ci-dessus) et même sur 0 (vous ne savez pas exactement ce que ce nombre signifie), mais vous obtenez toujours la même erreur.

Alors, essayez 2 ou 3 ..

PS: Partout où je trouve ce problème mentionné, il est dit de combiner des certificats d’autorité de certification intermédiaires avec votre certificat de serveur. dans un fichier (comme le suggère @ vikas-nalwar et vous l’avez fait) par ordre de vérification (mais je ne suis pas sûr que l’ordre compte) et en gros, définissez ssl_verify_depth sur le nombre de certificats contenus dans le paquet.

11
Andrew D.

Je pense que vous souhaitez activer la validation du client côté serveur. Si tel est le cas, je ne vois pas que vous avez votre certificat client dans la chaîne. Essayez ce qui suit dans le même ordre. Utilisez le certchain.pem. 

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

Je dois dire que cela fonctionne très bien pour moi avec nginx/1.13.2, c.-à-d.

  • J'ai une autorité de certification racine ayant signé deux autorités de certification intermédiaires.
  • les deux intermédiaires ont chacun signé un client
  • Je concatte les certs comme cat client-intermediate1.crt ca-client.crt > ca.chained1.crt et cat client-intermediate2.crt ca-client.crt > ca.chained2.crt et cat ca.chained1.crt ca.chained2.crt > ca.multiple.intermediate.crt

  • si je mets seulement ca.chained1.crt comme ssl_client_certificate, alors seul client1.crt peut se connecter, de même pour ca.chained2.crt/client2.crt

  • quand j'utilise ca.multiple.intermediate.crt alors les deux clients peuvent se connecter

pour révoquer un intermédiaire, supprimez simplement la chaîne de certificats de ca.multiple.intermediate.crt

voici la config pertinente. sa a également des paramètres de haute sécurité

# 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;

si vous voulez analyser le CN de certs et le transmettre au serveur principal, ajoutez ceci en dehors du bloc server {..

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

et à l'intérieur du bloc, vous pouvez l'utiliser alors

# 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

un autre moyen simple consiste à concaténer des certificats (y compris un certificat de domaine) dans un fichier unique et à l'utiliser sur vos serveurs et dans le fichier de configuration nginx

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

N'oubliez jamais d'utiliser d'abord le certificat de serveur, puis uniquement les certificats de serveur d'autorité de certification.

Vous pouvez en savoir plus sur http://nginx.org/en/docs/http/configuring_https_servers.html#chains

0
Naveen