Certificate Handling¶
A certificate is a binding between some identifying information (called a subject) and a public key. This binding is asserted by a signature on the certificate, which is placed there by some authority (the issuer) that at least claims that it knows the subject named in the certificate really “owns” the private key corresponding to the public key in the certificate.
The major certificate format in use today is X.509v3, designed by ISO and further hacked on by dozens (hundreds?) of other organizations.
When working with certificates, the main class to remember is
X509_Certificate
. You can read an object of this type, but you can’t create
one on the fly; a CA object is necessary for making a new certificate. So for
the most part, you only have to worry about reading them in, verifying the
signatures, and getting the bits of data in them (most commonly the public key,
and the information about the user of that key). An X.509v3 certificate can
contain a literally infinite number of items related to all kinds of
things. Botan doesn’t support a lot of them, because nobody uses them and
they’re an impossible mess to work with. This section only documents the most
commonly used ones of the ones that are supported; for the rest, read
x509cert.h
and asn1_obj.h
(which has the definitions of various common
ASN.1 constructs used in X.509).
So what’s in an X.509 certificate?¶
Obviously, you want to be able to get the public key. This is achieved by
calling the member function subject_public_key
, which will return a
Public_Key
*. As to what to do with this, read about load_key
in
Serializing Public Keys. In the general case, this could be any kind of
public key, though 99% of the time it will be an RSA key. However,
Diffie-Hellman, DSA, and ECDSA keys are also supported, so be careful about how
you treat this. It is also a wise idea to examine the value returned by
constraints
, to see what uses the public key is approved for.
The second major piece of information you’ll want is the name/email/etc of the person to whom this certificate is assigned. Here is where things get a little nasty. X.509v3 has two (well, mostly just two…) different places where you can stick information about the user: the subject field, and in an extension called subjectAlternativeName. The subject field is supposed to only included the following information: country, organization, an organizational sub-unit name, and a so-called common name. The common name is usually the name of the person, or it could be a title associated with a position of some sort in the organization. It may also include fields for state/province and locality. What a locality is, nobody knows, but it’s usually given as a city name.
Botan doesn’t currently support any of the Unicode variants used in ASN.1
(UTF-8, UCS-2, and UCS-4), any of which could be used for the fields in the
DN. This could be problematic, particularly in Asia and other areas where
non-ASCII characters are needed for most names. The UTF-8 and UCS-2 string
types are accepted (in fact, UTF-8 is used when encoding much of the time),
but if any of the characters included in the string are not in ISO 8859-1 (ie 0
… 255), an exception will get thrown. Currently the ASN1_String
type
holds its data as ISO 8859-1 internally (regardless of local character set);
this would have to be changed to hold UCS-2 or UCS-4 in order to support
Unicode (also, many interfaces in the X.509 code would have to accept or return
a std::wstring
instead of a std::string
).
Like the distinguished names, subject alternative names can contain a lot of things that Botan will flat out ignore (most of which you would likely never want to use). However, there are three very useful pieces of information that this extension might hold: an email address (mailbox@example.com), a DNS name (somehost.example.com), or a URI (http://www.example.com).
So, how to get the information? Call subject_info
with the name of the
piece of information you want, and it will return a std::string
that is
either empty (signifying that the certificate doesn’t have this information),
or has the information requested. There are several names for each possible
item, but the most easily readable ones are: “Name”, “Country”, “Organization”,
“Organizational Unit”, “Locality”, “State”, “RFC822”, “URI”, and “DNS”. These
values are returned as a std::string
.
You can also get information about the issuer of the certificate in the same
way, using issuer_info
.
X.509v3 Extensions¶
X.509v3 specifies a large number of possible extensions. Botan supports some, but by no means all of them. This section lists which ones are supported, and notes areas where there may be problems with the handling.
Key Usage and Extended Key Usage: No problems known.
Basic Constraints: No problems known. The default for a v1/v2 certificate is assume it’s a CA if and only if the option “x509/default_to_ca” is set. A v3 certificate is marked as a CA if (and only if) the basic constraints extension is present and set for a CA cert.
Subject Alternative Names: Only the “rfc822Name”, “dNSName”, and “uniformResourceIdentifier” fields will be stored; all others are ignored.
Issuer Alternative Names: Same restrictions as the Subject Alternative Names extension. New certificates generated by Botan never include the issuer alternative name.
Authority Key Identifier: Only the version using KeyIdentifier is supported. If the GeneralNames version is used and the extension is critical, an exception is thrown. If both the KeyIdentifier and GeneralNames versions are present, then the KeyIdentifier will be used, and the GeneralNames ignored.
Subject Key Identifier: No problems known.
Revocation Lists¶
It will occasionally happen that a certificate must be revoked before its expiration date. Examples of this happening include the private key being compromised, or the user to which it has been assigned leaving an organization. Certificate revocation lists are an answer to this problem (though online certificate validation techniques are starting to become somewhat more popular). Every once in a while the CA will release a new CRL, listing all certificates that have been revoked. Also included is various pieces of information like what time a particular certificate was revoked, and for what reason. In most systems, it is wise to support some form of certificate revocation, and CRLs handle this easily.
For most users, processing a CRL is quite easy. All you have to do is call the
constructor, which will take a filename (or a DataSource&
). The CRLs
can either be in raw BER/DER, or in PEM format; the constructor will figure out
which format without any extra information. For example:
X509_CRL crl1("crl1.der");
DataSource_Stream in("crl2.pem");
X509_CRL crl2(in);
After that, pass the X509_CRL
object to a X509_Store
object
with
-
X509_Code X509_Store::add_crl(const X509_CRL &crl)¶
and all future verifications will take into account the certificates
listed, assuming add_crl
returns VERIFIED
. If it doesn’t
return VERIFIED
, then the return value is an error code signifying
that the CRL could not be processed due to some problem (which could
be something like the issuing certificate could not being found, an
invalid signature, or the CRL having some format problem). For more
about the X509_Store
API, read Storing and Using Certificates.
Reading Certificates¶
X509_Certificate
has two constructors, each of which takes a source of
data; a filename to read, and a DataSource&
:
X509_Certificate cert1("cert1.pem");
/* This file contains two certificates, concatenated */
DataSource_Stream in("certs2_and_3.pem");
X509_Certificate cert2(in); // read the first cert
X509_Certificate cert3(in); // read the second cert
Storing and Using Certificates¶
If you read a certificate, you probably want to verify the signature on
it. However, consider that to do so, we may have to verify the signature on the
certificate that we used to verify the first certificate, and on and on until
we hit the top of the certificate tree somewhere. It would be a might huge pain
to have to handle all of that manually in every application, so there is
something that does it for you: X509_Store
.
The basic operations are: put certificates and CRLs into it, search for certificates, and attempt to verify certificates. That’s about it. In the future, there will be support for online retrieval of certificates and CRLs (eg with the HTTP cert-store interface currently under consideration by PKIX).
Adding Certificates¶
You can add new certificates to a certificate store using any of these functions:
-
void X509_Store::add_cert(const X509_Certificate &cert, bool trusted = false)¶
-
void X509_Store::add_cert(DataSource &source)¶
-
void X509_Store::add_trusted_certs(DataSource &source)¶
The versions that take a DataSource&
will add all the certificates
that it can find in that source.
All of them add the cert(s) to the store. The “trusted” certificates are the ones that you are willing to trust for certification purposes. For example, say your application is working with certificates that are owned by employees of some company, and all of their certificates are signed by the company CA, whose certificate is in turned signed by a commercial root CA. What you would then do is include the certificate of the commercial CA with your application, and read it in as a trusted certificate. From there, you could verify the company CA’s certificate, and then use that to verify the end user’s certificates. Only self-signed certificates may be considered trusted.
Adding CRLs¶
-
X509_Code X509_Store::add_crl(const X509_CRL &crl)
This will process the CRL and mark the revoked certificates. This will
also work if a revoked certificate is added to the store sometime
after the CRL is processed. The function can return an error code
(listed later), or will return VERIFIED
if everything completed
successfully.
Storing Certificates¶
You can output a set of certificates by calling PEM_encode
, which
will return a std::string
containing each of the certificates in
the store, PEM encoded and concatenated. This simple format can easily
be read by both Botan and other libraries/applications.
Certificate Stores¶
An object of type Certificate_Store
is a generalized interface to an
external source for certificates (and CRLs). Examples of such a store would be
one that looked up the certificates in a SQL database, or by contacting a CGI
script running on a HTTP server. There are currently three mechanisms for
looking up a certificate, and one for retrieving CRLs. By default, most of
these mechanisms will return an empty std::vector
of
X509_Certificate
. This storage mechanism is only queried when doing
certificate validation: it allows you to distribute only the root key with an
application, and let some online method handle getting all the other
certificates that are needed to validate an end entity certificate. In
particular, the search routines will not attempt to access the external
database.
The three certificate lookup methods are by_SKID
(Subject Key Identifier),
by_name
(the CommonName DN entry), and by_email
(stored in either the
distinguished name, or in a subjectAlternativeName extension). The name and
email versions take a std::string
, while the SKID version takes a
SecureVector<byte>
containing the subject key identifier in raw binary. You
can choose not to implement by_name
or by_email
, but by_SKID
is
mandatory to implement, and, currently, is the only version that is used by
X509_Store
.
Finally, there is a method for finding CRLs, called get_crls_for
, that
takes an X509_Certificate
object, and returns a std::vector
of
X509_CRL
. While normally there will be only one CRL, the use of the vector
makes it easy to return no CRLs (eg, if the certificate store doesn’t support
retrieving them), or return multiple ones (for example, if the certificate
store can’t determine precisely which key was used to sign the
certificate). Implementing the function is optional, and by default will return
no CRLs. If it is available, it will be used by X509_CRL
.
As for using such a store, you have to tell X509_Store
about it
with
-
void X509_Store::add_new_certstore(Certificate_Store *new_store)¶
The store object will be owned by (and deleted by)
X509_Store
, so make sure to allocate it withnew
.
Verifying Certificates¶
Verifying a certificate requires that we build up a chain of trust, starting from the root (usually a commercial CA), down through some number of intermediate CAs, and finally reaching the actual certificate in question. Thus, to verify, we actually have to have all those certificates on hand (or at the very least, know where we can get the ones we need).
The class which handles both storing certificates, and verifying them, is
-
class X509_Store¶
-
type Cert_Usage¶
- Can be any of:
ANY
(any usage is OK)CRL_SIGNING
TLS_SERVER
(for SSL/TLS server authentication)TLS_CLIENT
(for SSL/TLS client authentication)CODE_SIGNING
EMAIL_PROTECTION
(usually this means S/MIME)TIME_STAMPING
(in theory any time stamp application, usually IETF PKIX’s Time Stamp Protocol)
-
X509_Code validate_cert(const X509_Certificate &cert, Cert_Usage usage = ANY)¶
Return
VERIFIED
if the certificate can safely be considered valid for the usage(s) described by usage, and an error code if it is not.
First, how does :cpp:class`X509_Store::validate_cert` know if a certificate is valid? A certificate is valid if both of the following hold: a) the signature in the certificate can be verified using the public key in the issuer’s certificate, and b) the issuer’s certificate is a valid CA certificate. Note that this definition is recursive. We get out of this by “bottoming out” when we reach a certificate that we consider trusted. In general this will either be a commercial root CA, or an organization or application specific CA.
There are a few other restrictions (validity periods, key usage restrictions, etc), but the above summarizes the major points of the validation algorithm. In theory, Botan implements the certificate path validation algorithm given in RFC 2459, but in practice it does not (yet), because we don’t support the X.509v3 policy or name constraint extensions.
The default ANY
does not mean valid for any use, it means “is valid for
some usage”. This is usually what you want; requiring that a random certificate
support a particular usage will likely result in a lot of failures, unless your
application is very careful to always issue certificates with the proper
extensions, and you never use certificates generated by other apps.
Return values for validate_cert
(and add_crl
) include:
VERIFIED: The certificate is valid for the specified use.
INVALID_USAGE: The certificate cannot be used for the specified use.
CANNOT_ESTABLISH_TRUST: The root certificate was not marked as trusted.
CERT_CHAIN_TOO_LONG: The certificate chain exceeded the length allowed by a basicConstraints extension.
SIGNATURE_ERROR: An invalid signature was found
POLICY_ERROR: Some problem with the certificate policies was found.
CERT_FORMAT_ERROR: Some format problem was found in a certificate.
CERT_ISSUER_NOT_FOUND: The issuer of a certificate could not be found.
CERT_NOT_YET_VALID: The certificate is not yet valid.
CERT_HAS_EXPIRED: The certificate has expired.
CERT_IS_REVOKED: The certificate has been revoked.
CRL_FORMAT_ERROR: Some format problem was found in a CRL.
CRL_ISSUER_NOT_FOUND: The issuer of a CRL could not be found.
CRL_NOT_YET_VALID: The CRL is not yet valid.
CRL_HAS_EXPIRED: The CRL has expired.
CA_CERT_CANNOT_SIGN: The CA certificate found does not have an contain a public key that allows signature verification.
CA_CERT_NOT_FOR_CERT_ISSUER: The CA cert found is not allowed to issue certificates.
CA_CERT_NOT_FOR_CRL_ISSUER: The CA cert found is not allowed to issue CRLs.
UNKNOWN_X509_ERROR: Some other error occurred.