# The SSL socket class

class SWSSLSocket
  Constructor
  enum ssl_error
  class SWSSLError
  use_cert()
  use_cert_passwd()
  use_cert_cb()
  use_DHfile()
  use_verification()
  enum verify_result
  struct verify_info
  get_verify_result()
  struct peerCert_info
  get_peerCert_info()
  get_peerCert_PEM()
Typical Use
  Server side example
  Client side example

## SWSSLSocket

Derived (public) from SWInetSocket.
This class implements TCP/IP sockets with the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols. The OpenSSL library is used in this implementation, see the OpenSSL homepage for more information.
Constructor:

> SWSSLSocket(block_type block=blocking, int keysize=RSA_KEYSIZE)
> You can specify the blocking mode for this socket, see enum block_type for more information. You can also specify the keysize for generated certificates (default is 512bit).

Methods and datatypes:

> enum ssl_error{badPasswd, badFile}
> - badPasswd - Wrong password for private key.
> - badFile - Couldn't access or use file.
> class SWSSLError
> Derived (public) from SWBaseError. Should be used as SWBaseError but can also be compared to SSL specific errors (enum ssl_error).
> bool use_cert(const char *cert_file, const char *private_key_file, SWBaseError *error = NULL)
> Load a certificate. A socket used on the server side needs to have a certificate, but a temporary RSA session certificate will be created if you don't load one yourself. Every server side socket in your application that doesn't load a certificate will have the same session certificate (a global certificate will be created the first time one is needed). The keysize of the global certificate is defined by the constructor of the first server side SSL socket that calls accept() without loading a certificate first. You should try to avoid temporary certificates and create one yourself and use this method to load it. OpenSSL has a nice script to do this located at /usr/lib/ssl/misc/CA.sh (use -newcert as argument) as default. The client side can also load certificates but it is not required. The files should be in ASCII PEM format and the certificate and the private key can either be in the same file or two separate files. OpenSSL's standard password prompt will be used if the private key uses a password. You should load the certificate before calling accept() or connect(). Returns true on success. Possible SSL error types: badFile and badPasswd. Possible normal error types: fatal.
> bool use_cert_passwd(const char *cert_file, const char *private_key_file, std::string passwd, SWBaseError *error = NULL)
> As use_cert() but you can also specify the password for the private_key_file (and avoid OpenSSL's standard prompt).
> bool use_cert_cb(const char *cert_file, const char *private_key_file, int passwd_cb(char *buf, int size, int rwflag, void *userdata), char *userdata = NULL, SWBaseError *error = NULL)
> As use_cert() but you can specify a password callback given to OpenSSL that hands back the password to be used during decryption. On invocation a pointer to userdata is provided. The function pointed to by "passwd_cb" must write the password into the provided buffer "buf" which is of size "size". The actual length of the password must be returned to the calling function. The flag "rwflag" indicates whether the callback is used for reading/decryption (rwflag=0) or writing/encryption (rwflag=1 - not used in this method). See man SSL_CTX_set_default_passwd_cb(3) for more information.
> bool use_DHfile(const char *dh_file, SWBaseError *error = NULL)
> Load Diffie-Hellman parameters from file. These are used to generate a DH key exchange. See man SSL_CTX_set_tmp_dh_callback(3) and www.skip-vpn.org/spec/numbers.html for more information. Should be called before accept() or connect() if used. Returns true on success. Possible SSL error types: badFile (only if the file exist but is invalid). Possible normal error types: fatal (couldn't open file).
> bool use_verification(const char *ca_file, const char *ca_dir, SWBaseError *error = NULL)
> Should the peer certificate be verified? The arguments specify the locations of trusted CA certificates used in the verification. Either ca_file or ca_dir can be set to NULL but not both. See man SSL_CTX_load_verify_locations(3) for format information. Should be called before accept() or connect() if used and the verification result is then available by calling get_verify_result() on the connected socket (the new socket from accept() on the server side, the same socket on the client side). Returns true on success. Possible SSL error types: badFile. Possible normal error types: fatal.
> enum verify_result{noCert, CertOk, noIssCert, CertExpired, CertSelfSign, CertError}
> This enum is used in the structure verify_info. Indicates the verification result.
> - noCert - Peer didn't present a certificate, verification was not requested with use_verification() or not connected to peer yet.
> - CertOk - Peer certificate OK and is trusted.
> - noIssCert - Couldn't find issuer certificate.
> - CertExpired - The peer certificate has expired and is not valid.
> - CertSelfSign - The peer certificate is self signed and not trusted.
> - CertError - The peer certificate is invalid.
> struct verify_info{
>   verify_result result;
>   std::string error;
> }
> This structure contains the result of the peer certificate verification. Used in get_verify_result(). The string contains a human readable reason why the certificate isn't trusted or "ok" if it is. The string is empty if the result is noCert.
> verify_info get_verify_result(void)
> Gets the peer certificate verification result. Should be called after connect() or accept() where the verification is done if use_verification() was called. On the server side this should be done on the new class returned by accept() and not on the listener class! If you don't get CertOk and don't trust the peer you should disconnect. If you trust the peer (or perhaps ask the user if he/she does) but didn't get CertOk you might consider adding this certificate to the trusted CA certificates loaded by use_verification() (you can use get_peerCert_PEM() and then save the string to file), but don't add invalid certificates (CertError and CertExpired). No error types.
> struct peerCert_info{
>   // Names
>   std::string commonName;
>   std::string countryName;
>   std::string localityName;
>   std::string stateOrProvinceName;
>   std::string organizationName;
>   std::string organizationalUnitName;
>   std::string title;
>   std::string initials;
>   std::string givenName;
>   std::string surname;
>   std::string description;
>   std::string uniqueIdentifier;
>   std::string emailAddress;
>   // Expire dates
>   std::string notBefore;
>   std::string notAfter;
>   // Misc. data
>   long serialNumber;
>   long version;
>   std::string sgnAlgorithm;
>   std::string keyAlgorithm;
>   int keySize;
> }
> This structure contains information about the peer certificate. Used in get_peerCert_info(). A string is empty if the information isn't available in the peer certificate (or set to 0 in the case of integers). The structure is not touched on errors.
> bool get_peerCert_info(peerCert_info *info, SWBaseError *error = NULL)
> Gets information about the peer certificate. Should be called after connect() or accept() (on the connected socket). The information is copied to the (by you) allocated structure pointed to by "info". Returns false if the pointer is invalid or if the peer didn't present a certificate. Possible normal error types: fatal.
> int get_peerCert_PEM(std::string *pem, SWBaseError *error = NULL)
> Gets the peer certificate in PEM (ASCII) format. The PEM certificate is copied to the (by you) allocated string. The string will not be touched on errors. Should be called after connect() or accept() (on the connected socket). Returns the length (bytes) of the PEM certificate or -1 on errors. Possible error types: fatal (no peer certificate or invalid arguments).

## Typical Use

Here are the two examples from SWInetSocket again, now with SSL encryption.
Server side example:

    #include "SocketW.h"
    ...
    SWSSLSocket listener;
    SWSSLSocket *mySocket;

    listener.use_cert("cert.pem", "cert_key.pem");  // Load certificate

    listener.bind(5555);
    listener.listen();

    mySocket = (SWSSLSocket *)listener.accept();

    // send encrypted message to client...
    mySocket->sendmsg("Hello Client!");

    // disconnect and clean up
    mySocket->disconnect();
    delete mySocket;

Client side example:

    #include "SocketW.h"
    ...
    SWSSLSocket mySocket;

    mySocket.use_verification("cert.pem", NULL);  // Trust the certificates in
                                                  // cert.pem
    mySocket.connect(5555, "localhost");

    // Check verification
    SWSSLSocket::verify_info vinfo = mySocket.get_verify_result();
    if( vinfo.result != SWSSLSocket::CertOk ){
        // We don't trust the server
        mySocket.disconnect();
        exit(-1);
    }

    // receive encrypted message from server...
    string msg = mySocket.recvmsg();

    // disconnect
    mySocket.disconnect();

Copyright © 2003 Anders Lindström
Last updated 031025