3

I'm trying to verify the certificate of an IRC server against the UTN_USERFirst_Hardware CA. I'm running into error code 20 from SSL_get_verify_result (which if I'm correct means that it failed to get the peer cert?). Also, I'm not all too sure what the precedence of 'steps' is in the process.

Here is what I've cobbled together:

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

And the calling routine for context:

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

So, as it stands my flow is this: Establish connection at the socket (BIO) level -> call SSL_get_peer_certificate -> do certificate verification, is this correct?

What's the difference between SSL_get_verify_result and X509_verify_cert and which one should be used first? Sorry if this is too general, I need a little guidance and the docs are just manual pages with not much correlative information, I find it hard to get an organized view of OpenSSL.

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

  • Verification return code 20 is *"unable to get local issuer certificate"*. The codes are defined in `x509_vfy.h`, and 20 is `X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY`. The OpenSSL man page which discusses them is [`openssl verify`](http://www.openssl.org/docs/manmaster/apps/verify.html). See [OpenSSL Verify return code: 20 (unable to get local issuer certificate)](http://stackoverflow.com/q/11548336) on Stack Overflow. Also see [SSL/TLS Client](http://wiki.openssl.org/index.php/SSL/TLS_Client) on the OpenSSL wiki. The wiki page will answer *"How do I verify a peer certificate ..."* – jww Oct 08 '16 at 09:27
  • Have you tried use [`int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);`](https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_default_verify_paths.html) to set-up the dafult CA file path? – TJM Oct 19 '20 at 06:08

1 Answers1

1

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

I believe that's the wrong Root CA.


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

I think you are just about where you need to be. You only need a few tweaks.

When I examine the topmost Issuer in the chain (s: indicates Subject, while i: indicates 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)

It looks like you want your root of trust at CN=AddTrust External CA Root; not UTN_USERFirst_Hardware_Root_CA. You can find it at Comodo's site at [Root] AddTrust External CA Root.

The file addtrustexternalcaroot.crt is in PEM format. You only need to specify the -CAfile option to make the command complete successfully (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

Now that you know how to test it and what the good test case looks like, here's how to fix it in your code:

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

Here's what the CA Root looks like:

$ 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
jww
  • 97,681
  • 90
  • 411
  • 885
  • Thank you, I don't know why I was using that CA m( I've fixed that issue but I still get error 20 as before. –  Oct 10 '16 at 06:30
  • I've double checked it, and yes I have followed your advice, and downloaded the CA and used it exactly as you have it there, but `SSL_get_verify_result` is still returning 20 for some reason. –  Oct 11 '16 at 20:56
  • Okay, here it is: http://pastebin.com/2sspScj8 As far as I know this code is sufficient to establish a connection and verify a peer certificate. –  Oct 14 '16 at 03:53
  • I meant to include @jww –  Oct 14 '16 at 03:55
  • @Ezechiel - You have to call `SSL_{CTX}_load_verify_locations` before you make the connection. Please visit the [SS/TLS Cleint](https://wiki.openssl.org/index.php/SSL/TLS_Client) example on the OpenSSL wiki. – jww Oct 14 '16 at 04:03
  • That was the silver bullet, thank you. Now to figure out how to verify the whole certificate chain. –  Oct 14 '16 at 04:41
  • @Ezechiel - OpenSSL builds the path and validates the entire chain. The library builds the path according to [RFC 4158](http://tools.ietf.org/html/rfc4158). OpenSSL 1.1.0 (and above) differs from OpenSSL 1.0.2 (and below) because 1.1.0 performs hostname matching, while 1.0.2 does not. I've asked before, but please visit [SSL/TLS Client](http://wiki.openssl.org/index.php/SSL/TLS_Client). I wrote the wiki page, and all the information is there. – jww Oct 14 '16 at 04:50