Java Cryptography Architecture
For the purposes of digital signing of documents, verification of digital signatures, and handling digital certificates in the Java platform, the Java Cryptography Architecture (JCA) is used. JCA is a specification that gives the programmers a standard way to access cryptographic services, digital signatures, and digital certificates.
From an architectural point of view, JCA is designed to allow different implementations of various services from different software vendors. Such implementations of cryptographic services are called cryptographic service providers. Different software vendors implement different cryptographic service providers that support different sets of cryptographic algorithms. When working with JCA, the programmer specifies the names of the cryptographic service providers and the names of the cryptographic algorithms to be used. Algorithms are accessed by names and it is possible one and the same algorithm to have several implementations available in several different service providers.
The JCA specification establishes standards for the different types of cryptographic services and specifies the way to access the cryptographic algorithms. The implementation of the algorithms is not part of JCA and is left for the software vendors. Along with JDK 1.4 and any new version, Sun Microsystems distributes a standard implementation of JCA that is used as default if the programmer does not explicitly specify any other to use.
The JCA provides classes and interfaces for working with public and private keys, digital certificates, message signing, digital signatures verification, accessing protected keystores, and some other processes. These classes and interfaces are located in the standard packages java.security and java.security.cert. We will give a short description of the most important of them:
The Most Important Classes in JCA
java.security.KeyStore—gives access to protected keystores for certificates and passwords. The keystores are represented as set of entries and each entry has a unique name, called an alias. The KeyStore class has methods for loading keystore from a stream, storing a keystore to a stream, enumerating the entries in the keystore, extracting keys, certificates and certification chains, modifying entries in the keystore, and so forth. Two major formats for storing keystores are supported—PFX (according to the PKCS#12 standard) and JKS (Java Key Store format used by JDK internally). When we create objects of the class KeyStore, the format of the keystore should be given as a parameter. The possible values are “JKS” and “PKCS12“. Objects stored in a keystore can be accessed by the alias but for accessing keys a password also is required.
java.security.PublicKey—represents a public key. It holds the key itself, its encoding format, and the algorithm destined to be used with this key.
java.security.PrivateKey—represents a private key. It holds the key itself, its encoding format, and the algorithm destined to be used with this key.
java.security.cert.Certificate—it is an abstract class for all classes that represent digital certificates. It contains a public key and information for its owner. For representing each particular type of certificates (for example X.509, PGP, and so forth), an appropriate derived class is used.
java.security.cert.X509Certificate—represents an X.509 v.3 certificate. It provides methods for accessing its attributes—owner (Subject), issuer, public key of the owner, period of validity, version, serial number, digital signature algorithm, digital signature, additional extensions, and so forth. All the information in an X509Certificate object is available for reading only.
java.security.Signature—provides functionality for digitally signing and verifying digital signatures. When we create an instance of the Signature class, we specify the name of the algorithm for digital signatures that will be used. Several different algorithms, such as SHA1withRSA, SHA1withDSA, MD5withRSA, and so on are supported. The algorithm name is usually obtained by combining the name of some hashing algorithm with the name of some encrypting algorithm. When signing messages, we use the initSign() methods that take the private key, update() that takes the message for signing, and sign() that signs the message and returns the calculated signature. When we verify digital signatures, we use the initVerify() methods that take the public key for the verification, update() that takes the signed message, and verify() that takes the signature for verification and returns if the signature verification is successful (if the signature corresponds to the given message and public key).
java.security.cert.CertificateFactory—provides functionality for loading certificates, certification chains, and CRL lists from a stream. The generateCertificate() method that is purposed for reading a certificate from a stream expects the certificate to be DER-encoded (according to the PKCS#7 standard) and to be in a binary or text format (Base64-encoded). For reading a certification chain, the generateCertPath() method can be used and the encoding for the chain can be specified. Acceptable are encodings such as PkiPath, that corresponds to the ASN.1 DER sequence of certificates, and PKCS7 that corresponds to the PKCS#7 SignedData object (usually, such objects are stored in files with the standard extension .P7B). It is important to take into account the fact that PKCS7 encoding does not preserve the order of the certificates and, due to this particularity, we cannot use it for storing and reading certification chains. In Java, the only standard encoding for staring certification chains is PkiPath.
Java Certification Path API
The classes for verifying and building certification chains are located in the Java Certification Path API. This specification defines the two key classes: java.security.cert.CertPathValidator, purposed for verification of certification chains, and java.security.cert.CertPathBuilder, purposed for building certification chains from a given set of trusted Root-certificates and a given set of certificates that can be used as intermediate members of the chain.
We will briefly describe the most important classes from the Java Certification Path API that we will use in our further work:
java.security.cert.CertPath—represents a certification chain (ordered sequence of certificates). A standard certification chain starts from some end-user certificate, followed by zero or more certificates of intermediate certification authorities (CA), and ends with a Root-certificate of some top-level CA. Each certificate in the chain, except the last one, is issued and signed by the next certificate after it. The CertPath class is purposed to keep such correctly formed certification chains, but also can keep sets of certificates that do not form a certification chain.
java.security.cert.TrustAnchor—represents an end point of trust used in the process of verification of certification chains. It consists of a public key, a name of trusted certification authority, and a set of constraints that restrict the set of paths that can be checked. The set of constraints is empty by default and usually is not used. We can think about the TrustAnchor objects like the trusted Root-certificates used during the verification of certification chains; in other words, the Root-certificates of these CA that we trust unconditionally.
java.security.cert.PKIXParameters—it is a auxiliary class that describes the parameters of the PKIX algorithm, used for verification of certification chains. These parameters include a list of end points of trust for the verification (set of TrustAnchor objects), the date for which the verification should be performed, option that indicates if CRL lists should be used, and some other settings for the algorithm.
java.security.cert.CertPathValidator—provides functionality for verification of certification chains. When we create an instance of this class, we specify the verification algorithm that should be used. In most cases, the PKIX algorithm (see RFC-3280) is used. When verifying certification chains, the PKIX algorithm starts from the first certificate in the chain (the end-user certificate), continues with the next certificates, and finishes with the last one. It is necessary this last certificate from the chain to be signed by some certificate that is in the list of the end points of trust used for the verification (the set of TrustAnchor objects).
By specification, the chain that is to be verified should not contain the end point of trust; it should not finish with the Root-certificate of some CA but with the next before it. Due to this strange problem when we read some certification chain from a protected keystore before starting to verify it, it is necessary first to remove the last certificate from the chain. Otherwise, it is possible for the verification to fail despite the fact that the chain is valid.
The method for the verification of the validate() certification chain requires as input parameters the chain for verification (with the last certificate removed beforehand) and the parameters for the verification algorithm. For the PKIX algorithm, these parameters should be an instance of the PKIXParameters class that contains the list of the trusted CA Root-certificates that should be used as end points of trust.
My next article will provide an analysis of the most essential problems connected with the digitally signing of documents in Web-based systems and suggests a particular solution for them. Motivated is the need for using a digitally signed Java applet that is integrated with the Web application and signs the files on the client’s machine before uploading them to the server. The problems related to Java applets signing and interoperability between applets and Web browsers are examined. The mechanisms for verification of digital signatures, certificates, and certification chains and the possibilities for their particular application are also discussed and analyzed.
About the Author
Svetlin Nakov is part-time computer science lecturer in Sofia University,
Bulgaria. He has over 5 years of professional software engineering and
training experience and currently works as IT consultant in a leading
Bulgarian software company. His areas of expertise include Java and related
technologies, .NET Framework, network security, data structures and
algorithms, and programming code quality. More information on his research
background, skills and work experience is available from his home site