Skip to content

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.

Supported Formats and Sizes

wolfSSL (formerly CyaSSL) 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.

An X.509 certificate is encoded using ASN.1 format. The DER format is the ASN.1 encoding. The PEM format is Base64 encoded and wrapped with a human readable header and footer. TLS send certificates in DER format.

Supported Certificate Extensions

If an unsupported or unknown extension that is marked as critical is found, then an error message is returned, otherwise unsupported or unknown extensions found are ignored. Certificate extension parsing expect that at the very least --enable-certext (macro WOLFSSL_CERT_EXT) has been used when compiling the wolfSSL library. This is a high level list of certificate extensions that can be parsed and at least part if not all of the extensions be used.

Extension From RFC 5280 Supported
Authority Key Identifier Yes
Subject Key Identifier Yes
Key Usage Yes
Certificate Policies Yes
Policy Mappings No
Subject Alternative Name Yes
Issuer Alternative Name No
Subject Directory Attributes No
Basic Constraints Yes
Name Constraints Yes
Policy Constraints Yes
Extended Key Usage Yes
CRL Distribution Points Yes
Inihibit anyPolicy Yes
Freshest CRL No
Additional Extension Supported
Netscape Yes
Custom OID Yes

The next couple of sections delve deeper into the support of the individual certificate extensions.

Auth/Subject Key ID

By default key ID's are a SHA1 hash of the key. SHA256 hashes of the key are also supported.

Subject Alternative Names

Alternative Name Types Supported
email Yes
DNS name Yes
IP address Yes
URI Yes

Key Usage

Key usage can be parsed and retrieved after parsing of the certificate is complete.

Key Usage Supported
digitalSignature Yes
nonRepudiation Yes
keyEncipherment Yes
dataEncipherment Yes
keyAgreement Yes
keyCertSign Yes
cRLSign Yes
encipherOnly Yes
decipherOnly Yes

Extended Key Usage

Starting at wolfSSL version 3.15.5 if an extended key usage is unknown/unsupported then it is ignored. For versions before 3.15.5 an unknown extended key usage OID will cause a parsing error.

Extended Key Usage Supported
anyExtendedKeyUsage Yes
id-kp-serverAuth Yes
id-kp-clientAuth Yes
id-kp-codeSigning Yes
id-kp-emailProtection Yes
id-kp-timeStamping Yes
id-kp-OCSPSigning Yes

Custom OID Extensions

Custom OID X.509 extension injection and parsing was introduced in wolfSSL version 5.3.0. The enable options --enable-certgen must be used along with the macros WOLFSSL_ASN_TEMPLATE, WOLFSSL_CUSTOM_OID and HAVE_OID_ENCODING to enable custom extensions. WOLFSSL_ASN_TEMPLATE is defined by default when using ./configure but needs to be manually defined if building with WOLFSSL_USER_SETTINGS.

After building wolfSSL with these settings the function wc_SetCustomExtension() can be used to set a custom extension in a Cert struct and wc_SetUnknownExtCallback() can be used to register a callback for handling unknown extension OIDs in a DecodedCert struct.

Certificate Loading

Certificates are normally loaded using the file system (although loading from memory buffers is supported as well - see No File System and using Certificates).

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.

NOTE: If you have to load a chain of Roots and Intermediate certificates you must load them in the order of trust. Load ROOT CA first followed by Intermediate 1 followed by Intermediate 2 and so on. You may call wolfSSL_CTX_load_verify_locations() for each cert to be loaded or just once with a file containing the certs in order (Root at the top of the file and certs ordered by the chain of trust)

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.

NOTE: This is the exact reverse of the order necessary when loading a certificate chain for verification! Your file contents in this scenario would be Entity cert at the top of the file followed by the next cert up the chain and so on with Root CA at the bottom of the file.

int wolfSSL_CTX_use_certificate_chain_file(WOLFSSL_CTX *ctx,
                                 const char *file);

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).

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).

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:

Certificate Chain

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.

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.

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:

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.

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.

Serial Number Retrieval

The serial number of an X.509 certificate can be extracted from wolfSSL using wolfSSL_X509_get_serial_number(). 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.

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. Supported types are:

  • CA_TYPE
  • TRUSTED_PEER_TYPE
  • CERT_TYPE
  • CRL_TYPE
  • DH_PARAM_TYPE
  • DSA_PARAM_TYPE
  • CERTREQ_TYPE
  • DSA_TYPE
  • DSA_PRIVATEKEY_TYPE
  • ECC_TYPE
  • ECC_PRIVATEKEY_TYPE
  • RSA_TYPE
  • PRIVATEKEY_TYPE
  • ED25519_TYPE
  • EDDSA_PRIVATEKEY_TYPE
  • PUBLICKEY_TYPE
  • ECC_PUBLICKEY_TYPE
  • PKCS8_PRIVATEKEY_TYPE
  • PKCS8_ENC_PRIVATEKEY_TYPE

RSA Key Generation Notes

The RSA private key contains the public key as well. The private key can be used as both a private and public key by wolfSSL as used in test.c. 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. Additionally, the wc_RsaKeyToPublicDer() function can be used to export the public RSA key.

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 */;

Supported types are:

  • CA_TYPE
  • TRUSTED_PEER_TYPE
  • CERT_TYPE
  • CRL_TYPE
  • DH_PARAM_TYPE
  • DSA_PARAM_TYPE
  • CERTREQ_TYPE
  • DSA_TYPE
  • DSA_PRIVATEKEY_TYPE
  • ECC_TYPE
  • ECC_PRIVATEKEY_TYPE
  • RSA_TYPE
  • PRIVATEKEY_TYPE
  • ED25519_TYPE
  • EDDSA_PRIVATEKEY_TYPE
  • PUBLICKEY_TYPE
  • ECC_PUBLICKEY_TYPE
  • PKCS8_PRIVATEKEY_TYPE
  • PKCS8_ENC_PRIVATEKEY_TYPE

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.

Certificate Signing Request (CSR) Generation

wolfSSL supports X.509 v3 certificate signing request (CSR) generation. CSR generation is off by default but can be turned on during the ./configure process with --enable-certreq --enable-certgen or by defining WOLFSSL_CERT_GEN and WOLFSSL_CERT_REQ in Windows or non-standard environments.

Before a CSR 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 details on the Cert and CertName structures please reference Certificate Generation above.

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

Cert request;
InitCert(&request);

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 https://github.com/wolfSSL/wolfssl-examples/blob/master/certgen/csr_example.c:

strncpy(req.subject.country, "US", CTC_NAME_SIZE);
strncpy(req.subject.state, "OR", CTC_NAME_SIZE);
strncpy(req.subject.locality, "Portland", CTC_NAME_SIZE);
strncpy(req.subject.org, "wolfSSL", CTC_NAME_SIZE);
strncpy(req.subject.unit, "Development", CTC_NAME_SIZE);
strncpy(req.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE);
strncpy(req.subject.email, "info@wolfssl.com", CTC_NAME_SIZE);

Then, a valid signed CSR can be generated using the variable key from the above key generation example (of course any valid ECC/RSA key or RNG can be used):

byte der[4096]; /* Store request in der format once made */

ret = wc_MakeCertReq(&request, der, sizeof(der), NULL, &key);
/* check ret value for error handling, <= 0 indicates a failure */

Next you will want to sign your request making it valid, use the rng variable from the above key generation example. (of course any valid ECC/RSA key or RNG can be used)

derSz = ret;

req.sigType = CTC_SHA256wECDSA;
ret = wc_SignCert(request.bodySz, request.sigType, der, sizeof(der), NULL, &key, &rng);
/* check ret value for error handling, <= 0 indicates a failure */

Lastly it is time to convert the CSR to PEM format for sending to a CA authority to use in issueing a certificate:

ret = wc_DerToPem(der, derSz, pem, sizeof(pem), CERTREQ_TYPE);
/* check ret value for error handling, <= 0 indicates a failure */
printf("%s", pem); /* or write to a file */

Limitations

There are fields that are mandatory in a certificate that are excluded in a CSR. There are other fields in a CSR that are also deemed “optional” that are otherwise mandatory when in a certificate. Because of this the wolfSSL certificate parsing engine, which strictly checks all certificate fields AND considers all fields mandatory, does not support consuming a CSR at this time. Therefore while CSR generation AND certificate generation from scratch are supported, wolfSSL does not support certificate generation FROM a CSR. Passing in a CSR to the wolfSSL parsing engine will return a failure at this time. Check back for updates once we support consuming a CSR for use in certificate generation!

See also: Certificate Generation

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);