客户端程序验证SSL_get_peer_certificate返回的服务器证书? [英] Client program to validate server certificate returned by SSL_get_peer_certificate?

查看:486
本文介绍了客户端程序验证SSL_get_peer_certificate返回的服务器证书?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用OpenSSL的C / C ++编程语言的SSL / TLS客户端程序。我正在寻找方法来验证由 SSL_get_peer_certificate 函数调用返回的服务器证书( X509 )。此外,我有我自己的CA证书加载使用 SSL_CTX_load_verify_locations 功能。 CA认证了服务器证书。

I have a SSL/TLS client program using OpenSSL in C++ programming language. I am looking for methods to validate server certificate (X509) returned by SSL_get_peer_certificate function call. Also, I have my own CA certificate loaded using SSL_CTX_load_verify_locations function. The CA certified the server certificate.

我可以对我的服务器进行SSL会话。现在,我想验证在SSL握手期间使用我自己的CA接收的服务器证书。我找不到在C或C ++中做到的方法。

I am able to make SSL session to my server. Now, i want to validate server certificate received during SSL handshake using my own CA. I couldn't find a way to do it in C or C++.

#include <iostream>
#include <string.h>

#include <unistd.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>


#define DEFAULT_PORT_NUMBER                     443

int create_socket(char *, uint16_t port_num);

int openSSL_client_init()
{
    OpenSSL_add_all_algorithms();
    ERR_load_BIO_strings();
    ERR_load_crypto_strings();
    SSL_load_error_strings();

    if (SSL_library_init() < 0)
        return -1;
    return 0;
}

int openSSL_create_client_ctx(SSL_CTX **ctx)
{
    const SSL_METHOD *method = SSLv23_client_method();

    if ((*ctx = SSL_CTX_new(method)) == NULL)
        return -1;


    //SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);

    return 0;
}

int main(int argc, char *argv[])
{
BIO *outbio = NULL;

X509 *cert;
X509_NAME *certname = NULL;

SSL_CTX *ctx;
SSL *ssl;
int server = 0;
int ret, i;

if (openSSL_client_init()) {
    std :: cerr << "Could not initialize the OpenSSL library !" << std     :: endl;
    return -1;
}

outbio  = BIO_new_fp(stdout, BIO_NOCLOSE);

if (openSSL_create_client_ctx(&ctx)) {
    std :: cerr << "Unable to create a new SSL context structure." << std :: endl;
    return -1;
}


std :: cout << "Adding Certifcate" << std :: endl;
if (SSL_CTX_load_verify_locations(ctx, "ca-cert.pem", NULL) <= 0) {
    std :: cerr << "Unable to Load certificate" << std :: endl;
    return -1;
}


ssl = SSL_new(ctx);
server = create_socket(argv[1], atoi(argv[2]));

if (server < 0) {
    std :: cerr << "Error: Can't create TCP session" << std :: endl;
    return -1;
}
std :: cout << "Successfully made the TCP connection to: " << argv[1] << " port: " << atoi(argv[2]) << std :: endl;

SSL_set_fd(ssl, server);

if (SSL_connect(ssl) != 1) {
    std :: cerr << "Error: Could not build a SSL session to: " << argv[1] << std :: endl;
    return -1;
}

std :: cout << "Successfully enabled SSL/TLS session to: " << argv[1] << std :: endl;
//SSL_SESSION *ss = SSL_get_session(ssl);

cert = SSL_get_peer_certificate(ssl);
if (cert == NULL) {
    std :: cerr << "Error: Could not get a certificate from: " <<  argv[1] << std :: endl;
    return -1;
}

certname = X509_NAME_new();
certname = X509_get_subject_name(cert);

std :: cout << "Displaying the certificate subject data:" << std :: endl;
X509_NAME_print_ex(outbio, certname, 0, 0);
std :: cout << std :: endl;


char msg[100000] = "GET / HTTP/1.1\r\nHOST: www.siliconbolt.com\r\n\r\n";
SSL_write(ssl, msg, strlen(msg));
SSL_read(ssl, msg, 100000);
std :: cout << "Message is " << msg << std :: endl;


SSL_free(ssl);
close(server);
X509_free(cert);
SSL_CTX_free(ctx);
std :: cout << "Finished SSL/TLS connection with server" << std :: endl;
return 0;
}


int create_socket(char *ip_cstr, uint16_t port_num)
{
int fd;
struct sockaddr_in dest_addr;

fd = socket(AF_INET, SOCK_STREAM, 0);

dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port_num);
dest_addr.sin_addr.s_addr = inet_addr(ip_cstr);

memset(&(dest_addr.sin_zero), '\0', 8);

if (connect(fd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) == -1)
    return -1;

return fd;
}


推荐答案

事情和OpenSSL只有一点帮助。如果你期望每个步骤的完整代码,我会认为这个问题太宽泛。因此,我将集中在验证所需的基本部分,并主要指出其他资源的具体部分的实施细节。

Proper certificate verification is a complex thing and OpenSSL is only slightly helpful there. If you expect full code for each step I would consider the question as too broad. Therefore I'll focus on the essential parts needed in verification and mainly point out other resources for the details of the implementation of the specific parts.

验证服务器证书的第一步是将信任链构建到受信任的根CA证书<强>。如果您设置了受信任的根(即在您的代码中调用 SSL_CTX_load_verify_locations ),并且还使用 SSL_CTX_set_verify 更改为 SSL_VERIFY_PEER 。此内置验证还包括检查叶和链证书是否已经有效且尚未到期。

The first step for validating a server certificate is building the trust chain to a trusted root CA certificate. This is implicitly done by openssl inside the TLS handshake if you've set a trusted root (i.e. call of SSL_CTX_load_verify_locations in your code) and also set the verification mode with SSL_CTX_set_verify to SSL_VERIFY_PEER. This built-in validation also includes checks if the leaf and chain certificates are already valid and not yet expired.

下一步是验证主题证书与预期匹配。对于服务器证书,这通常意味着目标主机名以某种方式包括在证书的公用名或主题备用名中。实际要求取决于应用程序层协议,即HTTP,SMTP,LDAP ...都有略微不同的规则,特别是如果涉及通配符。由于OpenSSL 1.0.2 aa X509_check_host 功能可用,可用于在大多数情况下检查规则。使用早期版本的OpenSSL,您可以自己实现这样的功能。请注意,您明确需要验证主机名,即OpenSSL不会为您执行此操作,并且省略此步骤将使中间人攻击您的应用程序变得容易。

The next step is to validate if the subject of the certificate matches the expected one. For server certificates this usually means that the target hostname is somehow included in the common name or the subject alternative names of the certificate. The actual requirements depend on the application layer protocol, i.e. HTTP, SMTP, LDAP ... all have slightly different rules especially if wildcards are involved. Since OpenSSL 1.0.2 a a X509_check_host function is available and can be used to check against the rules in most cases. With earlier versions of OpenSSL you are on your own to implement such a function. Note that you explicitly need to validate the hostname, i.e. OpenSSL will not do this for you and omitting this step will make man in the middle attacks against your application easy.

这一点,你知道证书直接或间接由受信任的根CA发出,并且证书与预期的主机名匹配。您现在需要检查证书是否已撤销。一种方法是使用您到达某处的证书撤销列表( CRL ),另一种方法是使用在线证书状态协议( OCSP )。

At this point that you know that the certificate is directly or indirectly issued by a trusted root CA and that the certificate matches the expected hostname. You now need to check if the certificate is revoked. One way is to use a certificate revocation list (CRL) which you got somewhere, another is to use the the Online Certificate Status Protocol (OCSP).

对于CRL,请参阅 OpenSSL现在自动处理CRL(证书吊销列表)吗?如何使用您已经下载的CRL。但是OpenSSL不会帮助您首先下载CRL。

For CRL see Does OpenSSL automatically handle CRLs (Certificate Revocation Lists) now? which hows how to use a CRL you've downloaded already. But OpenSSL will not help you with downloading the CRL in the first place. Instead you would need to extract the CRL distribution point from the leaf certificate, download the CRL yourself and then you can use it.

至于OCSP OpenSSL提供了必要的API,可以使用构建OCSP请求并验证OCSP响应。你还是自己去弄明白这个OCSP请求发送到哪里。这需要通过解析叶证书和从权限信息访问字段提取OCSP URL再次完成。虽然有其余的API是不容易使用,并且大部分或完全未记录至少在OpenSSL版本1.1.0之前。我不知道一个好的,易于理解的示例,其中使用此API实现OCSP验证,但您可以查看 openssl ocsp 命令和其实施

As for OCSP OpenSSL provides the necessary API to build OCSP requests and validate OCSP responses. You are still on your own to figure out where to sent this OCSP request to. This needs to be done again by parsing the leaf certificate and extracting the OCSP URL from the Authority Information Access field. And while there is API for the rest it is not easy to use and was mostly or fully undocumented at least before OpenSSL version 1.1.0. I don't know of a good and easy to understand example where OCSP validation is implemented using this API but you might have a look at the openssl ocsp command and its implementation.

如果你编写一个客户端应用程序,并且只想接受一个单独的证书,你可以省略验证 PKI ,但仅检查证书是否是预期的证书,即证书锁定。有关此和示例代码的详细信息,请参见 OWASP:证书和公钥锁定

In case you write a client application and want to accept only a single certificate anyway you can omit all the complex steps of validating against a PKI but only check if the certificate is exactly the expected one, i.e. certificate pinning. See OWASP: Certificate and Public Key Pinning for more information about this and for sample code.

这篇关于客户端程序验证SSL_get_peer_certificate返回的服务器证书?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆