Monday, July 30, 2007

Java - Implement your own Service Provider Interface

As I continue development on the next release of Odyssi PKI, I've tried to apply some of the lessons I've learned regarding extensibility in object-oriented code. Version 0.1 of Odyssi PKI was relatively static, in terms of what formats and technologies were supported. The request types that the CA was able to handle were statically defined. While you could implement the X509CertificateRequest interface to define your own request format, the rest of the CA was unable to handle the request beyond a certain point. This made for a very limited, inflexible design. Want to add XKMS support? Get ready to rework a LOT of code. As a result, this lack of preparation for the future was high on my list of things to redesign for the next release.

As I sought out the best way to handle this problem, I realized that working with X.509 certificate request formats/objects is very similar to the way Java already handles certificates, encryption keys, and signature algorithms. In each case, the algorithms and formats for these objects is implemented through the use of a Service Provider Interface (SPI). In this example, the SPI is the implementation of the Java Cryptography Extension that is configured for the JVM. If your preferred algorithm is not supported, perhaps one of the other JCE implementations has it. All that is needed is to configure the JVM to recognize the JCE implementation, and ensure that it is located on your classpath.

Upon realizing this, I decided that there must be a way to take advantage of this type of design to handle X.509 certificate request objects. I needed to look no further than Java's ServiceLoader class. The ServiceLoader class is used to locate classes that implement a given SPI interface or extend an abstract SPI base class. In our example, we have several classes and interfaces that will be used to handle X.509 certificate request objects. The first interface is X509CertificateRequest. This interface defines several methods that are common to all types of certificate requests: getPublicKey(), getSignatureAlgorithm(), etc. We also define a class called X509CertificateRequestFactory. This class is a factory class that creates X509CertificateRequest objects. It is also the class that will make use of the ServiceLoader object. Lastly, we need to define an interface called X509CertificateRequestFactorySpi. This interface defines the methods that must be present in all of our SPI implementations. We'll look at X509CertificateRequestFactory first, as it is the most important.

The X509CertificateRequestFactory class has a static method called getInstance() that takes as its parameter a certificate request format. This format could be PKCS #10, XKMS, CRMF, or any other format that we like, so long as we have an SPI implementation for it. The constructor for this class is private, and takes a X509CertificateRequestFactorySpi object as its only parameter. This is done because the SPI implementation object will be used by the factory class to provide all of its underlying functionality.

Now that we've discussed the general structure of the factory class, let's look at the SPI interface and the methods it defines. Since the X509CertificateRequestFactorySpi class is used to provide all of the underlying functionality for the request factory, it must provide methods such as getCertificateRequest(...) that will be used to generate the request object itself from another object (byte[], String, XML document, etc.) In addition, another method isSupported() is defined, which takes as its only parameter a String, denoting the request format. This method returns true if the request format is supported by this SPI implementation.

So how do we use the ServiceLoader class? The ServiceLoader class looks for a provider configuration file located in the META-INF/services directory of the SPI implementation's JAR file. This file is the fully-qualified binary name of the services type. That is, if our request factory's full name is net.odyssi.certserv.x509.request.X509CertificateRequestFactory, the provider configuration file would be called net.odyssi.certserv.x509.request.X509CertificateRequestFactory, and would be located in the META-INF/services directory of the implementation JAR file. This file contains the name(s) of the SPI implementation class(es) contained within the JAR. So, for example, if we wanted to support PKCS #10 certificate requests, our provider configuration file may have one entry that reads:

net.odyssi.certserv.x509.request.impl.PKCS10CertificateRequestFactory

The PKCS10CertificateRequestFactory class provides all of the dirty work in generating an X509CertificateRequest object from an arbitrary source, such as an InputStream or a byte[]. Now that we've defined our provider configuration file with our SPI implementation class, we're ready to put the ServiceLoader class to work.

Within the getInstance() method of the X509CertificateRequestFactory class, we would have the following:


public static X509CertificateRequestFactory getInstance(String format) {

ServiceLoader sl = ServiceLoader.load(X509CertificateRequestFactorySpi.class);
Iterator it = sl.iterator();
while(it.hasNext()) {
X509CertificateRequestFactorySpi impl = (X509CertificateRequestFactorySpi)it.next();
if(impl.isSupported(format)) {
return new X509CertificateRequestFactory(impl);
}
}

return null;
}


In this method, the ServiceLoader sl = ServiceLoader.class(X509CertificateRequestFactorySpi.class) line instructs the ServiceLoader to find all implementations of the X509CertificateRequestFactorySpi interface. From here, we iterate through the returned results and see if an implementation can be found for the given request format. The returned X509CertificateFactory would then used the SPI implementation to provide its underlying functionality for getCertificateRequest(...), etc.

The ServiceLoader class provides a great mechanism for adding functionality and future-proofing your applications. Unlike other mechanisms such as Spring's dependency injection, no modifications are necessary for your code to take advantage of the new SPI implementation. The SPI implementation JAR file simply needs to be located on the classpath for the functionality to be available. By making use of a well defined SPI, it is possible to interchange components with your application without losing functionality. It also provides a great way of adding new capabilities to your already existing applications. For more information about ServiceLoader, take a look at the javadocs, or this article.

Thursday, July 26, 2007

PostgreSQL Replication - PGCluster

Earlier, I did an article on MySQL multi-master replication. While MySQL is a very fast database, able to provide the vast majority of features that most people need, there are definite shortcomings. As a result, I've begun looking more and more into PostgreSQL as my database of choice for my development work. The major shortcoming for me, however, has always been PostgreSQL's replication and failover support. The Slony-I project provides replication, however it is asynchronous and master-slave. In many environments, this just isn't good enough. I came across PGCluster, only to see on its project site that the latest code was for PostgreSQL 7.3.

At first, I thought that this was reason enough to abandon PostgreSQL and focus primarily on MySQL. However, I have since found that I am mistaken. The PGCluster site that I initially came across was an old site for the project. The new PGCluster site shows that PGCluster has been updated for PostgreSQL 8.2. This is great news for anyone looking to use PostgreSQL in an enterprise-wide capacity. PGCluster provides synchronous, multi-master replication. It is designed for high-availability, as well as load-balancing. While I haven't had a chance to install the latest version of PGCluster, I will be giving it a long, hard look in the near future.

While working on the Odyssi PKI project's next release, I've spent a bit more time focusing on the database aspect. Certificate Authority servers are typically considered to have high-security requirements, so they are often run using dedicated database servers. You wouldn't typically want your certificate data in the same database server with your general business data. Normally, the database instance is dedicated to the CA, and is hardened for security to prevent compromise. However, in situations where high numbers of certificates are issued, performance and scalability may become a factor. In a hosted PKI environment, for example, large numbers of certificates being issued may put a strain on the database server. In addition, CA servers have high availability requirements, particularly for CRL issuance, etc. The need for failover replication becomes apparent.

With PGCluster, it is possible to implement a database architecture that allows for the performance and reliability needed for this type of scenario. The example provided on the PGCluster website shows a simple replication scenario between 3 servers; two are located in close proximity, with the third connecting remotely over a VPN. When designing a database layout for an Odyssi PKI deployment, you would typically want to have a minimum of 2 databases, acting in a load-balanced, multi-master configuration. This will provide you with failover capability in the event of a server failure, as well as the ability to split processing between servers.

Starting with Odyssi PKI's next release, I plan to include some documentation outlining recommended architecture, best-practices, etc. for designing a PKI. Now that I have discovered PGCluster is not a dead project, I will be sure to use it in my examples.

Friday, July 20, 2007

Odyssi PKI - 1,000 Downloads

As of this morning, version 0.1 of Odyssi PKI has been downloaded 1,000 times. My thanks to all those who downloaded the program. I am currently working on version 0.2, which will represent a major rewrite and restructuring of the entire codebase. Version 0.1 was more intended as a proof-of-concept. In writing it, I was able to experiment with several technologies I hadn't played with before, like Spring or Hibernate. The development turned out to be a great learning experience.

Version 0.2 will be much better from a design and functionality standpoint. It is being rewritten from the ground up, and will include many functions and features that were missing in version 0.1. This includes:

  • Easier CA administration, through the use of a web-based admin console
  • Easier creation and management of Registration Authorities
  • Enhanced security requirements, such as enforcement of strong authentication in the Registration Authority console
  • CRL support
  • XKMS (possibly)
  • Enhanced certificate template support, allowing admins to define the characteristics of different types of certificates that will be generated
  • Support for numerous X.509 certificate extensions
  • Stand-alone release (using an included distribution of Apache Tomcat) for easier deployment and administration
This is just a small list of the features I plan on including in the next version. I have no firm dates on when it will be released, as I'm working on it in my free time (which is minimal, now that I have a toddler). However, work is progressing quickly and I hope to have something posted really soon.