GnuTLS 未正确验证 googleapis.com 的证书
GnuTLS doesn't correctly verify certs for googleapis.com
我本来问了一个related question on security.stackexchange.com。这是 MCVE。
简短版:当我使用 GnuTLS 验证到 googleapis.com 的 HTTPS 连接时,验证失败。对于其他站点(例如 github.com),它会成功。
我正在显式加载 /etc/ssl/certs/ca-certificates.crt
文件(在实际程序中,我们缓存它,而不是每次都访问文件系统)。
CA 商店由 Ubuntu 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
(注意"Peer's certificate is trusted")
更新中删除了 "Equifax" 证书,但根据 security.stackexchange.com 问题,GnuTLS 应该看到中间 "GeoTrust" 证书并将其视为有效根。
我做错了什么?
... apt-get install libgnutls28-dev, which is the only relevant difference between my machine and a vanilla 14.04 box
在您描述的情况下,此版本存在一个已知问题。有关详细信息和补丁,请参阅 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's certificate is trusted
gnutls-cli 仍在使用原始的 gnutls 版本 2.12.23,默认情况下带有 Ubuntu 14.04(请参阅 gnutls-cli -v
的输出)。此版本似乎不受此问题影响。
我本来问了一个related question on security.stackexchange.com。这是 MCVE。
简短版:当我使用 GnuTLS 验证到 googleapis.com 的 HTTPS 连接时,验证失败。对于其他站点(例如 github.com),它会成功。
我正在显式加载 /etc/ssl/certs/ca-certificates.crt
文件(在实际程序中,我们缓存它,而不是每次都访问文件系统)。
CA 商店由 Ubuntu 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
(注意"Peer's certificate is trusted")
更新中删除了 "Equifax" 证书,但根据 security.stackexchange.com 问题,GnuTLS 应该看到中间 "GeoTrust" 证书并将其视为有效根。
我做错了什么?
... apt-get install libgnutls28-dev, which is the only relevant difference between my machine and a vanilla 14.04 box
在您描述的情况下,此版本存在一个已知问题。有关详细信息和补丁,请参阅 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's certificate is trusted
gnutls-cli 仍在使用原始的 gnutls 版本 2.12.23,默认情况下带有 Ubuntu 14.04(请参阅 gnutls-cli -v
的输出)。此版本似乎不受此问题影响。