【发布时间】:2016-06-10 12:18:15
【问题描述】:
我有一个客户端和一个服务器试图相互验证并启动 TLS 连接。我现在使用的证书是自签名的。
在服务器代码中我设置了SSL_VERIFY_FAIL_IF_NO_PEER_CERT。握手成功,但SSL_get_peer_certificate 在服务器端返回一个 NULL 指针。如果客户端没有返回证书,为什么握手没有失败?
如果我在服务器端注释掉SSL_get_peer_certificate 检查,客户端和服务器确实连接并且能够通信,但它不是 TLS 连接。当我看到他们通过wireshark交换数据包时,我只看到TCP流量。
服务器代码:
BIO *acceptTLSConnection(char *port) {
BIO *sbio, *bbio, *acpt = NULL;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
SSL_library_init();
ctx = SSL_CTX_new(TLSv1_server_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
if(!SSL_CTX_use_certificate_file(ctx,"servercert.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_use_PrivateKey_file(ctx,"serverkey.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stderr);
fatalError("Error setting up SSL_CTX.");
}
if(!SSL_CTX_load_verify_locations(ctx, "clientcert.pem", NULL))
fatalError("Could not load trusted CA certificates.");
sbio=BIO_new_ssl(ctx,0);
BIO_get_ssl(sbio, &ssl);
if(!ssl) {
fatalError("Can't locate BIO SSL pointer.");
}
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
bbio = BIO_new(BIO_f_buffer());
sbio = BIO_push(bbio, sbio);
acpt=BIO_new_accept(port);
BIO_set_accept_bios(acpt,sbio);
/* Setup accept BIO */
if(BIO_do_accept(acpt) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error in setting up accept BIO");
}
/* Now wait for incoming connection */
if(BIO_do_accept(acpt) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error in connection");
}
sbio = BIO_pop(acpt);
BIO_free_all(acpt);
if(BIO_do_handshake(sbio) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error in SSL handshake");
}
/* Verify a client certificate was presented during the negotiation */
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) fatalError("Client did not present a cert during handshake.");
/* Verify the result of chain verification */
int res = SSL_get_verify_result(ssl);
if(!(X509_V_OK == res)) fatalError("Cert presented by client couldn't be verified.");
return sbio;
}
客户端代码:
BIO *makeTLSConnection(char *servIP, char *servPort) {
char *servLoc = calloc(strlen(servIP) + strlen(servPort) + 2, sizeof(char));
strcat(servLoc, servIP);
strcat(servLoc, ":");
strcat(servLoc, servPort);
BIO *sbio = NULL;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
SSL_library_init();
ctx = SSL_CTX_new(TLSv1_client_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
if(!SSL_CTX_use_certificate_file(ctx,"clientcert.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_use_PrivateKey_file(ctx,"clientkey.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stderr);
fatalError("Error setting up SSL_CTX.");
}
if(!SSL_CTX_load_verify_locations(ctx, "servercert.pem", NULL))
fatalError("Could not load trusted CA certificates.");
sbio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(sbio, &ssl);
if(!ssl) {
fatalError("Can't locate SSL pointer.");
}
BIO_set_conn_hostname(sbio, servLoc);
if(BIO_do_connect(sbio) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error connecting to server.");
}
if(BIO_do_handshake(sbio) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error establishing SSL connection.");
}
/* Verify a server certificate was presented during the negotiation */
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) fatalError("Server did not present a cert during handshake.");
/* Verify the result of chain verification */
int res = SSL_get_verify_result(ssl);
if(!(X509_V_OK == res)) fatalError("Cert presented by server couldn't be verified.");
return sbio;
}
【问题讨论】:
-
好问题。您已满足必须在服务器上同时指定
SSL_VERIFY_PEER和SSL_VERIFY_FAIL_IF_NO_PEER_CERT的要求。如果您使用SSL_set_verify(在特定 SSL 会话上)而不是SSL_CTX_set_verify(在父上下文上),您能否复制结果。 -
@jww:是的,同样的结果。可能是我错误地生成并签署了我的证书吗?这里的书似乎一切都如此,以至于我想知道是否需要向下看一层。或者如果我的证书格式不正确,握手会返回错误吗?
-
我的错...如果可接受的证书可用,则让客户端发送其证书的服务器端函数是
SSL_CTX_set_client_CA_list。