101
needs to copy the data from the fourth parameter to the first. This method is viable for applications that need to decrypt keys during normal operation in which constant user prompting is a nuisance.
An unlimited number of PEM-encoded items can be stored in a file, but only one DER item may be stored in a file. Also, different types of PEM
items may be stored within a single file. As a result, if the private key is kept in PEM encoding, it can be appended to the certificate chain file, and
the same filename can be used for the calls to
SSL_CTX_use_certificate_chain_file
and
SSL_CTX_use_PrivateKey_file
. This trick is used in Example 5-5
. PEM and DER encodings are discussed in
Chapter 8 .
At this stage, we will limit our discussion to the process of providing certificate information to the peer, rather than discussing the processes of validation. The validation problem will be discussed
in Step 2.
5.1.2.3 Our example extended
Using the same example applications that weve already provided, lets modify them with what weve learned about making SSL connections. Keep in mind that this example is not yet secure. It
does not validate anything about the peer to which it connects; it merely provides the authentication information. The new version of our client in client1.c is shown in
Example 5-5 .
The bold lines are those that we have added or changed.
Example 5-5. client1.c
1 include common.h 2
3 define CERTFILE client.pem 4 SSL_CTX setup_client_ctxvoid
5 { 6 SSL_CTX ctx;
7 8 ctx = SSL_CTX_newSSLv23_method;
9 if SSL_CTX_use_certificate_chain_filectx, CERTFILE = 1 10 int_errorError loading certificate from file;
11 if SSL_CTX_use_PrivateKey_filectx, CERTFILE, SSL_FILETYPE_PEM = 1
12 int_errorError loading private key from file; 13 return ctx;
14 } 15
16 int do_client_loopSSL ssl 17 {
18 int err, nwritten; 19 char buf[80];
20 21 for ;;
22 { 23 if fgetsbuf, sizeofbuf, stdin
24 break; 25 for nwritten = 0; nwritten sizeofbuf; nwritten +=
err 26 {
27 err = SSL_writessl, buf + nwritten, strlenbuf - nwritten;
28 if err = 0
102
29 return 0; 30 }
31 } 32 return 1;
33 } 34
35 int mainint argc, char argv[] 36 {
37 BIO conn; 38 SSL ssl;
39 SSL_CTX ctx; 40
41 init_OpenSSL; 42 seed_prng;
43 44 ctx = setup_client_ctx;
45 46 conn = BIO_new_connectSERVER : PORT;
47 if conn 48 int_errorError creating connection BIO;
49 50 if BIO_do_connectconn = 0
51 int_errorError connecting to remote machine; 52
53 if ssl = SSL_newctx 54 int_errorError creating an SSL context;
55 SSL_set_biossl, conn, conn; 56 if SSL_connectssl = 0
57 int_errorError connecting SSL object; 58
59 fprintfstderr, SSL Connection opened\n; 60 if do_client_loopssl
61 SSL_shutdownssl; 62 else
63 SSL_clearssl; 64 fprintfstderr, SSL Connection closed\n;
65 66 SSL_freessl;
67 SSL_CTX_freectx; 68 return 0;
69 }
In this example, we make a call to the function
seed_prng
. As its name suggests, this function seeds the OpenSSL PRNG. Its implementation is left out; see
Chapter 4 for details on an
appropriate implementation. It is very important to maintaining the security of SSL for the PRNG to be properly seeded, so this function should never be left out in real-world applications.
The function
setup_client_ctx
performs the actions as discussed earlier to provide certificate data to the server properly. In this setup process, we leave out any calls to
SSL_CTX_set_default_passwd_cb
since the default OpenSSL passphrase callback is acceptable for our purposes. The only other point of interest is the error checking performed. This
example prints errors and exits if anything goes wrong; a more robust error handling technique was left out for clarity. The sidebar contains more information about the contents of the file
client.pem .
Generating the Files Needed by the Examples
Our example programs refer to several filenames that contain PEM-encoded certificates
103
and keys. In this sidebar, well describe the process of creating each of them. There are two certificates used, the files server.pem and client.pem. In addition, we have a trusted
root certificate in the file root.pem. As the example develops, we will also require two other files that will contain DH parameters dh512.pem and dh1024.pem. All of the
files were generated using the command-line tool described in
Chapter 2 .
Before moving into the details of the commands themselves, we should first describe our certificate hierarchy. As weve stated, there is just one trusted certificate: the root
CAs. This certificate is self-signed, as are all root CA certificates. This root CA represents the CA for a company. The qualifications for peer certificate validation for
both our example client and server will be simply to verify that the other has been signed by the root CA. To demonstrate how a chain can grow and still be verifiable, we
will create a server CA. This CA will be signed by the root CA and it, in turn, will be used to sign all server identity certificates. The client certificates, on the other hand, will
be signed directly by the root CA. Nothing prevents us from making the hierarchy arbitrarily complex, but weve left just one intermediate CA to demonstrate how to do it.
Creating more intermediates would follow the same pattern, as well see.
The first command shown in each example below generates a certificate signing request. In creating the request, the command-line utility will prompt the user for the contents of
the data fields that will be put in the request. The values we typed in are printed when the last command in each example runs. The values that arent seen are those of the
subjectAltName
field. We embed the server and client fully qualified domain name FQDN in the respective certificates
commonName
field, and also in the
dNSName
field of the
subjectAltName
. The latter is done by changing the configuration file to include subjectAltName = DNS:FQDN under the certificate extensions section the
usr_cert section; the configuration file is the default aside from this change. To create the root CA:
openssl req -newkey rsa:1024 -sha1 -keyout rootkey.pem -out rootreq.pem
openssl x509 -req -in rootreq.pem -sha1 -extfile myopenssl.cnf \
-extensions v3_ca -signkey rootkey.pem -out rootcert.pem cat rootcert.pem rootkey.pem root.pem
openssl x509 -subject -issuer -noout -in root.pem subject= C=USST=VAL=FairfaxO=Zork.orgCN=Root CA
issuer= C=USST=VAL=FairfaxO=Zork.orgCN=Root CA
To create the server CA and sign it with the root CA:
openssl req -newkey rsa:1024 -sha1 -keyout serverCAkey.pem - out \
serverCAreq.pem openssl x509 -req -in serverCAreq.pem -sha1 -extfile \
myopenssl.cnf -extensions v3_ca -CA root.pem -CAkey root.pem \
-CAcreateserial -out serverCAcert.pem cat serverCAcert.pem serverCAkey.pem rootcert.pem
serverCA.pem openssl x509 -subject -issuer -noout -in serverCA.pem
subject= C=USST=VAL=FairfaxO=Zork.orgOU=Server DivisionCN=Server CA
issuer= C=USST=VAL=FairfaxO=Zork.orgCN=Root CA
104
To create the servers certificate and sign it with the Server CA:
openssl req -newkey rsa:1024 -sha1 -keyout serverkey.pem - out \
serverreq.pem openssl x509 -req -in serverreq.pem -sha1 -extfile
myopenssl.cnf \ -extensions usr_cert -CA serverCA.pem -CAkey serverCA.pem \
-CAcreateserial -out servercert.pem cat servercert.pem serverkey.pem serverCAcert.pem
rootcert.pem \ server.pem
openssl x509 -subject -issuer -noout -in server.pem subject= C=USST=VAL=FairfaxO=Zork.orgCN=splat.zork.org
issuer= C=USST=VAL=FairfaxO=Zork.orgOU=Server DivisionCN=Server CA
To create the client certificate and sign it with the Root CA
openssl req -newkey rsa:1024 -sha1 -keyout clientkey.pem - out \
clientreq.pem openssl x509 -req -in clientreq.pem -sha1 -extfile
myopenssl.cnf \ -extensions usr_cert -CA root.pem -CAkey root.pem \
-CAcreateserial -out clientcert.pem cat clientcert.pem clientkey.pem rootcert.pem client.pem
openssl x509 -subject -issuer -noout -in client.pem subject= C=USST=VAL=FairfaxO=Zork.orgCN=shell.zork.org
issuer= C=USST=VAL=FairfaxO=Zork.orgCN=Root CA
To create dh512.pem and dh1024.pem:
openssl dhparam -check -text -5 512 -out dh512.pem openssl dhparam -check -text -5 1024 -out dh1024.pem
Lines 54-58 create the
SSL
object and connect it. There are some functions used here that we have not yet discussed. The call to
SSL_new
creates our
SSL
object and copies the settings weve already placed in the
SSL_CTX
to the newly created object. At this point, the SSL object is still in a generic state. In other words, it could play the role of the server or the client in an SSL
handshake. Another factor left unspecified is the path of communications for the
SSL
object. Since
SSL
objects are flexible in the sense that they can perform SSL functions on top of many different types of IO methods, we must specify a
BIO
for our object to use. Line 56 does this through a call to
SSL_set_bio
. This function is passed our connection
BIO
twice since SSL objects are robust enough to operate on two one-way IO types instead of requiring a single full-duplex IO method.
Basically, we must specify the BIO to use for writing separately from the BIO used for reading. In this case, they are the same object, since sockets allow two-way communication.
The last unfamiliar function used here is
SSL_connect
. This function causes the SSL object to initiate the protocol using the underlying IO. In other words, it begins the SSL handshake with the
application on the other end of the underlying
BIO
. This function will return an error for problems such as incompatible protocol versions.
105
The
do_client_loop
function is almost identical to that of our non-SSL client. Weve simply changed the parameter to an
SSL
object instead of a
BIO
, and the
BIO_write
becomes an
SSL_write
. In addition, weve added a return value to this function. If no errors occur, we can call
SSL_shutdown
to stop the SSL connection; otherwise, we call
SSL_clear
. This is done to force OpenSSL to remove any session with errors from the session cache. We will look at session
caching in more detail later in this chapter, but for now, it is worth noting that session caching is effectively disabled in the examples weve provided so far.
The last point to make about this example is that we removed the call to
BIO_free
. This is done because
SSL_free
automatically frees the SSL objects underlying
BIO
s for us. Example 5-6
has the contents of server1.c, the file containing the implementation of our SSL- enabled server. Again, it isnt yet secure since it validates nothing about the peer; it simply
provides its certificate information to the client.
Example 5-6. server1.c
1 include common.h 2
3 define CERTFILE server.pem 4 SSL_CTX setup_server_ctxvoid
5 { 6 SSL_CTX ctx;
7 8 ctx = SSL_CTX_newSSLv23_method;
9 if SSL_CTX_use_certificate_chain_filectx, CERTFILE = 1 10 int_errorError loading certificate from file;
11 if SSL_CTX_use_PrivateKey_filectx, CERTFILE, SSL_FILETYPE_PEM = 1
12 int_errorError loading private key from file; 13 return ctx;
14 } 15
16 int do_server_loopSSL ssl 17 {
18 int err, nread; 19 char buf[80];
20 21 do
22 { 23 for nread = 0; nread sizeofbuf; nread += err
24 { 25 err = SSL_readssl, buf + nread, sizeofbuf -
nread; 26 if err = 0
27 break; 28 }
29 fwritebuf, 1, nread, stdout; 30 }
31 while err 0; 32 return SSL_get_shutdownssl SSL_RECEIVED_SHUTDOWN ? 1 :
0; 33 }
34 35 void THREAD_CC server_threadvoid arg
36 { 37 SSL ssl = SSL arg;
38 39 ifndef WIN32
106
40 pthread_detachpthread_self; 41 endif
42 if SSL_acceptssl = 0 43 int_errorError accepting SSL connection;
44 fprintfstderr, SSL Connection opened\n; 45 if do_server_loopssl
46 SSL_shutdownssl; 47 else
48 SSL_clearssl; 49 fprintfstderr, SSL Connection closed\n;
50 SSL_freessl; 51
52 ERR_remove_state0; 53
54 ifdef WIN32 55 _endthread;
56 endif 57 }
58 59 int mainint argc, char argv[]
60 { 61 BIO acc, client;
62 SSL ssl; 63 SSL_CTX ctx;
64 THREAD_TYPE tid; 65
66 init_OpenSSL; 67 seed_prng;
68 69 ctx = setup_server_ctx;
70 71 acc = BIO_new_acceptPORT;
72 if acc 73 int_errorError creating server socket;
74 75 if BIO_do_acceptacc = 0
76 int_errorError binding server socket; 77
78 for ;; 79 {
80 if BIO_do_acceptacc = 0 81 int_errorError accepting connection;
82 83 client = BIO_popacc;
84 if ssl = SSL_newctx 85 int_errorError creating SSL context;
86 87 SSL_set_biossl, client, client;
88 THREAD_CREATEtid, server_thread, ssl; 89 }
90 91 SSL_CTX_freectx;
92 BIO_freeacc; 93 return 0;
94 }
After looking at the new client program, the modifications to the server should be clear. Since this is the server side of the SSL negotiation, a different function call is made:
SSL_accept
. The
SSL_accept
function initiates communication on the underlying IO layer to perform the SSL handshake.
107
In the
do_server_loop
function, we use a call to
SSL_get_shutdown
to check into the error status of the SSL object. This essentially allows us to differentiate normal client terminations from
actual errors. If the
SSL_RECEIVED_SHUTDOWN
flag is set, we know the session hasnt had an error and its safe to cache. In other words, we can call
SSL_shutdown
rather than simply clear the connection. The remainder of the modifications to the server program parallel those made to
the client. In summary, weve taken our applications and built them to the point of creating the objects
necessary for SSL connections. Each application does provide its certificate data to the peers to which it connects, but they do not verify the certificates they receive. We will build further in the
next step and learn how to validate peer certificates.
5.1.3 Step 2: Peer Authentication