GnuTLS无法正确验证googleapis.com的证书

时间:2017-10-25 14:43:52

标签: ssl gnutls

我最初问related question on security.stackexchange.com。这是MCVE。

简短版本:当我使用GnuTLS验证与googleapis.com的HTTPS连接时,验证失败。对于其他网站(例如github.com),它会成功。

我正在显式加载/etc/ssl/certs/ca-certificates.crt文件(在真实程序中,我们将其缓存,而不是每次都打到文件系统)。

Ubuntu的CA商店是updated recently。在此更新之前,以下代码有效。自更新以来,它失败了。

Ubuntu 14.04,使用g++ -o gnutls-client gnutls-client.cpp -lgnutls

进行编译
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <gnutls/x509.h>
#include <assert.h>

#define CURL_CA_BUNDLE "/etc/ssl/certs/ca-certificates.crt"   // FAILS
//#define CURL_CA_BUNDLE "old-ca-certificates.crt"      // WORKS

#define CHECK(x) assert((x) >= 0);

// Fails with sheets.googleapis.com
// Succeeds with (e.g.) github.com

int main(int argc, char *argv[])
{
    if (argc < 2) {
        exit(1);
    }

    const char *server_name = argv[1];

    gnutls_global_init();
    printf("gnutls-client (GnuTLS/%s)\n", gnutls_check_version(NULL));

    gnutls_certificate_credentials_t creds = NULL;
    CHECK(gnutls_certificate_allocate_credentials(&creds));
    gnutls_certificate_set_verify_flags(creds,
                    GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
    int certificateCount = gnutls_certificate_set_x509_trust_file(creds,
                    CURL_CA_BUNDLE, GNUTLS_X509_FMT_PEM);
    if (certificateCount >= 0) {
        printf("%d certificate(s) processed\n", certificateCount);
    }
    else {
        printf("Failed to set trust file: %d\n", certificateCount);
        exit(1);
    }

    gnutls_session_t session = NULL;
    CHECK(gnutls_init(&session, GNUTLS_CLIENT));

    CHECK(gnutls_server_name_set(session, GNUTLS_NAME_DNS,
        server_name, strlen(server_name)));
    CHECK(gnutls_set_default_priority(session));
    CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, creds));

    struct addrinfo hint, *addr;
    memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_INET;
    hint.ai_socktype = SOCK_STREAM;
    getaddrinfo(server_name, "https", &hint, &addr);

    int sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    CHECK(connect(sockfd, addr->ai_addr, addr->ai_addrlen));

    gnutls_transport_set_int(session, sockfd);

    int ret;
    do {
        ret = gnutls_handshake(session);
    } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
    if (ret < 0) {
        fprintf(stderr, "ret: %d\n", ret);
        exit(1);
    }

    printf("Connected: %s\n", gnutls_session_get_desc(session));

    unsigned int peercerts_size;
    const gnutls_datum_t *peercerts = gnutls_certificate_get_peers(session,
        &peercerts_size);

    printf("Server presented %d certs\n", peercerts_size);

    unsigned int verify_status;
    CHECK(gnutls_certificate_verify_peers2(session, &verify_status));
    printf("%d 0x%x\n", ret, verify_status);
    assert(verify_status == 0x0);

    return 0;
}

使用当前的CA捆绑包...

$ ./gnutls-client github.com
gnutls-client (GnuTLS/3.2.11)
148 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-GCM)
Server presented 2 certs
0 0x0

$ ./gnutls-client googleapis.com
gnutls-client (GnuTLS/3.2.11)
148 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-ECDSA-SECP256R1)-(AES-128-GCM)
Server presented 3 certs
0 0x42
gnutls-client: gnutls-client.cpp:82: int main(int, char**): Assertion `verify_status == 0x0' failed.
Aborted (core dumped)

使用以前的CA捆绑包...

$ ./gnutls-client github.com
gnutls-client (GnuTLS/3.2.11)
173 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-GCM)
Server presented 2 certs
0 0x0

$ ./gnutls-client googleapis.com
gnutls-client (GnuTLS/3.2.11)
173 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-ECDSA-SECP256R1)-(AES-128-GCM)
Server presented 3 certs
0 0x0

gnutls-cli,在同一台机器上运行正常:

$ gnutls-cli googleapis.com --x509cafile /etc/ssl/certs/ca-certificates.crt 
Processed 148 CA certificate(s).
Resolving 'googleapis.com'...
Connecting to '108.177.119.105:443'...
- Certificate type: X.509
 - Got a certificate list of 3 certificates.
 - Certificate[0] info:
  - subject `C=US,ST=California,L=Mountain View,O=Google Inc,CN=*.googleapis.com', issuer `C=US,O=Google Inc,CN=Google Internet Authority G2', RSA key 2048 bits, signed using RSA-SHA256, activated `2017-10-17 10:22:56 UTC', expires `2017-12-29 00:00:00 UTC', SHA-1 fingerprint `34e45ef97aadd3e73978790c2f16ce275a28cd1c'
 - Certificate[1] info:
  - subject `C=US,O=Google Inc,CN=Google Internet Authority G2', issuer `C=US,O=GeoTrust Inc.,CN=GeoTrust Global CA', RSA key 2048 bits, signed using RSA-SHA256, activated `2017-05-22 11:32:37 UTC', expires `2018-12-31 23:59:59 UTC', SHA-1 fingerprint `a6120fc0b4664fad0b3b6ffd5f7a33e561ddb87d'
 - Certificate[2] info:
  - subject `C=US,O=GeoTrust Inc.,CN=GeoTrust Global CA', issuer `C=US,O=Equifax,OU=Equifax Secure Certificate Authority', RSA key 2048 bits, signed using RSA-SHA1, activated `2002-05-21 04:00:00 UTC', expires `2018-08-21 04:00:00 UTC', SHA-1 fingerprint `7359755c6df9a0abc3060bce369564c8ec4542a3'
- The hostname in the certificate matches 'googleapis.com'.
- Peer's certificate is trusted
- Version: TLS1.2
- Key Exchange: RSA
- Cipher: AES-128-CBC
- MAC: SHA1
- Compression: NULL
- Handshake was completed

- Simple Client Mode:

^C

(请注意“同行的证书是可信的”)

更新中删除了“Equifax”证书,但根据security.stackexchange.com问题,GnuTLS应该看到中间的“GeoTrust”证书,并将其视为有效的根。

我做错了什么?

1 个答案:

答案 0 :(得分:1)

  

... apt-get install libgnutls28-dev,这是我的机器和香草14.04盒子之间唯一的相关区别

在您描述的情况下,此版本存在已知问题。有关详细信息和修补程序,请参阅gnutls28 in trusty no longer validates many valid certificate chains, such as google.com

  

$ gnutls-cli googleapis.com --x509cafile /etc/ssl/certs/ca-certificates.crt
  ...
   - 可信任Peer的证书

gnutls-cli仍在使用原始的gnutls版本2.12.23,默认情况下是Ubuntu 14.04(参见gnutls-cli -v的输出)。此版本似乎不受此问题的影响。