10.7 Verifying an SSL Peer's Certificate

10.7.1 Problem

You are using OpenSSL to support SSL-enabled communication between a client and a server. You want to instruct OpenSSL to verify the certificate received from the peer.

10.7.2 Solution

Every SSL connection has an SSL object, which in turn has an SSL_CTX object, and that object, in turn, has an X509_STORE object. OpenSSL uses the X509_STORE object as a container for any certificates and CRLs required to verify another certificate. OpenSSL creates an X509_STORE_CTX object and calls X509_verify_cert( ) for you, but not by default.

OpenSSL's default behavior is to not verify peer certificates, which is the worst default behavior that any SSL implementation could possibly provide. By not verifying certificates in an SSL connection, the strength of the security provided by SSL is severely reduced, to the point where the two parties in the conversation might as well be using nothing more than a symmetric cipher with keys exchanged in the clear. Without verifying certificates, you will have security against passive eavesdroppers, but that is all. With a small amount of effort, anyone could hijack the TCP connection before the SSL session is established and act as a man-in-the-middle.

10.7.3 Discussion

To have OpenSSL verify a peer's certificate, you must issue a call to SSL_CTX_set_verify( ). SSL_CTX_set_verify( ) accepts a bitmask of flags that tell OpenSSL how to deal with certificates. Depending on whether the SSL_CTX object is being used as a client or as a server, the meanings of the flags are somewhat different:

SSL_VERIFY_NONE

When the SSL_CTX object is being used in server mode, no request for a certificate is sent to the client, and the client should not send a certificate.

When the SSL_CTX object is being used in client mode, any certificate received from the server will be verified, but failure will not terminate the handshake.

This flag should never be combined with any of the others, and it should normally be used only in server mode (if it is ever used at all). When operating in client mode, you should always be verifying the server's certificate. When operating in server mode, you may not have any use for a client certificate, and requesting one may cause confusion for users. For example, if an SSL-enabled web site requests a certificate from a client, the user's browser may ask the user for a certificate to send to the server.

SSL_VERIFY_PEER

When the SSL_CTX object is being used in server mode, a request for a certificate will be sent to the client. The client may opt to ignore the request, but if a certificate is sent back, it will be verified. If the verification fails, the handshake will be terminated immediately.

When the SSL_CTX object is being used in client mode, if the server sends a certificate, it will be verified. If the verification fails, the handshake will be terminated immediately. The only time that a server would not send a certificate is when an anonymous cipher is in use. Anonymous ciphers are disabled by default. Any other flags combined with this one in client mode are ignored.

SSL_VERIFY_FAIL_IF_NO_PEER_CERT

If the SSL_CTX object is not being used in server mode or if SSL_VERIFY_PEER is not set, this flag is ignored. Use of this flag will cause the handshake to terminate immediately if the client provides no certificate.

SSL_VERIFY_CLIENT_ONCE

If the SSL_CTX object is not being used in server mode, or if SSL_VERIFY_PEER is not set, this flag is ignored. Use of this flag will prevent the server from requesting a certificate from the client in the case of a renegotiation. A certificate will still be requested during the initial handshake.

Using this knowledge of SSL_CTX_set_verify( ) and the code from Recipe 10.5, we'll build a new function, spc_create_sslctx( ), that will create an SSL_CTX object and initialize it with secure settings. In addition to calling SSL_CTX_set_verify( ), we'll disable the SSLv2 protocol, leaving only SSLv3 and TLSv1 enabled. We want to disable SSLv2 because it is well known to be insecure. It was the first publicly released version of the protocol and was not designed or adequately reviewed by security experts before its deployment. SSLv3 was designed and reviewed by security experts, and it corrects all of the known problems in SSLv2. Finally, we'll call SSL_CTX_set_cipher_list( ) to ensure that only secure ciphers will be used.

Before we can build spc_create_sslctx( ), we need to extend and complete the implementation of the spc_x509store_t object introduced in Recipe 10.5. Some additional flags are necessary for spc_create_sslctx( ), so we'll define those first:

SPC_X509STORE_USE_CERTIFICATE

If this flag is set, an SSL_CTX created by spc_create_sslctx( ) will be loaded with a private key and certificates to be sent to the peer if they're requested. This should always be set for a server context, but it may also be set for a client context.

SPC_X509STORE_SSL_VERIFY_NONE

This flag corresponds to OpenSSL's SSL_VERIFY_NONE flag and is used to construct the flags that are passed in the call to SSL_CTX_set_verify( ) by spc_create_sslctx( ).

SPC_X509STORE_SSL_VERIFY_PEER

This flag corresponds to OpenSSL's SSL_VERIFY_PEER flag and is used to construct the flags that are passed in the call to SSL_CTX_set_verify( ) by spc_create_sslctx( ).

SPC_X509STORE_SSL_VERIFY_FAIL_IF_NO_PEER_CERT

This flag corresponds to OpenSSL's SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag and is used to construct the flags that are passed in the call to SSL_CTX_set_verify( ) by spc_create_sslctx( ).

SPC_X509STORE_SSL_VERIFY_CLIENT_ONCE

This flag corresponds to OpenSSL's SSL_VERIFY_CLIENT_ONCE flag and is used to construct the flags that are passed in the call to SSL_CTX_set_verify( ) by spc_create_sslctx( ).

SPC_X509STORE_SSL_VERIFY_MASK

This is simply a combination of all the SSL verification flags that is intended for internal use only.

We will also need an additional set of functions to add certificate and key information into the context for presenting to a peer when it is requested. The information will be used by spc_create_sslctx( ) when creating an SSL_CTX object, but only if SPC_X509STORE_USE_CERTIFICATE is set in the spc_x509store_t's flags.

void spc_x509store_setusecertfile(spc_x509store_t *spc_store, char *file) {
  if (spc_store->use_certfile) free(spc_store->use_certfile);
  spc_store->use_certfile = (file ? strdup(file) : 0);
}
   
void spc_x509store_addusecert(spc_x509store_t *spc_store, X509 *cert) {
  sk_X509_push(spc_store->certs, cert);
}
   
void spc_x509store_setusekeyfile(spc_x509store_t *spc_store, char *file) {
  if (spc_store->use_keyfile) free(spc_store->use_keyfile);
  spc_store->use_keyfile = (file ? strdup(file) : 0);
}
   
void spc_x509store_setusekey(spc_x509store_t *spc_store, EVP_PKEY *key) {
  if (spc_store->use_key) EVP_PKEY_free(key);
  spc_store->use_key = key;
  CRYPTO_add(&(key->references), 1, CRYPTO_LOCK_EVP_PKEY);
}

Both the certificates and the keys can be specified either as a file from which to load the information, or as preexisting OpenSSL objects of the appropriate type (X509 objects for certificates, and EVP_PKEY objects for keys). If a filename is specified, it will take precedence over a preexisting OpenSSL object. If a preexisting key object is used, it is the caller's responsibility to free it using EVP_PKEY_free( ) at any point after it is added into the spc_x509store_t object because it is reference counted, and spc_x509store_setusekey( ) increments its reference count.

When specifying the certificates to be sent to a peer (whether the peer will be a server or a client), multiple certificates may be specified. The first certificate specified should always be the certificate belonging to your program. Any additional certificates should be certificates in the chain that may be needed to verify the validity of your own certificate. This is true whether the certificates are loaded from a file and specified via spc_x509store_setusecertfile( ), or are added to the spc_x509store_t one at a time using spc_x509store_addusecert( ). Note also that the certificates and the required private key may be contained within the same file. For both certificate and key files, PEM format should be used, because the alternative binary ASN.1 format (also known as DER) does not allow multiple objects to be present in the same file.

At this point, spc_create_sslctx( ) has everything it needs. It takes a single argument?the spc_x509store_t object?to get its information from, and it returns a new SSL_CTX object that can be used to establish SSL-enabled connections.

#include <openssl/ssl.h>

#define SPC_X509STORE_USE_CERTIFICATE                 0x04
#define SPC_X509STORE_SSL_VERIFY_NONE                 0x10
#define SPC_X509STORE_SSL_VERIFY_PEER                 0x20
#define SPC_X509STORE_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40
#define SPC_X509STORE_SSL_VERIFY_CLIENT_ONCE          0x80
#define SPC_X509STORE_SSL_VERIFY_MASK                 0xF0
   
SSL_CTX *spc_create_sslctx(spc_x509store_t *spc_store) {
  int                       i, verify_flags = 0;
  SSL_CTX                   *ctx = 0;
  X509_STORE                *store = 0;
  spc_x509verifycallback_t  verify_callback;
   
  if (!(ctx = SSL_CTX_new(SSLv23_method(  )))) goto error_exit;
  if (!(store = spc_create_x509store(spc_store))) goto error_exit;
  SSL_CTX_set_cert_store(ctx, store);  store = 0;
  SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
  SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
   
  if (!(verify_callback = spc_store->callback))
    verify_callback = spc_verify_callback;
  if (!(spc_store->flags & SPC_X509STORE_SSL_VERIFY_MASK))
    verify_flags = SSL_VERIFY_NONE;
  else {
    if (spc_store->flags & SPC_X509STORE_SSL_VERIFY_NONE)
      verify_flags |= SSL_VERIFY_NONE;
    if (spc_store->flags & SPC_X509STORE_SSL_VERIFY_PEER)
      verify_flags |= SSL_VERIFY_PEER;
    if (spc_store->flags & SPC_X509STORE_SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
      verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
    if (spc_store->flags & SPC_X509STORE_SSL_VERIFY_CLIENT_ONCE)
      verify_flags |= SSL_VERIFY_CLIENT_ONCE;
  }
  SSL_CTX_set_verify(ctx, verify_flags, verify_callback);
   
  if (spc_store->flags & SPC_X509STORE_USE_CERTIFICATE) {
    if (spc_store->use_certfile)
      SSL_CTX_use_certificate_chain_file(ctx, spc_store->use_certfile);
    else {
      SSL_CTX_use_certificate(ctx, sk_X509_value(spc_store->use_certs, 0));
      for (i = 1; i < sk_X509_num(spc_store->use_certs); i++) {
        SSL_CTX_add_extra_chain_cert(ctx, sk_X509_value(spc_store->use_certs, i));
      } 
    }
    if (spc_store->use_keyfile) {
      SSL_CTX_use_PrivateKey_file(ctx, spc_store->use_keyfile, SSL_FILETYPE_PEM);
    } else { 
      if (spc_store->use_key)
        SSL_CTX_use_PrivateKey(ctx, spc_store->use_key);
    } 
  }
   
  SSL_CTX_set_app_data(ctx, spc_store);
  return ctx;
   
error_exit:
  if (store) X509_STORE_free(store);  /* not ref counted */
  if (ctx) SSL_CTX_free(ctx);         /* ref counted */
  return 0;
}

10.7.4 See Also

Recipe 10.5