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

时间:2011-12-08 13:12:55

标签: authentication ssl nginx ssl-certificate

我正在尝试在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不支持中间证书的证书链?任何帮助非常感谢!

8 个答案:

答案 0 :(得分:44)

编辑:我也有这个“问题”,解决方案和解释位于文本底部

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

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

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

当我使用 IntermediateCA1和RootCA 放入“ssl_client_certificate”文件并设置“ssl_verify_depth 2”(或更多)时,客户端可以使用证书 Client1和Client2 登录到站点strong>(应该只是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/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,但这只是解决方法..

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

编辑2“ * 好的,这不是错误,它是功能;) *

我在这里得到回复:http://trac.nginx.org/nginx/ticket/301 它正在运作,你必须只检查你的ssl_client_i_dn是什么(。你可以使用证书主题,或者你想要的http://wiki.nginx.org/HttpSslModule#Built-in_variables

来代替发行者
  

这是证书验证的工作原理:证书必须是   验证了受信任的根。如果链不能构建为可信任的   root(非中间) - 验证失败。如果你信任root - all   由其直接或间接签署的证书将是   成功验证。

     

如果您可以使用限制验证深度   想要将客户端证书限制为直接颁发的证书   只是,但它更多的是关于DoS预防,显然它不可能   用于将verificate仅限制为intermediate1(但不是   intermediate2)。

     

您想要的是基于授权层的一些内容   在验证结果上 - 即您可能想要检查该客户端   证书颁发者是中间人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;
    }
}

答案 1 :(得分:11)

您是否尝试过增加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设置为捆绑中的证书数量。< / p>

答案 2 :(得分:2)

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

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

答案 3 :(得分:2)

当我与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 的第二行是重要的部分

答案 4 :(得分:1)

我必须说nginx/1.13.2对我来说工作正常,即

  • 我有一个签署两个中间CA的根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,那么只有client1.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;

答案 5 :(得分:0)

另一种简单的方法是在单个文件中连接证书(包括域证书)并在服务器和nginx配置文件中使用它

  

cat www.example.com.crt bundle.crt&gt; www.example.com.chained.crt

始终记住首先使用服务器证书,然后只使用CA服务器证书

您可以在http://nginx.org/en/docs/http/configuring_https_servers.html#chains

了解更多信息

答案 6 :(得分:0)

ssl_verify_depth -> sets a verification depth in the client certificates chain

Authority提供了一堆链接的证书,这些证书应与已签名的服务器证书串联在一起。服务器证书必须出现在组合文件中链接证书的前面

'$ openssl s_client -connect www.godaddy.com:443

...

Certificate chain
 0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
     /1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc
     /OU=MIS Department/CN=www.GoDaddy.com
     /serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
   i:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
 2 s:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
   i:/L=ValiCert Validation Network/O=ValiCert, Inc.
     /OU=ValiCert Class 2 Policy Validation Authority
     /CN=http://www.valicert.com//emailAddress=info@valicert.com'

在此示例中,www.GoDaddy.com服务器证书#0的主题(“ s”)由颁发者(“ i”)签名,而颁发者本身就是证书#1的主题,证书由#1签署。颁发者本身就是证书#2的主体,该证书由著名的颁发者ValiCert,Inc.签名,该证书的证书存储在浏览器的内置证书库中

Nginx使用ssl_verify_depth指令深入到证书束中,以验证发布者在其受信任存储中的身份,该存储在proxy_ssl_trusted_certificate

答案 7 :(得分:0)

@Jack和@HansL(仅允许一个IntermediateCA1允许客户端使用的解决方案)是使用nginx config ssl_trusted_certificate。来自nginx documentation

使用PEM格式指定具有受信任CA证书的文件,该证书用于 如果启用了ssl装订,请验证客户端证书和OCSP响应。 与ssl客户端证书设置的证书相反,这些列表 证书将不会发送给客户

这些设置对我有用(但当然可以与其他PKI一起使用)。在第一个文件中,仅存在IntermediateCA1。在后者中,只有RootCA

ssl_client_certificate     /etc/nginx/ssl/ca-bundle-for-client-selection-filtering.crt;
ssl_trusted_certificate    /etc/nginx/ssl/ca-bundle-for-client-cert-valdiation.crt;