1

I am using OpenSSL API for C++. Communication is between an embedded linux device (SSL server) and Windows software (SSL client).

I want to ensure that the intended server and client will only speak to one another. I have generated a root key for the server, along with the following:

  • Root CA (used by client to authorize server)
  • server certificate
  • server private key

My SSL connection works fine when only authorizing the server certificate during handshaking.

However, I also want to verify client authenticity, so I generated another root key for the client, along with the following:

  • Root CA (used by server to authorize client)
  • client certificate
  • client private key

Using the code below, my server fails to accept the client connection due to the following error:

724428760:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:3291:

Here is my server code related to SSL certificates:

bool SSLServer::loadCertificates(const char * sCertFile,
                                 const char * sKeyFile,
                                 const char * sCAFile)
{
    // set server certificate
    if (SSL_CTX_use_certificate_file(_pCTX, sCertFile, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }

    // set the private key
    if (SSL_CTX_use_PrivateKey_file(_pCTX, sKeyFile, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }

    // verify private key
    if (!SSL_CTX_check_private_key(_pCTX))
    {
        qWarning() << "Private key does not match the public certificate.";
        return false;
    }

    SSL_CTX_set_verify(_pCTX, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);

    // load the trusted client CA certificate into context
    if (SSL_CTX_load_verify_locations(_pCTX, sCAFile, NULL) != 1)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }

    // allow this CA to be sent to the client during handshake
    STACK_OF(X509_NAME) * list = SSL_load_client_CA_file(sCAFile);
    if (NULL == list)
    {
        qWarning() << "Failed to load SSL client CA file.";
        return false;
    }
    SSL_CTX_set_client_CA_list(_pCTX, list);
    SSL_CTX_set_verify_depth(_pCTX, 1);

    return true;
}

And here is my client code:

bool SSLClient::LoadCertificates(const char * sCAFile,
                                 const char * sClientCertFile,
                                 const char * sClientKeyFile)
{
    ASSERT(NULL != sCAFile && NULL != sClientCertFile && NULL != sClientKeyFile);

    // load RSA CA certificate into context to let client verify server's authenticity
    // (will be used with server certificate and private key)
    if (!SSL_CTX_load_verify_locations(_pCTX, sCAFile, NULL))
    {
        ERR_print_errors_fp(stderr);
        return false;
    }

    // load client certificate into context to let server verify client's authenticity
    // (will be used with server's RSA CA certificate)
    if (SSL_CTX_use_certificate_file(_pCTX, sClientCertFile, SSL_FILETYPE_PEM) != 1)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }

    // load client certificate private key into context
    if (SSL_CTX_use_PrivateKey_file(_pCTX, sClientKeyFile, SSL_FILETYPE_PEM) != 1)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }

    // verify that client cert and private key match
    if (!SSL_CTX_check_private_key(_pCTX))
    {
        OutputDebugString("Private key does not match the certificate public key\n");
        return false;
    }

    // require server certificate verification
    SSL_CTX_set_verify(_pCTX, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    SSL_CTX_set_verify_depth(_pCTX, 1);

    return true;
}

Again, it works completely fine if I remove the code related to verifying client certificate. Am I missing something, or doing something completely wrong?

schumacher574
  • 1,081
  • 1
  • 14
  • 32
  • 1
    At the first look the code likes good except that SSL_VERIFY_FAIL_IF_NO_PEER_CERT has no effect on the client side. Are you sure that you are really calling SSLClient::LoadCertificates in the rest of your code? – Steffen Ullrich Jan 10 '15 at 15:07
  • @SteffenUllrich Yes, I'm definitely calling LoadCertificates. I've tried with and without SSL_VERIFY_FAIL_IF_NO_PEER_CERT...seems to have no effect. Do I need to do anything to allow the OS (Windows and/or Linux) to work with the certificates, even though I'm specifically stating which certificates to work with in my code? – schumacher574 Jan 10 '15 at 18:48
  • The client should just send back whatever certificate is configured. The check if this certificate is valid is done by the server. But from the error message it looks like the server is not getting a certificate and not that the verification failed. Could you confirm this with a packet capture (wireshark etc)? – Steffen Ullrich Jan 10 '15 at 19:51
  • In server code sCAFile points to the CA of the client certificate? – Vladimir Kunschikov Jan 10 '15 at 19:56
  • @VladimirKunschikov yes that is correct, in server code sCAFile points to CA of the client certificate. – schumacher574 Jan 11 '15 at 04:09
  • @SteffenUllrich I wont be able to try until Monday but I will verify using wireshark ASAP...is it possible that a mismatch in OpenSSL versions could cause this issue? – schumacher574 Jan 11 '15 at 04:10
  • Different OpenSSL versions should not be a problem. – Steffen Ullrich Jan 11 '15 at 04:37
  • Your code is absolutely correct. I've copied it to simple sample server/client app and handshake went without problems. Just two minor improvements: I've added callbacks which prints ssl diagnostic messages and password callback, added openssl initialization. I think when you are generating certificates you are just hitting enter on all questions and run into problem described here: http://stackoverflow.com/questions/19726138/openssl-error-18-at-0-depth-lookupself-signed-certificate – Vladimir Kunschikov Jan 13 '15 at 07:18
  • @SteffenUllrich The client can only send back a certificate signed by the CAs that the server provided in the `CertificateRequest` message. If the client doesn't have such a certificate it will send nothing. – user207421 Jan 13 '15 at 09:53
  • @EJP: just checked with openssl: the client can send any certificates back it wants, the list of acceptable CA names is just a hint which certificate to pick. – Steffen Ullrich Jan 14 '15 at 20:02

2 Answers2

3

Working copy of your code along with certificates: http://files.webfile.ru/567c28b8973091cbdad036f3e43e989b

Exactly your problem can be reproduced if generate certificates just hitting 'enter' answering questions. You'll got 'self-signed' certificate without any intention to make it. Problem exactly like OpenSSL - error 18 at 0 depth lookup:self signed certificate When snooping ssl problems you should use not wireshark but ssldump.

Community
  • 1
  • 1
Vladimir Kunschikov
  • 1,735
  • 1
  • 16
  • 16
  • I added your callbacks to my code and tried with your certificates and everything worked fine. After, I was curious, so I tried again with my certificates and your callbacks, and surprising everything worked. Still trying to figure out why this is the case... – schumacher574 Jan 13 '15 at 23:36
  • I'm starting to think I did something as stupid as not copying the certificates to the client/server correctly. Even after removing the callbacks, everything works...the only thing I can think that is different is I recopied the certificates to both the server and client via scp... I feel a little dumb, but thank you very much for the help, and your code/certs definitely helped me find this mistake. – schumacher574 Jan 13 '15 at 23:47
  • is there any chance you guys put that code link back up. I am getting the same error. Thanks a bunch – Evren Bingøl Oct 24 '17 at 05:34
  • @EvrenBingøl sorry for not answering. I can add that code, if problem is still actual – Vladimir Kunschikov Nov 03 '17 at 10:13
  • @Vladimir Kunschikov. Thanks a bunch. I found out that was going on with mine. My issue was due to latest chrome certificates verification, where chrome wanted to have the DNS "SAN" fields in the certs and having to match the CN field to domain was not enough. Thanks anyways. – Evren Bingøl Nov 04 '17 at 13:42
3

724428760:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:3291:

The error message is misleading. While it says that the client did not return any certificates it happens also, if the client sends a certificate which the server can not validate. Please make sure, that the certificate sent by the client can actually be verified against the servers sCAFile.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Definitely a misleading error message...appears I was having some sort of invalid certificate issue due to incorrectly deploying my certs. Thanks for the help. – schumacher574 Jan 15 '15 at 07:11