Certificate storage guide
Audience
This document has been reviewed for maemo 3.x.
This document presents the first material that is read by developers involved with
the maemo Certificate Manager API. Offered here is a gradual approach to certificates
and their management on the maemo platform.
This material is complemented by the Maemo
Certificate Manager API Reference. Although this document introduces some
cryptographic notions and OpenSSL commands/functions, keep in mind that it
is not intended in any way to be a reference on OpenSSL or cryptography.
It is recommended that you download the API sample programs and have them compiled before
reading this document, since some sections refer to these programs.
Introduction
The following sections introduce digital certificates and certificates in the maemo platform.
Digital certificates
Public-key encryption, also known as asymmetric encryption,
is essentially a scheme where the encryption key is different to the decryption
key. Once the message is encrypted, the encryption key cannot decrypt it.
The encryption key is also known as the
public key, since it can be distributed without fear of revealing the decryption
(private) key. Typically, a user creates a pair of keys, then distributes
the public key (for example, on a public keyserver) and when this is done
the user is able to receive securely encrypted messages and is assured that
no one else is able to decrypt the received messages, unless they have access
to the private key.
Therefore keeping
the private key safe is a major concern and most schemes encrypt the private
key locally, demanding a password or security card to disclose it.
In most public-key systems, the keys are mathematically
related and can be used in inverse order, so encrypting with the private key
and decrypting with public key also works. However encrypting a message with
the private key does not guarantee secrecy, since anybody can decrypt it with
the widely known public key.
But it allows
for a second, equally important feature of public-key systems: authentication
or signing. Encrypting with the private key proves that the sender possesses
that private key, so (provided the private key is safely guarded) it must
be the same person who issued the public key. Authentication also means non-repudiation
(the sender cannot deny sending the message).
Public-key systems based on prime numbers are very slow. So, most
practical encryption schemes do not apply it to whole messages, only to message
hashes (for authentication) and for symmetric keys (for encryption).
X.509 certificates are a portable and standardised
way to distribute public keys along with the holder's identity. It contains
some information about the holder, including but not limited to:
- The name of certificate holder, also known as Distinguished Name (DN)
- Contact e-mail
- Internet domain
- Address
- Issue date
- Activation date
- Expiration date
- Public key
- Private key (must never be included in the publicly available certificate).
- Certification authority (see below)
- Certification authority signature (see below)
Since it is easy to generate a certificate claiming to
be anybody, a trustable certificate is not generated by the holder, but it
is generated and signed by a trusted third-party - a certification authority
(CA), for example, Verisign or CACert. The CA is expected to make positive
checks on the certificate requester, to verify the requester's identity (without
this check, the CA signature would be worthless).
If, for example, the Web client of a bank needs to establish an encrypted
connection, it asks for the certificate and checks the signature against the
CA's certificate. Obviously, the CA certificate is necessary to do this. In
practice, most products including Web browsers come bundled with several well-known
CA certificates, so the user rarely needs to download and install a new CA
certificate.
A CA certificate is generally
self-signed, since it is the highest trusted entity in the certificates hierarchy.
It is also possible to build a chain of trust: a CA certificate may be signed
by another CA. More commonly, an entity generates its own certificates by
itself, signing them with the CA-issued one. This is commonly done, for instance,
for the establishment of VPNs and wireless connections, where the certificate
is only to be used within the entity.
Each CA maintains a monotonic serial number counter that is incremented with
every new certificate. In this way, every CA-signed (and therefore widely
trustable) certificate has a worldwide unique ID, composed by certificate
issuer plus the serial number.
Self-signed
non-CA certificates are also common on the Internet. Since CA certificates
cost money, several sites choose to generate the certificate themselves. Most
web browsers warn the user about those untrusted certificates and prompt for
permission to continue connecting. An alternative method would be to request
a certificate form CACert (www.cacert.org), which is free of charge. The required
root certificate is already included in the product and in most of the free web
browsers as well.
Visiting again the
Web client/bank example, most HTTPS connections only check the server's certificate.
It is also possible for a client to have its own certificate and some services
may even demand it, since the server is sure that the client is whom it claims
to be.
X.509 certificates are incompatible
with PGP, and different from it in several aspects. X.509 certificates are
formally signed by trusted authorities who want to earn money with this service;
PGP/GPG keys rely on a web of trust and are signed by other PGP key holders
(a key signed by a trusted person becomes itself trusted, at least in the
niche to which the signed key belongs).
S/MIME is a standard separate from X.509, but both appear together in this
maemo API because a S/MIME message sender needs to know the message recipient's
public key and cryptographic capabilities. That information is provided by
the recipient's X.509 public certificate.
Certificate Revocation Lists (CRL) are lists of certificates that have
actively expired before their natural expiration date. A typical reason for
premature expiration is the leaking of its private key. The diffusion of CRLs
in a timely manner remains a challenge.
Certificates in the maemo platform
The maemo platform offers an API to deal with certificate manager storage and manipulation. This enables every piece of software to have access to all certificates, so that, for example, installing a new CA certificate takes immediate effect in all relevant programs (such as Web browser, e-mail, VPN and wireless connection). This saves you effort and disk space.
Creating your own certificates with OpenSSL
It is useful to
explain how certificates are created, to use them in the code examples. OpenSSL
tools are used for that purpose. The important configuration for certification
creation at /etc/ssl/openssl.cnf
are:
[ CA_default ] dir = /root/certificates # Where all the files are kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crls are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/my-ca.crt # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/my-ca.key # The private key default_days = 1095 # how long to certify for
Once configured,
the Certificate Authority can be created with the command-line tool openssl
:
cd /root/certificates touch index.txt echo 01 > serial openssl req -nodes -new -x509 -keyout my-ca.key -out my-ca.crt
Make sure that you fill all required fields
with sensible data, otherwise it may be rejected by some software. The my-ca.key
file
contains the CA's private key, which must be kept safe because the CA security
depends on its secrecy. my-ca.crt
is the public distributable
CA certificate.
When this is done, it is
possible to create certificates. The first command creates the certificate,
the second command signs it:
openssl req -nodes -new -keyout certificate.key -out certificate.csr openssl ca -out certificate.crt -in certificate.csr
OpenSSL learns from
the /etc/ssl/openssl.cnf
configuration which CA to use for signing
the new regular certificate. Again, make sure you fill all requested data
fields.
The certificate.key
file
contains the private key, and it must be safely guarded by the certificate
holder; certificate.crt
is the signed public certificate; certificate.csr
is
the unsigned intermediate certificate that may be discarded.
You can query a certificate file content by issuing
openssl x509 -in cert01.crt -noout -text
You can also order OpenSSL to include all the data in human-readable text format alongside the binary (base64-encoded) certificate. Finally, to make an actual encryption test with public keys (be sure to use small messages in this test):
# encrypt to a public certificate recipient cat certificate.crt | openssl rsautl -encrypt -in message -certin -out message.enc # recipient decrypts using the private key openssl rsautl -decrypt -in message.enc -inkey certificate.key
Maemo certificate databases
In maemo, certificates are stored in Berkeley DB version 1 format
files. Fortunately, the API user never needs to cope with low-level details
of BDB databases. The default maemo certificate file is /usr/share/certs/certman.cst
even
though alternative databases may be created and used.
The figure below shows the Certificate Manager plugin for the maemo Control Panel, with the focus on a certification authority (CA) that we created using OpenSSL and manually inserted in the default maemo certificate file, using our sample import program.
Inside every database there is a set of "folders", a simple classification
of the certificates useful to limit the scope of searches. The constants for
these folders are: CST_FOLDER_CA, CST_FOLDER_OTHER, CST_FOLDER_PERSONAL, CST_FOLDER_SITE
and CST_FOLDER_UNKNOWN. You can find those constants in most code samples.
Even though certificate UIDs are said to
be unique, most functions in the API refer to certificates by a storage ID.
This ID has no relationship with UID and it is unique only within a database.
The following sections comment on snippets
of code with basic certificate manager operations. It is recommended that
you try the accompanying example programs while reading this document. The
samples must be compiled inside Scratchbox with the maemo SDK installed.
Creating databases
$ ./dbcreate test.db
This program only opens the specified database file. If it does not exist, it must be created. This behaviour is provided by the open file API call:
// CST_open_file(filename, readonly [ignored], password [ignored]); storage = CST_open_file(argv[1], FALSE, NULL); if (! storage) { printf("Could not create certificate database %s.\n", argv[1]); return 1; }
After opening/creating the database, the program counts the available certificates, walking the GSList tree. This list is found by searching by purpose, specifying "any" as the purpose.
// get any certificate we find certificates = CST_search_by_purpose(storage, CST_PURPOSE_NONE); for (i = certificates; i; i = i->next) { ++count; } if (certificates > 0) { printf("Database already exists and it has %d certificates inside.", count); }
Finally, all allocated resources must be freed:
g_slist_free(certificates); CST_free(storage);
Importing certificates and keys
$ ./import test.db ca certs/ca.crt $ ./import test.db server certs/cert01.crt $ ./import test.db client certs/cert02.crt certs/cert02.key
The first example imports a CA certificate.
The second one imports a regular SSL server certificate. The third one imports
a SSL client certificate; since it is supposed to be your own certificate,
you also need to import the related private key so you can sign and decrypt
messages.
The most important API call here
is CST_import_cert. You must provide an open file for it (with the certificate
inside).
// get present list oldlist = CST_search_by_purpose(storage, CST_PURPOSE_NONE); fcert = fopen(cert, "r"); if (! fcert) { printf("Certificate file could not be open, errno = %d\n", errno); CST_free(storage); return 1; } err = CST_import_cert(storage, fcert, NULL); if (err) { printf("Error %d when trying to import certificate %s\n", err, cert); CST_free(storage); return 1; }
You need to know which certificate has just been imported, so you use a quick method to pinpoint it: compare two lists, one collected before import, the second collected just after.
// get new list list = CST_search_by_purpose(storage, CST_PURPOSE_NONE); // discover new certID by comparing the two lists for (i = list; i; i = i->next) { if (! g_slist_find(oldlist, i->data)) { certID = GPOINTER_TO_UINT(i->data); } } g_slist_free(list); g_slist_free(oldlist); if (! certID) { printf("Newly imported certificate not found!\n"); CST_free(storage); return 1; }
After you get the certID of the new certificate, set its purpose (based on command-line parameter) and get its account name, since this name is the identifier for the forthcoming import key.
CST_set_purpose(storage, certID, purpose, TRUE); printf("Getting x.509 certificate...\n"); x509cert = CST_get_cert(storage, certID); printf("Getting x.509 subject name...\n"); // must NOT be freed since X509_get_*(X509* certificate) // returns pointers to certificate's own memory account = X509_get_subject_name(x509cert);
If you need to import a private key, CST_import_priv_key
does
the hard work.
if (privkey) { fprivkey = fopen(privkey, "r"); if (! fprivkey) { printf("Key file could not be open, errno = %d\n", errno); CST_free(storage); return 1; } printf("Importing key...\n"); err = CST_import_priv_key(storage, account, fprivkey, password, password); if (err) { printf("Error %d when trying to import private key %s\n", err, privkey); CST_free(storage); return 1; }
You now have the certificate
and the private key in storage, but they are not bound to each other. You
search for the private key by its account name and then call CST_assign
to
bind the certificate to the key.
keylist = CST_priv_key_search_by_name(storage, account); if (! keylist) { printf("Error %d when trying to list of appended keys\n", CST_last_error()); CST_free(storage); return 1; } // picks the first of the list keyID = GPOINTER_TO_UINT(keylist->data); printf("Newly imported private key is keyID %d\n", keyID); err = CST_assign(storage, certID, keyID, password);
The figure below shows the Certificate Manager with a client certificate created by OpenSSL and manually inserted in maemo certificate file using the import sample program.
Sample program for searching and listing certificates
$ ./listcerts testdb.cst ca $ ./listcerts testdb.cst any
This program collects a list of certificates given a purpose and lists
the certID's on the screen, in a simple manner. The list collection is done
by CST_search_by_purpose
here, but there is a whole family of CST_search_*
functions
to choose from, covering all searching needs.
Most search functions return lists instead of X509 certificates since most
search arguments are not unique (that is, may match with an unbound number
of registers). Code snippet:
certificates = CST_search_by_purpose(storage, purpose); for (i = certificates; i; i = i->next) { printf("CertID found: %d\n", GPOINTER_TO_UINT(i->data)); ++count; }
An interesting detail about searching by purpose: searching by CST_PURPOSE_NONE returns all stored certificates, since only certificates matching all specified purpose bits are returned; CST_PURPOSE_NONE equals zero, so no purpose is specified, so all certificates are fit.
Deleting certificates
$ ./delcert testdb.cst 101
This sample deletes a certificate
with a given storage and certID. (You can obtain the ID of a certificate using
the listcerts program). You must retrieve the X.509 certificate through CST_get_cert()
,
only to make sure that it exists.
certificate = CST_get_cert(storage, certID); if (! certificate) { if (! CST_last_error()) { printf("Certificate %d does not exist\n", certID); CST_free(storage); return 1; } else { printf("Error %d while getting certificate\n", CST_last_error()); CST_free(storage); return 1; } }
Before you
release the X.509 certificate, choose some data from it using OpenSSL functions.
This is different from most functions that return pointers, X509_get_*_name
returns
X509_NAME pointers to the certificate memory, so they need not released.
issuer = X509_NAME_oneline(X509_get_issuer_name(certificate), NULL, 0); subject = X509_NAME_oneline(X509_get_subject_name(certificate), NULL, 0); printf("Issuer: %s Subject (holder): %s\n", issuer, subject);
But you have noted that X509_NAME objects
cannot be displayed directly, so convert them to characters using X509_NAME_oneline()
and
this function returns buffers that must be freed.
free(issuer); free(subject); X509_free(certificate);
And finally delete the certificate from the storage.
err = CST_delete_cert(storage, certID);
Validating certificate files
$ ./validate test.db certs/cert01.crt
This program validates a certificate file. Since CST_is_valid*
functions
still do not check the trust chain, they accept any non-corrupted certificate
as valid. This behaviour changes in future API versions (this is why this
function already needs the storage parameter). Code:
fcert = fopen(argv[2], "r"); if (! fcert) { printf("Certificate file could not be open, errno = %d\n", errno); CST_free(storage); return 1; } if (CST_is_valid_f(storage, fcert, NULL)) { printf("Certificate is valid.\n"); } else if (CST_last_error()) { printf("Error while validating certificate\n."); } else { printf("Certificate is invalid (corrupted or not trusted)\n"); } fclose(fcert);
Exporting certificates
$ ./export test.db 101
The program source is almost equal to the delcert
program,
except that you export the certificate to stdout instead of deleting it:
err = CST_export_cert_by_id(storage, certID, stdout);
Improve this page