Certificate Storage Guide

Audience

This document has been reviewed for maemo 4.0.

This document presents the first material to be read by developers involved with the Maemo Certificate Manager API. It offers a gradual approach to certificates and their management on the maemo platform.

This material is complemented by the Maemo Certificate Manager API Reference. Even though this document introduces some cryptographic notions and OpenSSL commands/functions, it is not intended in any way to be a reference on OpenSSL or cryptography.

It is recommended that the API sample programs are downloaded and 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 differs from 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 (i.e. private) key. Typically, a user creates a pair of keys, then distributes the public key (for example, on a public key server), and when this is done, the user is able to receive securely encrypted messages and can be assured that nobody 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 a second, equally important feature of public-key systems: authentication or signing. Encrypting with the private key proves that the sender possesses the private key in question, so (provided the private key is safely guarded) it must be the same person who has issued the public key. Authentication also means non-repudiation (i.e. 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 standardized way to distribute public keys along with the identity of the holder. It contains some information about the holder, including but not limited to:

  • The name of the 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. Instead, it is generated and signed by a trusted third-party - a certification authority (CA), e.g. Verisign or CACert. The CA is expected to make positive checks on the certificate requester in order to verify the identity of the requester (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 certificate 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 and 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 these 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 certificate of the server. It is also possible for a client to have its own certificate, and some services may even demand it, since the server needs to be 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 wishing 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 an S/MIME message sender needs to know the public key and cryptographic capabilities of the message recipient. That information is provided by the X.509 public certificate of the recipient.

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 the private key. The diffusion of CRLs in a timely manner remains a challenge.

Certificates in maemo platform

The maemo platform offers an API to deal with certificate manager storage and manipulation. This enables all the 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 both effort and disk space.

Creating Own Certificates with OpenSSL

It is useful to explain how certificates are created by using them in the code examples. OpenSSL tools are used for that purpose. OpenSSL tools are not installed by default so they must be installed manually. In order to install the package "openssl" you must be either root or set Application manager to the Red Pill mode. The important configurations 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
       

All required fields should be filled with sensible data, otherwise the certificate 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 has been 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, all requested data fields should be filled.

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 can be discarded.

Queries can be made to a certificate file content by issuing

openssl x509 -in cert01.crt -noout -text       

OpenSSL can also be ordered 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 (this test should be performed using small messages):

# 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 can be created and used.

The figure below shows the Certificate Manager plug-in for the maemo control panel, with the focus on a certification authority (CA) that we have created using OpenSSL and manually inserted in the default maemo certificate file, using our sample import program with following command:

./import /usr/share/certs/certman.cst ca my-ca.crt
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. These constants can be found 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 specific database.

The following sections comment on snippets of code with basic certificate manager operations. It is recommended that the accompanying example programs are tried 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 behavior 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 this is supposed to be your own certificate, you also need to import the related private key in order to sign and decrypt messages.

The most important API call here is CST_import_cert. An open file must be provided 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. There is a quick method to pinpoint it: by comparing 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 getting the certID of the new certificate, its purpose should be set (based on command-line parameter) and its account name gotten, 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 it is necessary 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;
		}       

Now both the certificate and the private key are in storage, but they are not bound to each other. The private key can be searched for by its account name. Then CST_assign can be called 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 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 certIDs on the screen in a simple manner. The list collection is here performed by CST_search_by_purpose, but there is a whole family of CST_search_* functions to choose from, covering all search needs.
Most search functions return lists instead of X.509 certificates, since most search arguments are not unique (i.e. they 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. (The ID of a certificate can be obtained by using the listcerts program). The X.509 certificate must be retrieved 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 releasing the X.509 certificate, some data should be chosen from it using OpenSSL functions. This is different from most functions that return pointers; the functions X509_get_*_name return X509_NAME pointers to the certificate memory, so they need not be 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);
      

As X509_NAME objects cannot be displayed directly, they should be converted to characters using X509_NAME_oneline(). This function returns buffers that must be freed.

free(issuer); 
free(subject);
X509_free(certificate);       

Finally, the certificate should be deleted 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 behavior 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 the certificate is exported to stdout instead of deleting it:

err = CST_export_cert_by_id(storage, certID, stdout);
      

References



Improve this page