SSL 证书校验错误 Trust anchor for certification path not found

问题描述

在使用 acme.sh脚本签发的Let’s Encrypt 免费SSL证书,在android中无法通过SSL系统验证,出现以下异常Trust anchor for certification path not found

1
2
3
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.

问题发现

安卓客户端无法连接域名服务器,浏览器直接打开网址正常,ssl连接正常,在SDK里连接失败。

错误消息为:

1
2
3
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.

问题原因

直接让 nginx配置文件使用了acme.sh脚本签发的<doman>.cer文件,这里面的文件都是内部使用,直接使用会导致服务器证书链配置错误,缺少中间证书设置。

解决方案

参考stackoverflow

  1. 使用openssl检查证书

    1
    openssl s_client -debug -connect www.mafxcc.com:443

    可能会出现:

    1
    Verify return code: 21 (unable to verify the first certificate)

    而且在前面的输出里有:

    1
    2
    3
    4
    5
    6
    depth=0 OU = Domain Control Validated, CN = www.domain.com
    verify error:num=20:unable to get local issuer certificate
    verify return:1 depth=0 OU = Domain Control Validated, CN = www.domain.com
    verify error:num=27:certificate not trusted
    verify return:1 depth=0 OU = Domain Control Validated, CN = www.domain.com
    verify error:num=21:unable to verify the first certificate`

    证书链只包含一个元素:

    1
    2
    3
    Certificate chain  
    0 s:CN = domain.com
    i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

    但是应该有将一个链中的签名权限引用回安卓信任的权限(Verisign、GlobalSign等):

    1
    2
    3
    4
    5
    6
    7
    ---  
    Certificate chain
    0 s:CN = domain.com
    i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
    1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
    i:O = Digital Signature Trust Co., CN = DST Root CA X3
    ---
  2. 在用acme.sh把证书生成以后, 需要把证书安装到真正需要用它的地方

    注意: 默认生成的证书都放在安装目录下: ~/.acme.sh/.com/, 不要直接使用此目录下的文件, 例如:
    不要直接让 nginx/apache 的配置文件使用这下面的文件. 这里面的文件都是内部使用, 而且目录结构可能会变化.

    正确的使用方法是使用 --installcert 命令,并指定目标位置, 然后证书文件会被安装到相应的位置, 例如:

    1
    2
    3
    4
    $ acme.sh --installcert -d <domain>.com \
    --key-file /etc/nginx/ssl/<domain>.key \
    --fullchain-file /etc/nginx/ssl/fullchain.cer \
    --reloadcmd "service nginx force-reload"

最后

在 Nginx 的配置 ssl_certificate 使用 /etc/nginx/ssl/fullchain.cer ,而非 /etc/nginx/ssl/<domain>.cer !

重启 Nginx 后再次验证 SSL证书。