WOLFSSL MANUAL: CHAPTER 7

Download wolfSSL Manual (PDF)

Chapter 7: Keys and Certificates

For an introduction to X.509 certificates, as well as how they are used in SSL and TLS, please see Appendix A.

7.1 Supported Formats and Sizes


wolfSSL has support for PEM, and DER formats for certificates and keys, as well as PKCS#8 private keys (with PKCS#5 or PKCS#12 encryption).

PEM, or “Privacy Enhanced Mail” is the most common format that certificates are issued in by certificate authorities. PEM files are Base64 encoded ASCII files which can include multiple server certificates, intermediate certificates, and private keys, and usually have a .pem, .crt, .cer, or .key file extension. Certificates inside PEM files are wrapped in the “-----BEGIN CERTIFICATE-----” and “-----END CERTIFICATE-----” statements.

DER, or “Distinguished Encoding Rules”, is a binary format of a certificate. DER file extensions can include .der and .cer, and cannot be viewed with a text editor.

7.2 Certificate Loading


Certificates are normally loaded using the file system (although loading from memory buffers is supported as well - see Section 7.5).

7.2.1 Loading CA Certificates

CA certificate files can be loaded using the wolfSSL_CTX_load_verify_locations() function:

int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX *ctx,

     const char *CAfile,

     const char *CApath);*

CA loading can also parse multiple CA certificates per file using the above function by passing in a CAfile in PEM format with as many certs as possible. This makes initialization easier, and is useful when a client needs to load several root CAs at startup.  This makes wolfSSL easier to port into tools that expect to be able to use a single file for CAs.

7.2.2 Loading Client or Server Certificates

Loading single client or server certificates can be done with the wolfSSL_CTX_use_certificate_file() function.  If this function is used with a certificate chain, only the actual, or “bottom” certificate will be sent.

int wolfSSL_CTX_use_certificate_file(WOLFSSL_CTX *ctx,

    const char *CAfile,

    int type);

CAfile is the CA certificate file, and type is the format of the certificate - such as SSL_FILETYPE_PEM.

The server and client can send certificate chains using the wolfSSL_CTX_use_certificate_chain_file() function.  The certificate chain file must be in PEM format and must be sorted starting with the subject's certificate (the actual client or server cert), followed by any intermediate certificates and ending (optionally) at the root "top" CA.  The example server (/examples/server/server.c) uses this functionality.

int wolfSSL_CTX_use_certificate_chain_file(WOLFSSL_CTX *ctx,

     const char *file);

7.2.3 Loading Private Keys

Server private keys can be loaded using the wolfSSL_CTX_use_PrivateKey_file() function.

int wolfSSL_CTX_use_PrivateKey_file(WOLFSSL_CTX *ctx,

                      const char *keyFile, int type);

keyFile is the private key file, and type is the format of the private key (e.g. SSL_FILETYPE_PEM).

7.2.4 Loading Trusted Peer Certificates

Loading a trusted peer certificate to use can be done with wolfSSL_CTX_trust_peer_cert().

int wolfSSL_CTX_trust_peer_cert(WOLFSSL_CTX *ctx,

                 const char *trustCert, int type);

trustCert is the certificate file to load, and type is the format of the private key (i.e. SSL_FILETYPE_PEM).

7.3 Certificate Chain Verification


wolfSSL requires that only the top or “root” certificate in a chain to be loaded as a trusted certificate in order to verify a certificate chain. This means that if you have a certificate chain (A -> B -> C), where C is signed by B, and B is signed by A, wolfSSL only requires that certificate A be loaded as a trusted certificate in order to verify the entire chain (A->B->C).

For example, if a server certificate chain looks like:

 

The wolfSSL client should already have at least the root cert (A) loaded as a trusted root (with wolfSSL_CTX_load_verify_locations()).  When the client receives the server cert chain, it uses the signature of A to verify B, and if B has not been previously loaded into wolfSSL as a trusted root, B gets stored in wolfSSL's internal cert chain (wolfSSL just stores what is necessary to verify a certificate: common name hash, public key and key type, etc.).  If B is valid, then it is used to verify C.

Following this model, as long as root cert "A" has been loaded as a trusted root into the wolfSSL server, the server certificate chain will still be able to be verified if the server sends (A->B->C), or (B->C).  If the server just sends (C), and not the intermediate certificate, the chain will not be able to be verified unless the wolfSSL client has already loaded B as a trusted root.

7.4 Domain Name Check for Server Certificates


wolfSSL has an extension on the client that automatically checks the domain of the server certificate. In OpenSSL mode nearly a dozen function calls are needed to perform this. wolfSSL checks that the date of the certificate is in range, verifies the signature, and additionally verifies the domain if you call:

wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn)

before calling wolfSSL_connect(). wolfSSL will match the X.509 issuer name of peer's server certificate against dn (the expected domain name). If the names match wolfSSL_connect() will proceed normally, however if there is a name mismatch, wolfSSL_connect() will return a fatal error and wolfSSL_get_error() will return DOMAIN_NAME_MISMATCH.

 

Checking the domain name of the certificate is an important step that verifies the server is actually who it claims to be. This extension is intended to ease the burden of performing the check.

7.5 No File System and using Certificates


Normally a file system is used to load private keys, certificates, and CAs. Since wolfSSL is sometimes used in environments without a full file system an extension to use memory buffers instead is provided. To use the extension define the constant NO_FILESYSTEM and the following functions will be made available:

 

int wolfSSL_CTX_load_verify_buffer(WOLFSSL_CTX* ctx, const unsigned

char* in,long sz, int format);

int wolfSSL_CTX_use_certificate_buffer(WOLFSSL_CTX* ctx,

const unsigned char* in,

long sz, int format);

int wolfSSL_CTX_use_PrivateKey_buffer(WOLFSSL_CTX* ctx,

const unsigned char* in,

long sz, int format);

int wolfSSL_CTX_use_certificate_chain_buffer(WOLFSSL_CTX* ctx,

                                   const unsigned char* in,long sz);
int wolfSSL_CTX_trust_peer_buffer(WOLFSSL_CTX* ctx,

const unsigned char* in,

Long sz, int format);

Use these functions exactly like their counterparts that are named “*_file” instead of “*_buffer”.  And instead of providing a filename provide a memory buffer.  See API documentation for usage details.

7.5.1 Test Certificate and Key Buffers

wolfSSL has come bundled with test certificate and key files in the past.  Now it also comes bundled with test certificate and key buffers for use in environments with no filesystem available.  These buffers are available in certs_test.h when defining one or more of USE_CERT_BUFFERS_1024, USE_CERT_BUFFERS_2048, or USE_CERT_BUFFERS_256.

7.6 Serial Number Retrieval


The serial number of an X.509 certificate can be extracted from wolfSSL using the following function.  The serial number can be of any length.

int wolfSSL_X509_get_serial_number(WOLFSSL_X509* x509,

unsigned char* buffer, int* inOutSz)

buffer will be written to with at most *inOutSz bytes on input. After the call, if successful (return of 0), *inOutSz will hold the actual number of bytes written to buffer. A full example is included wolfssl/test.h.

7.7 RSA Key Generation


wolfSSL supports RSA key generation of varying lengths up to 4096 bits. Key generation is off by default but can be turned on during the ./configure process with:

--enable-keygen

or by defining WOLFSSL_KEY_GEN in Windows or non-standard environments. Creating a key is easy, only requiring one function from rsa.h:

int MakeRsaKey(RsaKey* key, int size, long e, RNG* rng);

Where size is the length in bits and e is the public exponent, using 65537 is usually a good choice for e. The following from wolfcrypt/test/test.c gives an example creating an RSA key of 1024 bits:

RsaKey genKey;

RNG    rng;

int    ret;

InitRng(&rng);

InitRsaKey(&genKey, 0);

ret = MakeRsaKey(&genKey, 1024, 65537, &rng);

if (ret != 0)

    /* ret contains error */;

The RsaKey genKey can now be used like any other RsaKey. If you need to export the key, wolfSSL provides both DER and PEM formatting in asn.h. Always convert the key to DER format first, and then if you need PEM use the generic DerToPem() function like this:

byte der[4096];

int  derSz = RsaKeyToDer(&genKey, der, sizeof(der));

if (derSz < 0)

    /* derSz contains error */;

The buffer der now holds a DER format of the key. To convert the DER buffer to PEM use the conversion function:

byte pem[4096];

int  pemSz = DerToPem(der, derSz, pem, sizeof(pem),

                      PRIVATEKEY_TYPE);

if (pemSz < 0)

    /* pemSz contains error */;

The last argument of DerToPem() takes a type parameter, usually either PRIVATEKEY_TYPE or CERT_TYPE. Now the buffer pem holds the PEM format of the key.

7.7.1 RSA Key Generation Notes

Although an RSA private key contains the public key as well, wolfSSL doesn’t currently have the capability to generate a standalone RSA public key.  The private key can be used as both a private and public key by wolfSSL as used in test.c.  

The reasoning behind the lack of individual RSA public key generation in wolfSSL is that the private key and the public key (in the form of a certificate) is all that is typically needed for SSL.

A separate public key can be loaded into wolfSSL manually using the RsaPublicKeyDecode() function if need be.

7.8 Certificate Generation


wolfSSL supports X.509 v3 certificate generation. Certificate generation is off by default but can be turned on during the ./configure process with:

--enable-certgen

or by defining WOLFSSL_CERT_GEN in Windows or non-standard environments.

Before a certificate can be generated the user needs to provide information about the subject of the certificate. This information is contained in a structure from wolfssl/wolfcrypt/asn_public.h named Cert:

/* for user to fill for certificate generation */

typedef struct Cert {

    int      version;                   /* x509 version  */

    byte     serial[CTC_SERIAL_SIZE];   /* serial number */

    int      sigType;                   /*signature algo type */

    CertName issuer;                    /* issuer info */

    int      daysValid;                 /* validity days */

    int      selfSigned;                /* self signed flag */

    CertName subject;                   /* subject info */

    int      isCA;                      /*is this going to be a CA*/

    ...

} Cert;

Where CertName looks like:

typedef struct CertName {

char country[CTC_NAME_SIZE];

        char countryEnc;

        char state[CTC_NAME_SIZE];

        char stateEnc;

        char locality[CTC_NAME_SIZE];

        char localityEnc;

        char sur[CTC_NAME_SIZE];

        char surEnc;

        char org[CTC_NAME_SIZE];

        char orgEnc;

        char unit[CTC_NAME_SIZE];

        char unitEnc;

        char commonName[CTC_NAME_SIZE];

        char commonNameEnc;

        char email[CTC_NAME_SIZE];  /* !!!! email has to be last!!!! */

} CertName;

Before filling in the subject information an initialization function needs to be called like this:

Cert myCert;

InitCert(&myCert);

InitCert() sets defaults for some of the variables including setting the version to 3 (0x02), the serial number to 0 (randomly generated), the sigType to CTC_SHAwRSA, the daysValid to 500, and selfSigned to 1 (TRUE). Supported signature types include:

CTC_SHAwDSA

CTC_MD2wRSA

CTC_MD5wRSA

CTC_SHAwRSA

CTC_SHAwECDSA

CTC_SHA256wRSA

CTC_SHA256wECDSA

CTC_SHA384wRSA

CTC_SHA384wECDSA

CTC_SHA512wRSA

CTC_SHA512wECDSA

Now the user can initialize the subject information like this example from wolfcrypt/test/test.c:

strncpy(myCert.subject.country, "US", CTC_NAME_SIZE);

strncpy(myCert.subject.state, "OR", CTC_NAME_SIZE);

strncpy(myCert.subject.locality, "Portland", CTC_NAME_SIZE);

strncpy(myCert.subject.org, "yaSSL", CTC_NAME_SIZE);

strncpy(myCert.subject.unit, "Development", CTC_NAME_SIZE);

strncpy(myCert.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE);

strncpy(myCert.subject.email, "info@wolfssl.com", CTC_NAME_SIZE);

Then, a self-signed certificate can be generated using the variables genKey and rng from the above key generation example (of course any valid RsaKey or RNG can be used):

byte derCert[4096];

int certSz = MakeSelfCert(&myCert, derCert, sizeof(derCert), &key, &rng);

if (certSz < 0)

  /* certSz contains the error */;

The buffer derCert now contains a DER format of the certificate. If you need a PEM format of the certificate you can use the generic DerToPem() function and specify the type to be CERT_TYPE like this:

byte* pem;

int pemSz = DerToPem(derCert, certSz, pem, sizeof(pemCert), CERT_TYPE);

if (pemCertSz < 0)

  /* pemCertSz contains error */;

Now the buffer pemCert holds the PEM format of the certificate.

If you wish to create a CA signed certificate then a couple of steps are required. After filling in the subject information as before, you’ll need to set the issuer information from the CA certificate.  This can be done with SetIssuer() like this:

ret = SetIssuer(&myCert, “ca-cert.pem”);

if (ret < 0)

        /* ret contains error */;

Then you’ll need to perform the two-step process of creating the certificate and then signing it (MakeSelfCert() does these both in one step). You’ll need the private keys from both the issuer (caKey) and the subject (key). Please see the example in test.c for complete usage.

byte derCert[4096];

int certSz = MakeCert(&myCert, derCert, sizeof(derCert), &key, NULL, &rng);

if (certSz < 0);

   /*certSz contains the error*/;

certSz = SignCert(myCert.bodySz, myCert.sigType, derCert,

sizeof(derCert), &caKey, NULL, &rng);

if (certSz < 0);

   /*certSz contains the error*/;     

The buffer derCert now contains a DER format of the CA signed certificate.  If you need a PEM format of the certificate please see the self signed example above.  Note that MakeCert() and SignCert() provide function parameters for either an RSA or ECC key to be used.  The above example uses an RSA key and passes NULL for the ECC key parameter.

7.9 Convert to raw ECC key


With our recently added support for raw ECC key import comes the ability to convert an ECC key from PEM to DER. Use the following with the specified arguments to accomplish this:

EccKeyToDer(ecc_key*, byte* output, word32 inLen);

Example:

        

        #define FOURK_BUF 4096

byte  der[FOURK_BUF];

ecc_key userB;

        EccKeyToDer(&userB, der, FOURK_BUF);