SSL_get_verify_result returns irc.freenode.net 的错误 20

SSL_get_verify_result returns Error 20 for irc.freenode.net

我正在尝试根据 UTN_USERFirst_Hardware CA 验证 IRC 服务器的证书。 我 运行 从 SSL_get_verify_result 进入错误代码 20(如果我是正确的,这意味着它未能获得对等证书?)。 另外,我不太确定 'steps' 在这个过程中的优先级是什么。

这是我拼凑的:

int tallis_ssl_verify(tallis_t *tallis, X509 *cert)
{
    int rv;

    X509_VERIFY_PARAM_set_hostflags(
            tallis->param,
            X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);

    SSL_CTX_set_default_verify_paths(tallis->ssl_context);

    ERR_clear_error();
    rv = SSL_CTX_load_verify_locations(
            tallis->ssl_context,
            "/etc/ssl/certs/AddTrust_External_Root.pem",
            "/etc/ssl/certs");

    if (!rv)
    {
        fprintf(stderr, ERR_error_string(ERR_get_error(), NULL));
        return 1;
    }

    X509_VERIFY_PARAM_set1_host(tallis->param, tallis->host, 0);
    SSL_CTX_set_verify(tallis->ssl_context, SSL_VERIFY_PEER, NULL);
    SSL_set_verify(tallis->ssl_connection, SSL_VERIFY_PEER, NULL);

    ERR_clear_error();
    rv = SSL_get_verify_result(tallis->ssl_connection);

    if (rv != X509_V_OK)
    {
        printf("%d\n", rv);
        fprintf(stderr, ERR_error_string(ERR_get_error(), NULL));
        return 1;
    }

    ERR_clear_error();
    X509_STORE_CTX *ctx = X509_STORE_CTX_new();

    if (!ctx)
    {
        fprintf(stderr, ERR_error_string(ERR_get_error(), NULL));
        return 1;
    }

    ERR_clear_error();
    X509_STORE *store = X509_STORE_new();

    if (!store)
    {
        X509_STORE_free(store);
        fprintf(stderr, ERR_error_string(ERR_get_error(), NULL));
        return 1;
    }

    ERR_clear_error();
    rv = X509_STORE_CTX_init(ctx, store, cert, NULL);

    if (!rv)
    {
        X509_STORE_free(store);
        fprintf(stderr, ERR_error_string(ERR_get_error(), NULL));
        return 1;
    }

    X509_STORE_set_flags(store, X509_V_FLAG_CB_ISSUER_CHECK);
    X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());

    X509_STORE_load_locations(
            store,
            "/etc/ssl/certs/AddTrust_External_Root.pem",
            NULL);

    X509_STORE_set_default_paths(store);

    X509_LOOKUP_load_file(
            lookup,
            "/etc/ssl/certs/AddTrust_External_Root.pem",
            X509_FILETYPE_PEM);

    X509_STORE_add_cert(store, cert);

    ERR_clear_error();
    rv = X509_verify_cert(ctx);

    if (rv != 1)
    {
        X509_STORE_free(store);
        fprintf(
                stderr,
                "%s\n%s\n",
                ERR_error_string(ERR_get_error(), NULL),
                X509_verify_cert_error_string(ctx->error));

        return 1;
    }

    return 0;
}

上下文的调用例程:

int main(int argc, char *argv[])
{
    tallis_t *tallis = malloc(sizeof(tallis_t));
    tallis->host = "irc.freenode.net";
    tallis->port = "6697";
    tallis->bio = NULL;
    tallis->ssl_connection = NULL;
    ssl_init(tallis->ssl_connection);
    tallis->ssl_context = SSL_CTX_new(TLSv1_2_client_method());
    tallis->ssl_connection = SSL_new(tallis->ssl_context);
    tallis->param = SSL_get0_param(tallis->ssl_connection);

    int rv;

    rv = tallis_connect(tallis);

    if (rv)
        DIE("%s\n", "connection failed");

    ERR_clear_error();
    X509 *cert = SSL_get_peer_certificate(tallis->ssl_connection);

    if (!cert)
        DIE("%s\n", ERR_error_string(ERR_get_error(), NULL));

    rv = tallis_ssl_verify(tallis, cert);

    X509_free(cert);

    if (rv)
        DIE("%s\n", "certificate verificiation failed");
    else
        printf("%s\n", "certificate verification succeeded");

    rv = tallis_loop(tallis);

    if (rv)
        DIE("%s\n", "socket connection terminated");

    rv = ssl_shutdown(tallis);

    if (rv)
        DIE("%s\n", "ssl shutdown failed");
}

因此,目前我的流程是这样的:在套接字 (BIO) 级别建立连接 -> 调用 SSL_get_peer_certificate -> 进行证书验证,这样正确吗?

SSL_get_verify_resultX509_verify_cert有什么区别,应该先用哪个? 抱歉,如果这太笼统了,我需要一些指导,文档只是没有太多相关信息的手册页,我发现很难有条理地了解 OpenSSL。

怎么样'How do I verify a peer certificate in OpenSSL as simply as possible?'

I'm trying to verify the certificate of an IRC server against the UTN_USERFirst_Hardware CA

我认为根 CA 不对。


How about 'How do I verify a peer certificate in OpenSSL as simply as possible?'

我认为你正处于你需要去的地方。您只需要进行一些调整。

当我检查链中最顶端的 Issuer 时(s:表示 Subject,而 i: 表示 Issuer):

$ openssl s_client -connect irc.freenode.net:6697 -servername irc.freenode.net -tls1_2
CONNECTED(00000005)
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify error:num=20:unable to get local issuer certificate
Server did acknowledge servername extension.
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.freenode.net
   i:/C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
 1 s:/C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
   i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
 2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
...

<skip certificate and lots of other stuff>

...
Start Time: 1475924259
Timeout   : 7200 (sec)
Verify return code: 20 (unable to get local issuer certificate)

您似乎希望在 CN=AddTrust External CA Root 处建立信任根;不是 UTN_USERFirst_Hardware_Root_CA。您可以在 Comodo 的网站 [Root] AddTrust External CA Root.

找到它

文件addtrustexternalcaroot.crt为PEM格式。只需要指定-CAfile选项即可使命令成功完成(X509_V_OK):

$ openssl s_client -connect irc.freenode.net:6697 -servername irc.freenode.net -tls1_2 -CAfile addtrustexternalcaroot.crt 
CONNECTED(00000005)
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify return:1
depth=1 C = FR, ST = Paris, L = Paris, O = Gandi, CN = Gandi Standard SSL CA 2
verify return:1
depth=0 OU = Domain Control Validated, OU = Gandi Standard Wildcard SSL, CN = *.freenode.net
verify return:1
Server did acknowledge servername extension.
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.freenode.net
   i:/C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
 1 s:/C=FR/ST=Paris/L=Paris/O=Gandi/CN=Gandi Standard SSL CA 2
   i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
 2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
...

<skip certificate and lots of other stuff>

...
Start Time: 1475924761
Timeout   : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no

既然您知道如何测试它以及好的测试用例是什么样的,下面是如何在您的代码中修复它:

rv = SSL_CTX_load_verify_locations(
        ssl_context,
        ".../addtrustexternalcaroot.crt",
        NULL;

这是 CA 根的样子:

$ cat addtrustexternalcaroot.crt | openssl x509 -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
        Validity
            Not Before: May 30 10:48:38 2000 GMT
            Not After : May 30 10:48:38 2020 GMT
        Subject: C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b7:f7:1a:33:e6:f2:00:04:2d:39:e0:4e:5b:ed:
                    1f:bc:6c:0f:cd:b5:fa:23:b6:ce:de:9b:11:33:97:
                    a4:29:4c:7d:93:9f:bd:4a:bc:93:ed:03:1a:e3:8f:
                    cf:e5:6d:50:5a:d6:97:29:94:5a:80:b0:49:7a:db:
                    2e:95:fd:b8:ca:bf:37:38:2d:1e:3e:91:41:ad:70:
                    56:c7:f0:4f:3f:e8:32:9e:74:ca:c8:90:54:e9:c6:
                    5f:0f:78:9d:9a:40:3c:0e:ac:61:aa:5e:14:8f:9e:
                    87:a1:6a:50:dc:d7:9a:4e:af:05:b3:a6:71:94:9c:
                    71:b3:50:60:0a:c7:13:9d:38:07:86:02:a8:e9:a8:
                    69:26:18:90:ab:4c:b0:4f:23:ab:3a:4f:84:d8:df:
                    ce:9f:e1:69:6f:bb:d7:42:d7:6b:44:e4:c7:ad:ee:
                    6d:41:5f:72:5a:71:08:37:b3:79:65:a4:59:a0:94:
                    37:f7:00:2f:0d:c2:92:72:da:d0:38:72:db:14:a8:
                    45:c4:5d:2a:7d:b7:b4:d6:c4:ee:ac:cd:13:44:b7:
                    c9:2b:dd:43:00:25:fa:61:b9:69:6a:58:23:11:b7:
                    a7:33:8f:56:75:59:f5:cd:29:d7:46:b7:0a:2b:65:
                    b6:d3:42:6f:15:b2:b8:7b:fb:ef:e9:5d:53:d5:34:
                    5a:27
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
            X509v3 Key Usage: 
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Authority Key Identifier: 
                keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
                DirName:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
                serial:01

    Signature Algorithm: sha1WithRSAEncryption
         b0:9b:e0:85:25:c2:d6:23:e2:0f:96:06:92:9d:41:98:9c:d9:
         84:79:81:d9:1e:5b:14:07:23:36:65:8f:b0:d8:77:bb:ac:41:
         6c:47:60:83:51:b0:f9:32:3d:e7:fc:f6:26:13:c7:80:16:a5:
         bf:5a:fc:87:cf:78:79:89:21:9a:e2:4c:07:0a:86:35:bc:f2:
         de:51:c4:d2:96:b7:dc:7e:4e:ee:70:fd:1c:39:eb:0c:02:51:
         14:2d:8e:bd:16:e0:c1:df:46:75:e7:24:ad:ec:f4:42:b4:85:
         93:70:10:67:ba:9d:06:35:4a:18:d3:2b:7a:cc:51:42:a1:7a:
         63:d1:e6:bb:a1:c5:2b:c2:36:be:13:0d:e6:bd:63:7e:79:7b:
         a7:09:0d:40:ab:6a:dd:8f:8a:c3:f6:f6:8c:1a:42:05:51:d4:
         45:f5:9f:a7:62:21:68:15:20:43:3c:99:e7:7c:bd:24:d8:a9:
         91:17:73:88:3f:56:1b:31:38:18:b4:71:0f:9a:cd:c8:0e:9e:
         8e:2e:1b:e1:8c:98:83:cb:1f:31:f1:44:4c:c6:04:73:49:76:
         60:0f:c7:f8:bd:17:80:6b:2e:e9:cc:4c:0e:5a:9a:79:0f:20:
         0a:2e:d5:9e:63:26:1e:55:92:94:d8:82:17:5a:7b:d0:bc:c7:
         8f:4e:86:04