首页 文章

由中间CA(链)签名的NGinx SSL证书认证

提问于
浏览
31

我试图在nginx中启用客户端证书身份验证,其中证书已由中间CA签名 . 使用由自签名根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的内容,我尝试过只使用中间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只是不支持中间证书的证书链?任何帮助非常感谢!

6 回答

  • 11

    Edit: I had also this "problem", solution and explanation is at the bottom of the text .

    似乎nginx不支持中间证书 . 我的证书是自己创建的:(RootCA是自签名的,IntrermediateCA1是由RootCA签名的,等等)

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

    我想在nginx“IntermediateCA1”中使用,只允许访问“Client1”证书的所有者 .

    当我使用 IntermediateCA1 and RootCA 设置"ssl_client_certificate"文件并设置"ssl_verify_depth 2"(或更多)时,客户端可以使用证书 Client1 and Client2 (仅应该是Client1)登录到站点 . 同样的结果是当我将"ssl_client_certificate"文件与 only RootCA 放在一起时 - 两个客户端都可以登录 .

    当我使用 only IntermediateCA1 设置"ssl_client_certificate"文件,并设置"ssl_verify_depth 1"(或"2"或更多 - 无论如何)时,登录是不可能的,我得到错误400.在调试模式下,我看到日志:

    verify:0, error:20, depth:1, subject:"/C=PL/CN=IntermediateCA1/emailAddress=cert@asdf.com",issuer: "/C=PL/CN=RootCA/emailAddress=cert@asdf.com"
    verify:0, error:27, depth:1, subject:"/C=PL/CN=IntermediateCA1/emailAddress=cert@asdf.com",issuer: "/C=PL/CN=RootCA/emailAddress=cert@asdf.com"
    verify:1, error:27, depth:0, subject:"/C=PL/CN=Client1/emailAddress=cert@asdf.com",issuer: "/C=PL/CN=IntermediateCA1/emailAddress=cert@asdf.com"
    (..)
    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的唯一方法是创建两个自签名的RootCAs,但这只是解决方法 .

    Edit 1: 我在这里报告了这个问题:http://trac.nginx.org/nginx/ticket/301

    Edit 2" 好的,这不是错误,它是功能;)

    我在这里得到回复:http://trac.nginx.org/nginx/ticket/301 It is working ,你必须只检查你的ssl_client_i_dn是什么( . 而不是发行人你也可以使用证书的主题,或你想要的http://wiki.nginx.org/HttpSslModule#Built-in_variables

    这是证书验证的工作原理:证书必须经过验证才能到达受信任的根目录 . 如果无法将链构建到受信任的根(非中间),则验证失败 . 如果您信任root,则将成功验证由其直接或间接签署的所有证书 . 如果要将客户端证书限制为仅直接颁发的证书,则可以使用限制验证深度,但它更多的是关于DoS预防,显然它不能用于将verificate限制为仅限于中间1(而不是中间2) . 你想要的是基于验证结果的一些授权层 - 即你可能想检查客户的证书发行者是否为中间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/emailAddress=cert@asdf.com") {
            return 403;
        }
    }
    
  • 44

    你试过增加 ssl_verify_depth 指令吗? Docs说:

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

    但你的验证深度是1.你说:

    我已经尝试将ssl_verify_depth显式设置为1(如上所述)然后甚至为0(不确定该数字的确切含义),但仍然会得到相同的错误 .

    所以,试试2或3 ..

    PS: 在我发现提到此问题的任何地方,它被告知将中间CA证书与您的服务器证书组合在一起 . 按照验证的顺序将一个文件(如@vikas-nalwar suggested和你所做的那样)(但我不确定订单是否重要)并粗略地说明了捆绑中的证书数量 ssl_verify_depth .

  • 2

    我相信您希望在服务器端启用客户端验证 . 如果是这样的话,我不会看到你的链中有你的客户证书 . 请按完全相同的顺序尝试以下操作 . 使用certchain.pem .

    cat client.crt > certchain.pem
      cat intermediate.crt >> certchain.pem
      cat root.crt >> certchain.pem
    
  • 0

    当我与nginx和cloudflare搏斗时,
    这些线为我做了诀窍:

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

    optional_no_ca 的第二行是重要的部分

  • 2

    我不得不说用 nginx/1.13.2 为我工作正常,即

    • 我有一个根CA签署了两个中间CA.

    • 两个中间体各签了一个客户

    • 我连接了像 cat client-intermediate1.crt ca-client.crt > ca.chained1.crtcat client-intermediate2.crt ca-client.crt > ca.chained2.crt 以及 cat ca.chained1.crt ca.chained2.crt > ca.multiple.intermediate.crt 这样的证书

    • 如果我只把ca.chained1.crt作为 ssl_client_certificate 那么只有client1.crt可以连接,同样对于ca.chained2.crt / client2.crt

    • 当我使用 ca.multiple.intermediate.crt 时,两个客户端都可以连接

    要撤消中间件,只需从ca.multiple.intermediate.crt中删除cert链

    这是相关的配置 . 它还具有高安全性设置

    # 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

    另一种简单的方法是在单个文件中连接证书(包括域证书)并在您的服务器和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阅读更多相关信息

相关问题