1.2 Java Cryptography Architecture
Security, in particular cryptography, has always been a core API of Java, located in and below the Java, security package. The JCA was explicitly designed to provide and expose cryptographic operations to developers in need of such functionality. The advent of the Java2 SDK revealed a substantially improved JCA architecture, and we won't spend too much time living in the past discussing the way it used to be before the facelift. The architects of the JCA were given several broad but guiding design principles that had to be met.
■ Algorithm independence
■ Algorithm extensibility
■ Implementation independence
■ Implementation interoperability
Developers who require the use of cryptographic operations in their code dramatically benefit from these design principles. In fact, the elegance of the final design lies in its ability to let the developer decide what level of interaction they want to have with the underlying mechanics of the JCA, that is, a little or a lot. For example, developers can simply request a message digest, or they can be extremely specific and indicate that they require a specific message digest from an explicitly named cryptographic service provider, or simply a provider. A provider implements one or more Java packages that declare concrete implementations of well-known cryptographic features, like a key store that serves
■ 1.2 Java Cryptography Architecture 5
as the physical repository for secret keys and key pairs. While there will be the presence of Java classes included with the provider implementation, it is possible that their complete solution encompasses software and hardware. For example, the provider may use a Smart Card as the physical key store repository, and their key store implementation (software) knows how to interact with the Smart Card reader (hardware) attached to the computer.
The first guiding characteristic of the JCA architecture was Algorithm independence, which serves as a mechanism to classify cryptographic operations into well-known (read well-documented) categories. The JCA refers to each category as an engine, which is simply another name for a Java class. To ensure consistency between this book and the JCA documentation, we will use the engine nomenclature. The following is a complete list of engines found in the JCA:
■ MessageDigest produces a hash value for a given message
■ Signature produces a digital signature of a document
■ KeyPairGenerator produces a pair of keys that, for example, could sign a document
■ KeyFactory breaks down a key into its discrete parts and vice versa
■ KeyStore manages and stores various secret keys and key pairs
■ SecureRandom produces random numbers suitable for use in cryptology
■ AlgorithmParameters manages the encoding and decoding of the parameters for a given cryptographic algorithm
■ AlgorithmParameterGenerator generates a complete set of parameters required for a given cryptographic algorithm
■ CertificateFactory creates public key certificates and certificate revocation lists
■ CertPathBuilder establishes relationship chains between certificates
■ CertStore manages and stores certificates and certificate revocation lists
Each of these engines will be discussed in more detail and used throughout the text. To provide a high level of algorithm independence, each JCA engine uses the factory design pattern, as documented by Gamma et al in their landmark work . Essentially, each engine uses a factory method design, where the method is always declared static. Each factory method always returns back to the engine class itself (section 1.4 will briefly explore the Service Provider Interface [SPI] that provider's follow to see how this mechanism actually works). Here are two small code examples that demonstrate accessing the MessageDigest engine to obtain instances of different message digest algorithms (the algorithm parameter passed into any JCA or JCE engine is always case-insensitive):
MessageDigest md5Implementation = MessageDigest.getInstance("MD5"); MessageDigest shallmplementation = MessageDigest.getlnstance("SHA-1");
The decision by the JCA architects to use this design pattern in each engine class benefits developers and providers. As you read this, ongoing research on cryptographic operations is occurring at universities and cutting-edge companies around the globe. Providers can rest assured this research translates into newer, more powerful algorithms that can easily be accessed by Java developers around the globe. Developers benefit from the design because they do not have to worry about being locked into either single provider or obsolete algorithms. This leads into JCA's next design goal.
A by-product of the JCA's algorithm independence is its ability to support algorithm extensibility, which, simply stated, provides a mechanism for adding new algorithms that can be classified and exposed through one of the supported engines. Why should you be forced to use one and only one implementation of a cryptographic function? You should not. Algorithm extensibility can be thought of as capitalism for cryptographic algorithm engineers. If someone can successfully write a message digest that is five times faster and more collision-resistant to a greater bit-length than existing message digest implementations, then it should be extremely easy to access this new algorithm. Let's revisit the MessageDigest engine and see how we would access a new state-of-the-art message digest:
MessageDigest fastlmplementation = MessageDigest.getInstance("SuperFastHash");
Implementation independence represents the notion that developers should be free to simply say "I need an MD5 message digest" and immediately get an instance of a class that provides such functionality without requiring the invocation of a slue of provider specific methods. Implementation independence essentially offers the developer a choice of how to handle the presence of providers. Each of the earlier MessageDigest engine examples was implementation independent, but in some rare cases the developer may choose to waive this property and explicitly request a named provider. This example requests that the BC provider's MD5 message digest implementation be returned:
MessageDigest bcMd5Implementation = MessageDigest.getlnstance("MD5", "BC");
The Sun Java Runtime Environment (JRE) includes the SUN provider that implements one or more solutions for each of the JCA engines. However, it can't be stressed enough that the very nature of the JCA's architecture gives developers the opportunity to plug-in additional providers. Each provider determines what physical cryptographic implementations they wish to include in their solution. Theoretically, a provider may decide to only provide a single logical implementation of a random number generator, or they may opt to include a full spectrum of solutions for every engine.
NOTE: Throughout the rest of the text the term "developer" will refer to the downstream software engineer, that is, you or I. The term "provider" will refer to the upstream software engineer, that is, the engineers who developed the SUN provider that comes with the JCA.
There are numerous third party providers on the market; some are open source and others are commercial frameworks that require licensing. In the open source community,