- Example Programs and crypttool
- Cryptographic Services and Providers
- Cryptographic Keys
- Encryption and Decryption
- Message Digest
- Message Authentication Code
- Digital Signature
- Key Agreement
- Summary of Cryptographic Operations
- Cryptography with crypttool
- Limited versus Unlimited Cryptography
- Performance of Cryptographic Operations
- Practical Applications
- Legal Issues with Cryptography
- Summary
- Further Reading
Cryptographic Services and Providers
In Java API terminology, cryptographic services are programming abstractions to carry out or facilitate cryptographic operations. Most often, these services are represented as Java classes with names conveying the intent of the service. For example, digital signature service, represented by java.security.Signature class, creates and verifies digital signatures. However, not all services are directly related to cryptographic operations. Take the functionality to create certificates. This is provided as a certificate factory service through service class java.security.cert.CertificateFactory.
An instance of a service is always associated with one of many algorithms or types. The algorithm determines the specific sequence of steps to be carried out for a specific operation. Similarly, the type determines the format to encode or store information with specific semantics. For example, a Signature instance could be associated with algorithm DSA (Digital Signature Algorithm) or RSA (named after the initial letters of its three inventors: Rivest, Shamir, and Adleman). Similarly, a CertificateFactory instance could be associated with certificate type X.509.
While talking about cryptographic services and their algorithms or types, we use the term algorithm for brevity, knowing well that some services will have associated types and not algorithms.
The cryptographic service classes have a distinct structure to facilitate independence from algorithm and implementation. They typically do not have public constructors and the instances are created by invoking a static method getInstance() on the service class. The algorithm or type, represented as a string, must be specified as an argument to the getInstance() method. For exammple, the following statement creates a Signature instance with "SHA1WithDSA" algorithm.
Signature sign = Siganture.getInstance("SHA1WithDSA");
Besides the algorithm, one could also specify the implementation, also known as the provider, while creating an instance of the service. This is illustrated by passing the string "SUN", and identifying a specific provider as an additional parameter.
Signature sign = Siganture.getInstance("SHA1WithDSA", "SUN");
This structure of the API allows different implementation of the same service, supporting overlapping collections of algorithms, to exist within the same program and be accessible through the same service class. We talk more about this mechanism in the next section.
As noted earlier, certain services require an algorithm whereas others require a type. As we saw, signature service requires an algorithm whereas key store service requires a type. Roughly speaking, a service representing an operation needs an algorithm and a service representing an entity or actor needs a type.
Table 3-1 lists some of the J2SE v1.4 cryptographic services and supported algorithms, with brief descriptions. A comprehensive table can be found in Appendix B.
You may find the information in Table 3-1 a bit overwhelming, but don't be alarmed. We talk more about the various services and supported algorithms later in the chapter. Just keep in mind that cryptographic services have corresponding Java classes with the same names and algorithm identifiers passed as string arguments to method invocations.
The separation of service from algorithm, coupled with the API design where a specific service instance of a particular implementation is obtained by specifying them at runtime, is the key mechanism for algorithm and implementation independence. The visible service API classes, such as Signature and CertificateFactory, act only as a mechanism to get to the real implementation class, and hence are also referred to as engine classes. We find many examples of such classes later in the chapter and also in subsequent chapters.
Table 3-1. Java Cryptographic Services
Cryptographic Service |
Algorithms/Types |
Brief Description |
---|---|---|
SecureRandom |
SHA1PRNG |
Generates random numbers appropriate for use in cryptography. |
KeyGenerator |
DES, TripleDES, Blowfish |
Generates secret keys to be used by other services with the same algorithms. |
KeyPairGenerator |
DSA, RSA, DH |
Generates a pair of public and private keys to be used by other services with the same algorithms. |
MessageDigest |
SHA1, MD5 |
Computes the digest of a message. |
Mac |
HmacMD5, HmacSHA1 |
Computes the message authentication code of a message. |
Signature |
SHA1WithDSA, SHA1WithRSA |
Creates and verifies the digital signature of a message |
KeyStore |
JKS, JCEKS, PKCS12 |
Stores keys and certificates. |
CertificateFactory |
X509 |
Creates certificates. |
Cipher |
DES, TripleDES, Blowfish |
Encrypts and decrypts messages. |
KeyAgreement |
DH |
Lets two parties agree on a secret key without exchanging it over an insecure medium. |
Providers
As we noted, Cryptographic Service Providers, or just providers, are implementations of cryptographic services consisting of classes belonging to one or more Java packages. It is possible to have multiple providers installed within a J2SE environment, some even implementing the same service with the same algorithms. A program can either explicitly specify the provider name through an identifier string assigned by the vendor, or implicitly ask for the highest priority provider by not specifying any provider. In the last section, statement Signature.getInstance("SHA1withDSA") retrieves the implementation class of Signature implementing algorithm "SHA1withDSA" of the highest priority provider. In contrast, Signature.getInstance("SHA1withDSA", "SUN") retrieves the implementation class from the provider with the name "SUN".
A mechanism exists to specify priorities to these providers. We talk more about this mechanism in a subsequent section.
Note that JCA and JCE APIs define only the engine classes, most of them within java.security, javax.crypto and their various subpackages. The actual implementation of these classes is in various provider classes that come bundled with J2SE v1.4. It is also possible to install additional providers. We learn how to install additional providers in the section Installing and Configuring a Provider.
A few points about providers are worth noting. A provider doesn't have to implement all the services defined within JCA or JCE. Also, a provider can implement some services from one API and some from another. Which algorithms are to be supported for a specific service is also left to the provider. As you can see, the bundling of engine classes in separate APIs is quite independent of the packaging of classes within a provider.
Thankfully, there are APIs to access all the available providers, the services supported by them and other associated details. JSTK utility crypttool has a command to list the providers and related details. But before we get to that, let us understand the mechanism to achieve algorithm and implementation independence by looking at the internal structure of engine classes and their relationship with provider classes.
Algorithm and Implementation Independence
The best way to illustrate this independence is with the help of an example. Take the simple service of creating and verifying a digital signature, java.security.Signature. It has a static method getInstance() that takes the algorithm name and optionally, the provider name, as arguments and creates a concrete Signature object. The client program operates on this object, initializing it for signing by invoking the initSign() method or for verification by invoking the initVerify() method.
Under the hood, the static method getInstance() consults the singleton class java.security.Security to get the fully qualified name of the class associated with Signature service for the specified provider or, if the provider is not specified, the highest priority provider with Signature implementation for the specified algorithm. This implementation class must extend the abstract class java.security.SignatureSpi and provide the implementation of all the abstract methods. Once such a class name is found, the corresponding object is constructed using Java reflection and passed to the protected constructor of the Signature class. The Signature class keeps a reference of the newly created object in its member variable. Subsequent method invocations on Signature object operate on the object corresponding to the underlying implementation class.
The relationship of various classes and their runtime behavior is further illustrated in Figure 3-1. Class XYZProvider extends java.security.Provider and registers itself to the singleton class Security. This provider supplies the concrete implementation class XYZSignature, extending abstract class SignatureSPI.
Figure 3-1. Provider Architecture for Signature Class.
Although the preceding discussion and the diagram is for Signature service, the same is true for all other cryptographic services. The point to be noted is that even though the client program uses a well-known class, the selection of the actual class implementing the service happens at runtime. This makes adding new providers with new algorithms fairly straightforward and quite transparent to the client program. Well, at least within certain limits. We come across situations when this simple framework breaks down and the client must include code that knows about specific algorithms.
Listing Providers
As we said earlier, it is possible to query a J2SE environment for currently installed providers and the cryptographic services supported by them. This ability comes in handy in writing programs that adjust their behavior based on the capabilities available within an environment and also in troubleshooting.
Java class Security keeps track of all the installed providers in the form of Provider class instances and can be queried to get this information. A Provider object contains entries for each service and information on supported algorithms.
The example program ListCSPs.java, available in the examples directory for this chapter src\jsbook\ch3\ex1, lists all the installed cryptographic service providers, indicating their name and version.
Listing 3-1. Listing Cryptographic Service Providers
// File: src\jsbook\ch3\ex1\ListCSPs.java import java.security.Security; import java.security.Provider; public class ListCSPs { public static void main(String[] unused){ Provider[] providers = Security.getProviders(); for (int i = 0; i < providers.length; i++){ String name = providers[i].getName(); double version = providers[i].getVersion(); System.out.println("Provider["+i+"]:: " + name + " " + version); } } }
Compiling and running this program under J2SE v1.4.x, assuming that you are in the same directory as this file and either the CLASSPATH is not set or includes the current directory, produces the following output:
C:\ch3\ex1>%JAVA_HOME%\bin\javac ListCSPs.java C:\ch3\ex1>%JAVA_HOME%\bin\java ListCSPs Provider[0]:: SUN 1.2 Provider[1]:: SunJSSE 1.41 Provider[2]:: SunRsaSign 1.0 Provider[3]:: SunJCE 1.4 Provider[4]:: SunJGSS 1.0
You can infer from the output that J2SE v1.4.1 comes with five bundled providers and their names are: "SUN", "SunJSSE", "SunRsaSign", "SunJCE" and "SunJGSS". The same code is executed by utility crypttool with listp command, for listing providers.
Information about services implemented by a provider, aliases or different names corresponding to the same service, supported algorithms and other associated properties are stored within the Provider object as name value pairs. These name value pairs can be displayed by running the command "crypttool listp props". However, deducing information about each service from this listing is somewhat nontrivial and hence is made available through a separate option csinfo, for cryptographic service information. Let us look at the output of "crypttool listp csinfo" command in Listing 3-2.
Listing 3-2. Output of "bin\crypttool listp csinfo" command
C:\...\jstk>bin\crypttool listp -csinfo Provider[0]:: SUN 1.2 Cryptographic Services:: [0] MessageDigest : SHA1|SHA|SHA-1 ImplementedIn = Software MD5 ImplementedIn = Software [1] KeyStore : JKS ImplementedIn = Software [2] Signature: SHAwithDSA|DSAWithSHA1|DSA|SHA/DSA|SHA-1/DSA|SHA1withDSA| DSS|SHA1/DSA ImplementedIn = Software KeySize = 1024 [3] SecureRandom : SHA1PRNG ImplementedIn = Software [4] CertPathValidator : PKIX ImplementedIn = Software ValidationAlgorithm = draft-ietf-pkix-new-part1-08.txt [5] KeyPairGenerator : DSA ImplementedIn = Software KeySize = 1024 [6] CertificateFactory : X509|X.509 [7] AlgorithmParameterGenerator : DSA ImplementedIn = Software KeySize = 1024 [8] CertStore : LDAP ImplementedIn = Software LDAPSchema = RFC2587 Collection ImplementedIn = Software [9] AlgorithmParameters : DSA ImplementedIn = Software [10] KeyFactory : DSA ImplementedIn = Software [11] CertPathBuilder : PKIX ImplementedIn = Software ValidationAlgorithm = draft-ietf-pkix-new-part1-08.txt --------------------------------------------------------------- Provider[1]:: SunJSSE 1.41 Cryptographic Services:: [0] KeyStore : PKCS12 [1] Signature : MD5withRSA SHA1withRSA MD2withRSA [2] TrustManagerFactory : SunX509 [3] KeyPairGenerator : RSA [4] SSLContext : SSL SSLv3 TLS TLSv1 [5] KeyManagerFactory : SunX509 [6] KeyFactory : RSA --------------------------------------------------------------- Provider[2]:: SunRsaSign 1.0 Cryptographic Services:: [0] Signature : MD5withRSA SHA1withRSA MD2withRSA [1] KeyPairGenerator : RSA [2] KeyFactory : RSA --------------------------------------------------------------- Provider[3]:: SunJCE 1.4 Cryptographic Services:: [0] Cipher : DES Blowfish TripleDES|DESede PBEWithMD5AndTripleDES PBEWithMD5AndDES [1] KeyStore : JCEKS [2] KeyPairGenerator : DiffieHellman|DH [3] AlgorithmParameterGenerator : DiffieHellman|DH [4] AlgorithmParameters : TripleDES|DESede PBEWithMD5AndDES|PBE DES Blowfish DiffieHellman|DH [5] KeyAgreement : DiffieHellman|DH [6] KeyGenerator : HmacSHA1 TripleDES|DESede HmacMD5 DES Blowfish [7] SecretKeyFactory : TripleDES|DESede DES PBEWithMD5AndDES [8] KeyFactory : DiffieHellman|DH [9] Mac : HmacMD5 HmacSHA1 --------------------------------------------------------------- Provider[4]:: SunJGSS 1.0 Cryptographic Services:: ---------------------------------------------------------------
The output contains a wealth of information about various services supported by bundled providers. To interpret the results, follow the following simple rules:
-
The left side of ":" has the service name and the right side has the algorithms or types supported.
-
More than one algorithm or type name in the same line, separated by "|" imply aliases for the same name.
-
Some of the entries have additional information in the form of name-value pairs. An example of such a name-value pair is "ImplementedIn = Software" for a number of entries.
Once you get comfortable with the output, you have figured out a lot about various cryptographic services available with J2SE SDK, v1.4. Regarding the supported services, the following observations are worth noting:
-
Cipher service in provider "SunJCE" supports only symmetric algorithms. You cannot use this provider or any other bundled provider for public-key encryption.
-
"TripleDES" and "DESede" are aliases for the same algorithm.
-
Provider "SUN" has implementation for not only JCA services but also for a number of certificate validation services. We cover certificates and other related operations in Chapter 4, PKI with Java.
-
Three different types of KeyStore are supported, each one in a different provider: "JKS" in "SUN", "JCEKS" in "SunJCE" and "PKCS12" and "SunJSSE".
If you are working with a J2SE v1.4 compliant environment from a vendor other than Sun or have installed third-party providers, the output may be different. In either case, crypttool is a good tool to explore your environment.
Installing and Configuring a Provider
Installing a provider means placing the jar file(s) having the provider classes at appropriate locations and modifying the security configuration files so that the application program is able to load and execute the provider class files. This can be done by installing the provider as a standard Java extension by placing the jar file in the jre-home\lib\ext directory where jre-home is the Java runtime software installation directory. If you have J2SE SDK, v1.4 installed in c:\j2sdk1.4 then the jre-home will be c:\j2sdk1.4\jre. If you have only the JRE (Java Runtime Environment), then jre-home will be the root directory of the JRE installation, such as c:\Program Files\Java\jre1.4.0.
It is also possible to install a provider by just making the jar file available as a component in the bootclasspath of the program. This would require launching the client program with the command "java Xbootclasspath/a:provider-jar-file client-program-class". Just setting the CLASSPATH to include the provider jar file doesn't work.
If the provider is not installed as an extension and it is to be accessed by a program where a Security Manager is installed, then it must be granted appropriate permissions in the global or user-specific policy file java.policy. Recall that JVM running an applet will most likely have a Security Manager installed. The syntax of policy files and other details on granting specific permissions are covered in Chapter 5, Access Control. However, the brief description given below would suffice for installing a provider.
The global policy file resides in the directory jre-home\lib\security. The default location of the user specific policy file is in the user home directory. A sample policy statement granting such permission to a provider with the name "MyJCE" and class files in myjce_provider.jar kept in directory c:\myjce appears below:
grant codeBase "file:/c:/myjce/myjce_provider.jar" { permission java.lang.RuntimePermission "getProtectionDomain"; permission java.security.SecurityPermission "putProviderProperty.MyJCE"; };
After installation, a provider must be configured before it can be accessed by the client programs. This configuration is done either statically for the whole J2SE environment by modifying security properties file or dynamically for a given run of a program by invoking appropriate API calls from within the program.
Static configuration requires modification of the security properties file jre-home\lib\security\java.security. This file contains an entry for each provider, either bundled or installed, of the form
security.provider.n=master-class-name
Here n is a number specifying the priority, 1 being the highest, and master-class-name is the fully qualified name of the class in the provider jar file that extends the class java.security.Provider. To add a provider, simply insert an entry corresponding to the provider's master class with the appropriate priority number. Note that this may require some adjustment in the priority of existing providers.
For example, after installing Cryptix JCE provider (Cryptix JCE provider is an open source implementation available from http://www.cryptix.org with lowest priority, a portion of the java.security file would look like:
security.provider.1=sun.security.provider.Sun security.provider.2=com.sun.net.ssl.internal.ssl.Provider security.provider.3=com.sun.rsajca.Provider security.provider.4=com.sun.crypto.provider.SunJCE security.provider.5=sun.security.jgss.SunProvider #Added by Pankaj on July 22, 2002 for testing. security.provider.6=cryptix.jce.provider.CryptixCrypto
If you have more than one provider with the same priority, then the registration by the last provider overrides the previous registrations. Also, you must not have gaps within the priority number sequence, otherwise only the providers with consecutive priorities starting at 1 are registered. If there is a typo in the fully qualified name of the master class then the corresponding provider is not registered. All this happens silently without any warning, so you must be careful while modifying the security properties file.
You can check for successful configuration by running the command "crypttool listp". An invocation of this program should list all the providers, including the new ones, in the same order as the specified priority numbers. A frequent mistake, especially on development machines with multiple Java runtime software installed, is not realizing that the runtime environment of the java command may not be the same as the one you just configured. This is easily rectified by executing the command with %JAVA_HOME%\bin\java in place of java to launch the right JVM. Utility crypttool picks up the java executable from %JAVA_HOME%\bin directory, so make sure that the value of environment variable JAVA_HOME matches the Java installation you just configured.
A provider is dynamically configured within the client program code by calling addProvider or insertProviderAt method of Security class. For this to work, appropriate permission must be granted to the client code. For example, the following statement in the policy file java.policy provides the adequate permission to all code from directory c:\myclient.
grant codeBase "file:/c:/myclient/" { permission java.security.SecurityPermission "insertProviderAt.*"; permission java.security.SecurityPermission "addProvider.*"; };
Another thing to keep in mind is that the JCE engine authenticates the provider by verifying the signature on the code. The verification step looks for a signature by JCE Code Signing CA or a CA whose certificate has been signed by it. This is not a problem for bundled or commercial providers, as they are signed with appropriate private keys, but it becomes an issue with your own implementation of a JCE provider and most of the open source providers. For example, when I installed the "CryptixCrypto" provider and launched a program accessing one of its services, the exception java.security.NoSuchProviderException was thrown with a message saying: JCE cannot authenticate the provider CryptixCrypto.
If you do want to play with an unsigned provider during development, you can bypass the JCE engine by specifying an alternate JCE implementation. In the case of Cryptix provider, one way to do this is simply by removing the JDK's jce.jar file from jre-home\lib as the Cryptix provider comes with its own JCE classes.
Another option is to use the open source JCE provider from Legion of the Bouncy Castle, available from http://www.bouncycastle.org. This provider comes with an appropriate signed jar file and supports a wide variety of services and algorithms. For release 1.18 (the current one in March 2003), you should download a file named bcprov-jdk14-118.jar and place it in jre-home\lib\ext directory and add the following line in your java.security file:
security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider
If you do install this provider, run command "bin\crypttool listp" to get a list of active providers; and if this succeeds and shows BC as a provider, then command "bin\crypttool listp provider BC csinfo" to get a listing of available services and algorithms supported with this provider.
To recap, you must pay attention to the following while installing a security provider:
-
The provider jar file has been placed in the standard extension directory or its path is specified through Xbootclasspath argument to JVM.
-
The provider jar has been granted appropriate permissions. This is required only if the program is running under a Security Manager. This is likely to be the case if your program is running within a container.
-
An appropriate CA has signed the provider jar.
Why is installing a security provider so complicated? Compromise of a security provider can easily compromise all the security provided by cryptography. Hence, it is imperative that proper safeguards are in place. A number of the above mentioned steps are about ensuring that only trusted code is used as a security provider.
Why would someone want to use a third-party provider? Here are some good reasons:
-
You need your Java application to be integrated into an existing environment that uses algorithms and/or types not supported by bundled providers.
-
You bought special hardware to speed up your application but use of this hardware requires using the vendor's provider.
-
The algorithms supported by the bundled providers are not strong enough for your requirements.
-
You live in a country where you can only download the J2SE SDK with "limited" cryptography but want to use "unlimited" cryptography. We will talk more about this later in the section Limited versus Unlimited Cryptography.
-
You invented a new algorithm or better implementation of an existing algorithm and want to use it.
Whether you use the provider supplied with J2SE v1.4.x SDK or install your own, the programs using the cryptographic services remain the same.