1. Introduction
1.1 Document Purpose
1.2 Prerequisites
1.3 Next Steps
2. Project Goals
2.1 Purpose of libpkix
2.2 Why do this?
2.3 Requirements
3. Features of libpkix
3.1 Validating X.509 Certificate Chains
3.2 Building Valid X.509 Certificate Chains
3.3 Collecting Credentials to Help Others Build and Validate Certificate Chains
4. libpkix Architecture
4.1 The Caller
4.2 libpkix Portable Code
4.3 libpkix Portability Layer
This document describes the overall architecture of the libpkix library. It should be read by anyone who wants to understand the library's purpose and how it fits into larger systems.
Before reading this document, you might benefit from having some familiarity with PKI and X.509 certificates. There are some good white papers on the PKI Forum's web site.
If you have comments on this document, please send them to the project administrator. If you want more information about libpkix, visit our Home Page.
The purpose of the libpkix library is to provide a widely useful C library for building and validating chains of X.509 certificates, compliant with the latest standards.
When we say "widely useful", we mean that we hope libpkix will be used by many people in many products on many different platforms. When we say "the latest standards", we mean IETF RFC 3280 or its successors (developed by the IETF's PKIX working group) and the latest version of ITU X.509 (ISO/IEC 9594-8).
Through this effort, we aim to address several problems that have slowed PKI deployment: poor interoperability due to non-standard certificate chain validation and lack of application support for PKI. These are not the only obstacles to PKI usage, but they are substantial ones.
libpkix makes it much easier for application developers to include high-quality certificate chain validation and building in their applications.
In order to fulfill this challenging mission, we have identified the following requirements that libpkix must meet. This section also talks briefly about how we intend to ensure that these requirements are met.
Security is the main focus of this library, so we cannot compromise in this area. We aim to prevent security vulnerabilities through careful design, thorough documentation, education, design reviews and code reviews, and testing. If vulnerabilities are discovered, we will respond promptly to address them.
Our code will be built into mission-critical systems. It must be reliable. We aim for zero bugs, using all of the techniques described under Security above to avoid, find, and eliminate bugs in our code. We also build in facilities that help diagnose problems (such as assertions and tracing in the debug build).
Without widespread conformance to PKI standards, complicated interoperability problems will arise. Therefore, libpkix will conform strictly to IETF RFC 3280. The first release will include all required features. Subsequent releases will add support for optional features, such as segmented CRLs, OCSP, etc.
Our code should work in as many environments as possible-- on almost any operating system, linked with almost any support libraries, and used for almost any application.
Our aim here is to ensure that our code can compile, link, and run in many environments. We will not and cannot change our APIs and implementation to meet the local conventions of each environment. If people don't like our APIs, they can build a shim layer on top of them. Or they can copy our code and change it so it meets their conventions. But we discourage this since it makes it hard for them to stay in synch with improvements and fixes.
We use a Portability Layer to ensure that our code takes advantage of local features. And we use callbacks so that applications can customize or extend our code. These features minimize the changes required to use libpkix.
Performance is always a concern. In many cases, our code will replace code that did very minimal checks on certificates, skipping constraints and even revocation checks. And this code will often be in the critical path, such as when verifying signed code before running it.
We will do everything we can to avoid reducing performance while adding more functionality (proper path processing and revocation checking). But we won't sacrifice our other priorities, listed above.
Tests show that during certificate chain validation and building, most time is spent downloading certificates and CRLs from the network and checking cryptographic signatures. Therefore, the first performance improvements will probably come through caching, prefetching, intelligent queries, and hardware crypto.
We will not make these improvements in the first release of libpkix. But we will design the library so that they can be made in the future. And we will try to avoid adding any unnecessary performance problems (caused by single-threaded code, poor implementation decisions, etc.).
Essentially, libpkix provides three features:
Validating a certificate chain is the most basic operation in a Public Key Infrastructure (PKI). Most secure Internet protocols (such as S/MIME, SSL/TLS, and IPsec/IKE) require that each participant validate the other party's certificate chain, thus securely determining their public key. Once this is done, the participants can communicate securely by encrypting or signing things with their key pairs. But without a valid certificate chain, none of this can proceed securely. Chain validation is also required when verifying a digital signature (as on signed code or documents) and in other cases, too.
Validating a certificate chain can be complicated. See RFC 3280 for all the details. But the essence is that each certificate in the chain is checked. Each certificate is a digitally signed statement saying what the subject's public key is. In a valid chain, the first certificate is issued by a trusted CA (the "trust anchor"). The subject of this certificate is the CA that issued the next certificate and so on until the last certificate, whose subject is called the "end entity". Each certificate can contain constraints that must be met by subsequent certificates (like "no names ending in sun.com"). If those constraints are met and the certificates are valid, then the chain is valid.
The party that validates a certificate chain is known as the "relying party".
Often, a relying party doesn't have a complete chain of certificates. This is especially true if the person who created the chain didn't know the relying party's trust anchor (common when the two parties are in different organizations). In this case, the relying party may try to build a valid chain.
Building a valid chain can also be complicated, but the concept is simple. The relying party kicks off the process by indicating what kind of chain it wants (like "a chain with a trust anchor of sun.com and an end entity of larry@example.com"). The chain builder then searches for a valid chain that meets those requirements. If no such chain can be found (or a specified time limit expires), the chain builder gives up. Otherwise, it returns the valid chain.
The end entity often has credentials (CRLs and certificates) that the relying party will need to build a valid chain. To increase efficiency, the end entity may want to gather these credentials and send them to the relying party. This is called collecting credentials. It's especially important if the end entity has credentials that the relying party needs but cannot obtain on their own (due to firewalls or other access control).
The libpkix library has two main components: the libpkix Portable Code, which implements the main functionality of the library (building and validating certificate chains), and the libpkix Portability Layer, which allows the Portable Code to access platform-specific functions like memory allocation through a platform-independent API. Here is a diagram of the libpkix architecture:
+-------------------------+ | Caller | +--------------+ | | libpkix PC | | +--------------+--+ | | libpkix PL | | +-----------------+-------+ | Underlying Platform | +-------------------------+
The Caller (an application or whatever other code is using libpkix) calls libpkix through its public APIs (defined in pkix.h). The Caller can also call the libpkix Portability Layer through its platform-independent public APIs (also defined in pkix.h). And the Caller can call the underlying platform (an operating system or portability runtime like NSPR) directly.
Note that the Caller may be an application, a library, a part of the operating system, a device driver, or whatever. libpkix doesn't worry about that.
The libpkix Portable Code provides the main public APIs for libpkix, which are declared in pkix.h and its subsidiary include files (omitting any identifiers that start with PKIX_PL, since those are part of the libpkix PL APIs). This code is designed to be as portable as possible. All non-portable code is factored out into the libpkix Portability Layer.
Note that the Caller can extend the functionality provided by the Portable Code by supplying callback functions to handle proprietary extensions and such.
The libpkix Portability Layer provides a standard API for features such as memory management and X.509 certificate parsing, which the libpkix Portable Code uses. There will be several implementations of the Portability Layer. Only one will be used for any particular instance of libpkix, of course.
Most of the functions in the PL libraries are typically only called by the libpkix Portable Code. But the Caller may use any of them as desired. And there are a few that the Caller must use, such as PKIX_PL_Object_DecRef.libpkix can be used in many ways. Here are a few examples.
A web browser (or any code that uses SSL/TLS) can use libpkix to validate the certificate chain supplied in the SSL handshake. If the chain can't be validated (maybe because the client and server have different trust anchors), libpkix can be used to try to build a valid certificate chain. Most SSL libraries don't support certificate chain building, bridge CA topologies, or policy constraints.
An S/MIME email client can use libpkix to build and validate a certificate chain to verify the signature on a signed email or securely determine the public key of a recipient before sending them an encrypted email. This client might also use libpkix to collect certificates to be included with a signed email.
Other sample applications of libpkix include validating signed code or documents, etc.
For more information on libpkix, read the libpkix Programmer's Guide.