c语言 - 为什么系统的 SSL 证书未验证?



我制作了以下软件,通过SSL向谷歌主页发送Https get请求:

#include <stdio.h>
#include<string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif  //INADDR_NONE 
typedef int SOCKET;
SOCKET OpenConnection(char* hostname, char* port){
struct hostent *phe;
struct sockaddr_in sin;
unsigned int port_as_integer = htons((unsigned short)atoi(port));
int sock;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
if(port_as_integer == 0){
return -1;
}
sin.sin_port = port_as_integer;

if(phe = gethostbyname(hostname)){
memcpy(&sin.sin_addr,phe->h_addr, phe->h_length);
} else if((sin.sin_addr.s_addr = inet_addr(hostname)) == INADDR_NONE){
return -1;
}
sock = socket(PF_INET,SOCK_STREAM,6);
if(socket < 0 || connect(sock, (const struct sockaddr *)&sin, sizeof(sin) ) < 0 ){
return -1;
}
return (SOCKET) sock;
}
int verifyCerts( SSL_CTX* ctx )
{

const char *path = getenv(X509_get_default_cert_dir_env());
if (!path){
path = X509_get_default_cert_dir();
}
return SSL_CTX_load_verify_locations(ctx,NULL,path);
}
void releaseSocket( SSL_CTX* ctx, int server)
{
/* close socket */
close(server);   
/* release context */
SSL_CTX_free(ctx);        
putchar('n');
}

SSL_CTX* InitCTX(void)
{
OpenSSL_add_all_algorithms(); 
SSL_load_error_strings();
const SSL_METHOD* method = SSLv23_method();
SSL_CTX* ctx = SSL_CTX_new(method);
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1);

if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}

int value = verifyCerts( ctx );
if(value == 0) {
printf("Certificate errorn");
exit(1);
}
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
return ctx;
}
void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if (cert != NULL)
{
printf("Server certificates:n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %sn", line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %sn", line);
//free(line);       /* free the malloc'ed string */
X509_free(cert);     /* free the malloc'ed certificate copy */
} else {
printf("Info: No client certificates configured.n");
} 
}
int main(int argc, char* argv[])
{
printf("Initializing Connection");
char buf[1024];

SSL_library_init();
char* hostname = "google.com";
char* portnum = "443";
int bytes=0,error=0;
const char* cpRequestMessage = "GET / HTTP/1.1rnHost: www.google.comrnUser-Agent: Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0rnConnection: closernAccept: text/html;UTF-8rnAccept-Lang: grrnrn";
SSL_CTX* ctx = InitCTX();
int server = OpenConnection(hostname, portnum);
SSL* ssl = SSL_new(ctx);      /* create new SSL connection state */
SSL_set_fd(ssl, server);    /* attach the socket descriptor */

if (SSL_connect(ssl) == FAIL) {
ERR_print_errors_fp(stderr);
} else {
printf("nnConnected with %s encryptionn", SSL_get_cipher(ssl));

/* encrypt & send message */
printf("REQUEST:nn%sn",cpRequestMessage);
SSL_write(ssl, cpRequestMessage, strlen(cpRequestMessage));  

do {
int bytes = SSL_read(ssl, buf, sizeof(buf));
int error = SSL_get_error(ssl,bytes);
switch (error)
{
case SSL_ERROR_SSL:
puts("SSL ERROR SSL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_SYSCALL:
puts("SSL ERROR SYSCALL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC_JOB:
puts("SSL ERROR WANT ASYNC_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_X509_LOOKUP:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_WRITE:
puts("SSL ERROR WANT WRITE");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_READ:
puts("SSL ERROR WANT READ");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_ZERO_RETURN:
puts("SSL ERROR SSL_ERROR_ZERO_RETURN");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_NONE:
default:
break;
}
puts(buf);
} while (bytes>0);

/* release connection state */
SSL_free(ssl);       
}
releaseSocket(ctx,server);   
putchar('n');
return 0;
}

使用系统的CA路径文件夹进行验证。但由于某些原因,SSL证书没有经过验证:

39690623349184:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:1924:
Initializing Connection

你知道发生这种情况的原因吗?

编辑1

我还尝试手动放置系统的证书路径:

首先我检测到了路径(尽管命令失败了,但我只需要找到证书的位置。

update-ca-certificates  --verbose
Updating certificates in /etc/ssl/certs...
/usr/sbin/update-ca-certificates: 101: /usr/sbin/update-ca-certificates: cannot create /etc/ssl/certs/ca-certificates.crt.new: Permission denied

然后我尝试了以下操作:

int verifyCerts( SSL_CTX* ctx )
{
return SSL_CTX_load_verify_locations(ctx,NULL,"/etc/ssl/certs");
}

通过手动将路径指定为/etc/ssl/certs。其原理是检查我是否可以使用系统提供的证书的硬编码位置进行验证。

一旦我重新构建了我的应用程序,我发现它仍然失败:

139850890965440:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:1924:
Initializing Connection

但这条路充满了证书:

$ ls -l /etc/ssl/certs/ | head
σύνολο 560
lrwxrwxrwx 1 root root     23 Ιουν  9 21:35 002c0b4f.0 -> GlobalSign_Root_R46.pem
lrwxrwxrwx 1 root root     45 Μαΐ   8  2020 02265526.0 -> Entrust_Root_Certification_Authority_-_G2.pem
lrwxrwxrwx 1 root root     36 Μαΐ   8  2020 03179a64.0 -> Staat_der_Nederlanden_EV_Root_CA.pem
lrwxrwxrwx 1 root root     27 Μαΐ   8  2020 062cdee6.0 -> GlobalSign_Root_CA_-_R3.pem
lrwxrwxrwx 1 root root     25 Μαΐ   8  2020 064e0aa9.0 -> QuoVadis_Root_CA_2_G3.pem
lrwxrwxrwx 1 root root     50 Μαΐ   8  2020 06dc52d5.0 -> SSL.com_EV_Root_Certification_Authority_RSA_R2.pem
lrwxrwxrwx 1 root root     54 Μαΐ   8  2020 09789157.0 -> Starfield_Services_Root_Certificate_Authority_-_G2.pem
lrwxrwxrwx 1 root root     15 Οκτ  30  2020 0a775a30.0 -> GTS_Root_R3.pem
lrwxrwxrwx 1 root root     16 Μαΐ   8  2020 0b1b94ef.0 -> CFCA_EV_ROOT.pem

所有这些都从不同的路径符号链接。

您无法验证证书,因为您需要在主函数中放置以下行(根据此答案指定(:

int res = SSL_set_tlsext_host_name(ssl, hostname);
if(res == 0) {
ERR_print_errors_fp(stderr);
}

主要结果:

int main(int argc, char* argv[])
{
printf("Initializing Connection");
char buf[1024];

SSL_library_init();
char* hostname = "google.com";
char* portnum = "443";
int bytes=0,error=0;
const char* cpRequestMessage = "GET / HTTP/1.1rnHost: www.google.comrnUser-Agent: Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0rnConnection: closernAccept: text/html;UTF-8rnAccept-Lang: grrnrn";
SSL_CTX* ctx = InitCTX();
int server = OpenConnection(hostname, portnum);
SSL* ssl = SSL_new(ctx);      /* create new SSL connection state */
SSL_set_fd(ssl, server);    /* attach the socket descriptor */
int res = SSL_set_tlsext_host_name(ssl, hostname);
if(res == 0) {
ERR_print_errors_fp(stderr);
}

if (SSL_connect(ssl) == FAIL) {
ERR_print_errors_fp(stderr);
} else {
printf("nnConnected with %s encryptionn", SSL_get_cipher(ssl));

/* encrypt & send message */
printf("REQUEST:nn%sn",cpRequestMessage);
SSL_write(ssl, cpRequestMessage, strlen(cpRequestMessage));  

do {
int bytes = SSL_read(ssl, buf, sizeof(buf));
int error = SSL_get_error(ssl,bytes);
switch (error)
{
case SSL_ERROR_SSL:
puts("SSL ERROR SSL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_SYSCALL:
puts("SSL ERROR SYSCALL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC_JOB:
puts("SSL ERROR WANT ASYNC_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_X509_LOOKUP:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_WRITE:
puts("SSL ERROR WANT WRITE");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_READ:
puts("SSL ERROR WANT READ");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_ZERO_RETURN:
puts("SSL ERROR SSL_ERROR_ZERO_RETURN");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_NONE:
default:
break;
}
puts(buf);
} while (bytes>0);

/* release connection state */
SSL_free(ssl);       
}
releaseSocket(ctx,server);   
putchar('n');
return 0;
}

使用它,您可以使用系统证书进行验证。verifyCerts运行良好,如下所示:

int verifyCerts( SSL_CTX* ctx )
{

const char *path = getenv(X509_get_default_cert_dir_env());
if (!path){
path = X509_get_default_cert_dir();
}
return SSL_CTX_load_verify_locations(ctx,NULL,path);
}

最新更新