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.

Certificate Manager applet of Control Panel showing a certification authority manually inserted by our sample program in /usr/share/certs/certman.cst

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.

A client certificate inserted manually inserted by our sample program in /usr/share/certs/certman.cst

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