无法获得ssl / tls中的客户端证书

问题描述

我将尝试使用openssl进行相互身份验证的TLS。 但是,如下面的输出结果所示,客户端可以接收并输出服务器证书,但是服务器尚未收到客户端证书 我的工作细节如下。

服务器和客户端证书的生成(不通过CA进行证书签名,仅进行自签名)

(1)生成服务器密钥和证书。

$ openssl genrsa -des3 -out server.key 2048    
$ openssl req -new -key server.key -out server.csr   
$ cp server.key server.key.origin    
$ openssl rsa -in server.key.origin -out server.key    
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

(2)生成客户端密钥和证书。

$ openssl genrsa -des3 -out client.key 2048    
$ openssl req -new -key client.key -out client.csr    
$ cp client.key client.key.origin    
$ openssl rsa -in client.key.origin -out client.key    
$ openssl x509 -req -days 365 -in client.csr -signkey client.key -out client.crt

server.c

#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err,s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr);    exit(2); }

int main(void) {
    int err;
    int listen_sd;
    int sd;
    struct sockaddr_in sa_serv;
    struct sockaddr_in sa_cli;
    size_t client_len;

    SSL_CTX  *ctx;
    SSL    *ssl;
    X509                *client_cert;
    char                *str;
    char                buf[4096];
    SSL_METHOD  *meth;

    SSL_load_error_strings();
    SSLeay_add_ssl_algorithms();
    meth = TLSv1_2_server_method();
    ctx = SSL_CTX_new(meth);

    if(!ctx) {
        ERR_print_errors_fp(stderr);
        exit(2);
    }

    if(SSL_CTX_use_certificate_file(ctx,CERTF,SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }

    if(SSL_CTX_use_PrivateKey_file(ctx,KEYF,SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(4);
    }

    if(!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr,"Private key does not match the certificate public keyn");
        exit(5);
    }

    listen_sd = socket(AF_INET,SOCK_STREAM,0);
    CHK_ERR(listen_sd,"socket");

    memset(&sa_serv,0x00,sizeof(sa_serv));
    sa_serv.sin_family = AF_INET;
    sa_serv.sin_addr.s_addr = INADDR_ANY;
    sa_serv.sin_port = htons(1111);

    err = bind(listen_sd,(struct sockaddr*)&sa_serv,sizeof(sa_serv));
    CHK_ERR(err,"bind");

    err = listen(listen_sd,5);
    CHK_ERR(err,"listen");

    client_len = sizeof(sa_cli);
    sd = accept(listen_sd,(struct sockaddr*)&sa_cli,&client_len);
    CHK_ERR(sd,"accept");
    close(listen_sd);

    ssl = SSL_new(ctx);
    CHK_NULL(ssl);
    SSL_set_fd(ssl,sd);

    // to request client's certificate
    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);

    err = SSL_accept(ssl);
    CHK_SSL(err);

    printf("SSL connection using %s \n",SSL_get_cipher(ssl));

    client_cert = SSL_get_peer_certificate(ssl);

    if(client_cert != NULL) {
        printf("Client certificate: \n");

        str = X509_NAME_oneline(X509_get_subject_name(client_cert),0);
        CHK_NULL(str);
        printf("\t subject: %s\n",str);
        OPENSSL_free(str);

        str = X509_NAME_oneline(X509_get_issuer_name(client_cert),0);
        CHK_NULL(str);
        printf("\t issuer: %s\n",str);
        OPENSSL_free(str);

        X509_free(client_cert);
    } else {
        printf("Client does not have certificate. \n");
    }

    err = SSL_read(ssl,buf,sizeof(buf)-1);
    CHK_SSL(err);
    buf[err] = 0x00;
    printf("Got %d chars: %s \n",err,buf);

    err = SSL_write(ssl,"I hear you/",strlen("I hear you."));
    CHK_SSL(err);

    close(sd);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    return(0);
}

// client.c

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


#define CHK_NULL(x) if((x) == NULL) exit(1);

#define CHK_ERR(err,s) if((err) == -1) { perror(s); exit(1); }

#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }


int main(void) {
    int err;
    int sd;
    struct sockaddr_in sa;

    SSL_CTX   *ctx;
    SSL     *ssl;
    X509                    *server_cert;
    char                    *str;
    char                    buf[4096];
    SSL_METHOD    *meth;

    SSL_load_error_strings();

    SSLeay_add_ssl_algorithms();

    meth = TLSv1_2_client_method();

    ctx = SSL_CTX_new(meth);

    CHK_NULL(ctx);

    if(SSL_CTX_use_certificate_file(ctx,"./client.crt","./client.key",SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(4);
    }

    if(!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr,"Private key does not match the certificate public keyn");
        exit(5);
    }

    CHK_SSL(err);

    sd = socket(AF_INET,0);
    CHK_ERR(sd,"socket");

    memset(&sa,sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    sa.sin_port = htons(1111);

    err = connect(sd,(struct sockaddr*)&sa,sizeof(sa));
    CHK_ERR(err,"connect");

    ssl = SSL_new(ctx);
    CHK_NULL(ssl);

    SSL_set_fd(ssl,sd);
    err = SSL_connect(ssl);
    CHK_NULL(err);

    printf("SSL connection using %s \n",SSL_get_cipher(ssl));

    server_cert = SSL_get_peer_certificate(ssl);
    CHK_NULL(server_cert);
    printf("Server certificate: \n");

    str = X509_NAME_oneline(X509_get_subject_name(server_cert),0);
    CHK_NULL(str);
    printf("\t subject: %s \n",str);
    OPENSSL_free(str);

    str = X509_NAME_oneline(X509_get_issuer_name(server_cert),0);
    CHK_NULL(str);
    printf("\t issuer: %s \n",str);
    OPENSSL_free(str);

    X509_free(server_cert);

    err = SSL_write(ssl,"Hello World!",strlen("Hello World!"));
    CHK_SSL(err);

    err = SSL_read(ssl,sizeof(buf)-1);
    CHK_SSL(err);
    buf[err] = 0x0;
    printf("Got %d chars: %s \n",buf);
    SSL_shutdown(ssl);

    close(sd);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    return 0;
}

下面是输出结果。

(1)服务器

$ ./server  
SSL connection using ECDHE-RSA-AES256-GCM-SHA384
Client does not have certificate.
Got 12 chars: Hello World!

(2)客户

$ ./client
SSL connection using ECDHE-RSA-AES256-GCM-SHA384    
Server certificate:    
subject: /C=IN/ST=WB/L=Kolkata/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/[email protected]    
issuer: /C=IN/ST=WB/L=Kolkata/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/[email protected]    
Got 11 chars: I hear you/

我不知道为什么服务器的输出显示“客户端没有证书”。

尽管我在server.c中添加了“ SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL)”,但服务器未收到客户端的证书。

解决方法

ssl = SSL_new(ctx);
...

// to request client's certificate
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);

您从上下文生成SSL对象,然后更改上下文。这对创建的SSL对象没有影响,这意味着服务器不会首先请求客户端证书,因此客户端将不会提供证书。

将SSL_CTX_set_verify放在正确的位置后,您会注意到服务器将无法验证客户端证书。这是因为证书不是由服务器信任的CA签名的。有关如何设置受信任的根CA的信息,请参见SSL_CTX_load_verify_locations