TLS服务器未从客户端收到证书,无法终止握手。

4
我有一个客户端和服务器试图相互认证并启动TLS连接。我目前使用的证书是自签名的。
在服务器代码中,我设置了 SSL_VERIFY_FAIL_IF_NO_PEER_CERT。握手成功,但是服务器端的 SSL_get_peer_certificate 返回一个空指针。如果客户端没有返回证书,为什么握手没有失败呢?
如果在服务器端注释掉 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;
}

1
好问题。您已经满足了服务器上必须指定SSL_VERIFY_PEERSSL_VERIFY_FAIL_IF_NO_PEER_CERT的要求。如果您使用SSL_set_verify(在特定的SSL会话上)而不是SSL_CTX_set_verify(在父上下文中)能否复制结果? - jww
@jww:是的,结果一样。我生成和签署证书时可能出了问题吗?这里的一切都看起来很规范,所以我在想是否需要深入研究。如果我的证书存在某些问题,握手会返回错误吗? - Mike Goldin
我的错...让客户端发送其证书(如果有可接受的证书)的服务器端函数是SSL_CTX_set_client_CA_list - jww
1个回答

4
看起来你在服务器上缺少调用 SSL_CTX_use_certificate_chain_fileSSL_CTX_set_client_CA_list。 我相信 SSL_CTX_set_client_CA_list 触发了执行客户端身份验证的机制(即,在交换过程中,它引出了 RFC 中第7.4.6节中的 ClientCertificate 消息)。
如果 SSL_CTX_set_client_CA_list 导致了你正在寻找的故障,那么我倾向于认为这是 OpenSSL 库中的错误。当在服务器上指定 SSL_VERIFY_PEERSSL_VERIFY_FAIL_IF_NO_PEER_CERT 时,应该会出现失败,因为文档是这样说的。
另请参见 OpenSSL 用户邮件列表中的 Is verification supposed to fail with SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT without SSL_CTX_set_client_CA_list 。 这是对此问题的回答。
此外,请参见 Testing SSL/TLS Client Authentication with OpenSSLOpenSSL client not sending client certificate

事实证明,这是由于错误的证书问题,但是您的指导也是正确的,并帮助我调试了真正的问题。 - Mike Goldin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接