Cryptography With Java
Cryptography With Java
C H A P T E R 3
Cryptography with
Java
ryptography, or the art, science and mathematics of keeping messages secure, is at the
C heart of modern computer security. Primitive cryptographic operations such as one-way
hash functions, also known as message digests, and encryption, either with symmetric or asym-
metric algorithms, form the basis for higher level mechanisms such as MAC (Message Authen-
tication Code), digital signature and certificates. At yet another level, these are merely building
blocks for security infrastructure consisting of PKI, secure communication protocols such as
SSL and SSH, and products incorporating these technologies.
The study of principles and algorithms behind these cryptographic operations and security
protocols is fascinating but of little practical relevance to a Java programmer. A typical Java pro-
grammer programs at a much higher level, dealing mostly with the APIs, configuration options,
proper handling of cryptographic entities such as certificates and keystores, and interfacing with
other security products to satisfy the application’s security needs. At times, there may be deci-
sions to be made with respect to the most appropriate mechanism, algorithms, parameters and
other relevant aspects for solving the problem at hand. At other times, the challenge may be to
design the application so that it can be deployed under different situations to satisfy different
security and performance needs. At yet other times, the primary objective may be simply to
achieve the best possible performance, scalability and availability of the application without
compromising the level of security by selecting the right security products. Our discussion of
cryptography with Java in this and subsequent chapters is structured around this notion of use-
fulness and practicality to a typical Java programmer.
Two Java APIs, JCA (Java Cryptography Architecture) and JCE (Java Cryptography
Extension) both part of J2SE SDK v1.4, define the general architecture and specific services for
cryptographic operations. Among these, JCA was introduced first and specifies the architectural
framework for cryptographic support in Java. It also includes Java classes for digital signature,
45
Ch03.fm Page 46 Tuesday, August 5, 2003 2:39 PM
message digest and other associated services. JCE classes follow the same general structure as
JCA classes, and include classes for encryption and decryption, MAC computation and a few
others. We discuss the JCA architectural framework and explore various cryptographic services
available with JCA and JCE in this chapter. Toward this, we develop simple programs making
use of these APIs and look at their source code.
Though we talk about some of the JCA and JCE APIs and present code fragments, the dis-
cussion of Java interfaces, classes and methods is anything but exhaustive. Our intent is to get a
better view of the overall picture and understand their inter-relations. If you do need the com-
plete information on any specific topic, refer to the J2SE SDK Javadocs and the respective spec-
ification documents. Keep in mind that the purpose of this chapter is to make you, a Java and
J2EE programmer, feel at home with cryptographic capabilities of Java and not to make you an
expert on developing security software.
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 Signa-
ture Algorithm) or RSA (named after the initial letters of its three inventors: Rivest, Shamir,
and Adleman). Similarly, a CertificateFactory instance could be associated with certif-
icate 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 algo-
rithms.
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 pro-
vider, 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 repre-
senting 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
Ch03.fm Page 48 Tuesday, August 5, 2003 2:39 PM
Providers
As we noted, Cryptographic Service Providers, or just providers, are implementations of crypto-
graphic 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 pro-
vider by not specifying any provider. In the last section, statement Signature.getIn-
stance(“SHA1withDSA”) retrieves the implementation class of Signature implementing
algorithm “SHA1withDSA” of the highest priority provider. In contrast, Signature.get-
Instance(“SHA1withDSA”, “SUN”) retrieves the implementation class from the pro-
vider with the name “SUN”.
Ch03.fm Page 49 Tuesday, August 5, 2003 2:39 PM
A mechanism exists to specify priorities to these providers. We talk more about this mech-
anism 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 implementa-
tion 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 sec-
tion 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 inde-
pendent 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 algo-
rithm and implementation independence by looking at the internal structure of engine classes
and their relationship with provider classes.
Java.security.SignatureSpi java.security.Provider
XYZSignature
XYZProvider
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 hap-
pens 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 pro-
grams 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, indi-
cating their name and version.
Ch03.fm Page 51 Tuesday, August 5, 2003 2:39 PM
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 corre-
sponding 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.
Ch03.fm Page 52 Tuesday, August 5, 2003 2:39 PM
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::
---------------------------------------------------------------
Ch03.fm Page 54 Tuesday, August 5, 2003 2:39 PM
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 crypto-
graphic services available with J2SE SDK, v1.4. Regarding the supported services, the follow-
ing observations are worth noting:
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.
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 regis-
tered. 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 environ-
ment 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 addPro-
vider 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:\mycli-
ent.
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 pro-
viders, 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
Ch03.fm Page 57 Tuesday, August 5, 2003 2:39 PM
Cryptographic Keys 57
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 ser-
vices 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.
• 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 pro-
grams using the cryptographic services remain the same.
Cryptographic Keys
Secret keys, a stream of randomly generated bits appropriate for the chosen algorithm and pur-
pose, are central to a number of cryptographic operations. In fact, much of the security offered
Ch03.fm Page 58 Tuesday, August 5, 2003 2:39 PM
by cryptography depends on appropriate handling of keys, for the algorithms themselves are
publicly published. What it means is that a key that can be easily compromised, computed,
guessed, or found by trial and error with reasonable effort offers little or no security, no matter
how secure the algorithm. Strength of security, or the degree of difficulty in determining the
right key by a brute force exhaustive search, depends on the size and randomness of the key. For
all these reasons, it is imperative that due diligence is exercised in selecting the right keys, using
them properly and protecting them adequately.
However, not all cryptographic operations require secret keys. Certain operations work
with a pair of keys—a private key that must be kept secret and a corresponding public key that
can be shared freely.
The Java platform offers a rich set of abstractions, services and tools for generation, stor-
age, exchange and use of cryptographic keys, simplifying the problem to careful use of these
APIs and tools.
Cryptographic Keys 59
Generating Keys
A Key object is instantiated by either internal generation within the program or getting the
underlying bit stream in some way from an external source such as secondary storage or another
program. Let us look at how keys are generated programmatically.
A SecretKey for a specific algorithm is generated by invoking method gener-
ateKey() on javax.crypto.KeyGenerator object. KeyGenerator is an engine
class implying that a concrete object is created by invoking the static factory method getIn-
stance(), passing the algorithm name and optionally, the provider name as arguments. After
creation, the KeyGenerator object must be initialized in one of two ways—algorithm inde-
pendent or algorithm specific. Algorithm independent initialization requires only the key size in
number of bits and an optional source of randomness. Here is example program GenerateS-
ecretKey.java that generates a secret key for DES algorithm.
Run the same program again. Do you get the same key material? No, you get a different
value. How is this explained? The KeyGenerator uses the default implementation of
SecureRandom as a source of randomness and this generates a different number for every
execution.
Generation of public and private key pair follows a similar pattern with class KeyGener-
ator replaced by java.security.KeyPairGenerator and method SecretKey
generateKey() replaced by KeyPair generateKeyPair(). Example program Gen-
erateKeyPair.java illustrates this.
Storing Keys
Keys need to be stored on secondary storage so that programs can access them conveniently and
securely for subsequent use. This is accomplished through the engine class java.secu-
rity.KeyStore. A KeyStore object maintains an in-memory table of key and certificate
entries, indexed by alias strings, allowing retrieval, insertion and deletion of entries. This object
can be initialized from a file and saved to a file. Such files are known as keystore files. For secu-
rity reasons, keystore files and, optionally, individual entries, are password protected.
The following code fragment illustrates initializing a KeyStore object from a JCEKS
keystore file test.ks protected with password “changeit”.
FileInputStream fis = new FileInputStream(“test.ks”);
KeyStore ks = KeyStore.getInstance(“JCEKS”);
ks.load(fis, “changeit”.toCharArray());
Different providers or even the same provider supporting different keystore types can store
keys in different types of persistent store: a flat file, a relational database, an LDAP (Light-
weight Data Access Protocol) server or even MS-Windows Registry.
J2SE v1.4 bundled providers support flat file formats JKS and JCEKS. JKS keystore can
hold only private key and certificate entries whereas JCEKS keystore can also hold secret key
entries. There is also read-only support for keystore type PKCS12, allowing import of Netscape
and MSIE browser certificates into a Java keystore
Java keystore types JKS and JCEK work okay for development and simple applications
with small number of entries, but may not be suitable in the production environment that is
required to support a large number of entries. Consider investing in a commercial provider for
such uses.
Java platform includes a simple command line utility keytool to manage keystores. The
primary purpose of this tool is to generate public and private key pairs and manage certificates
for PKI based applications. We talk more about this tool in Chapter 4, PKI with Java.
Algorithms
A number of encryption algorithms have been developed over time for both symmetric and
asymmetric cryptography. The ones supported by the default providers in J2SE v1.4 are: DES,
Ch03.fm Page 62 Tuesday, August 5, 2003 2:39 PM
Java API
Java class javax.crypto.Cipher is the engine class for encryption and decryption ser-
vices. A concrete Cipher object is created by invoking the static method getInstance()
and requires a transform string of the format algorithm/mode/padding (an example
string would be “DES/ECB/PKCS5Padding”) as an argument. After creation, it must be ini-
tialized with the key and, optionally, an initialization vector. After initialization, method
update() can be called any number of times to pass byte arrays for encryption or decryption,
terminated by a doFinal() invocation.
byte[] dataBytes =
Ch03.fm Page 64 Tuesday, August 5, 2003 2:39 PM
Message Digest 65
byte[] dataBytes =
"J2EE Security for Servlets, EJBs and Web
Services".getBytes();
Message Digest
Message digests, also known as message fingerprints or secure hash, are computed by applying
a one-way hash function over the data bits comprising the message. Any modification in the
original message, either intentional or unintentional, will most certainly result in a change of the
digest value. Also, it is computationally impossible to derive the original message from the
digest value. These properties make digests ideal for detecting changes in a given message.
Compute the digest before storing or transmitting the message and then compute the digest after
loading or receiving the message. If the digest values match then one can be sure with good con-
fidence that the message has not changed. However, this scheme fails if a malicious interceptor
has access to both the original message and its digest. In this case the interceptor could easily
alter the message, compute the digest of the modified message and replace the original digest
with the new one. The solution, as we see in the next section, is to secure the message digest by
encrypting it with a secret key.
Ch03.fm Page 66 Tuesday, August 5, 2003 2:39 PM
A common use of message digests is to securely store and validate passwords. The basic
idea is that you never store the password in clear-text. Compute the message digest of the pass-
word and store the digest value. To verify the password, compute its digest and match it with the
stored value. If both values are equal, the verification succeeds. This way no one, not even the
administrator, gets to know your password. A side effect of this mechanism is that you cannot
get back a forgotten password. This is not really as bad as it sounds, for you can always get it
changed to a temporary password by an administrator, and then change it to something that only
you know.
Message digests of messages stored in byte arrays are computed using engine class
java.security.MessageDigest. The following program illustrates this.
MessageDigest md = MessageDigest.getInstance("SHA1");
FileInputStream fis = new FileInputStream(datafile);
byte[] dataBytes = new byte[1024];
int nread = fis.read(dataBytes);
while (nread > 0) {
md.update(dataBytes, 0, nread);
nread = fis.read(dataBytes);
};
byte[] mdbytes = md.digest();
System.out.println("Digest(in hex):: " +
Util.byteArray2Hex(mdbytes));
}
}
A concrete, algorithm-specific MessageDigest object is created following the general
pattern of all engine classes. The invocation of update() method computes the digest value
and the digest() call completes the computation. It is possible to make multiple invocations
of update(byte[] bytes) before calling the digest() method, thus avoiding the need
to accumulate the complete message in a single buffer, if the original message happens to be
fragmented over more than one buffer or cannot be kept completely in main memory. This is
likely to be the case if the data bytes are being read from a huge file in fixed size buffers. In fact,
convenience classes DigestInputStream and DigestOutputStream, both in the
package java.security, exist to compute the digest as the bytes flow through the associated
streams.
Ch03.fm Page 67 Tuesday, August 5, 2003 2:39 PM
The verification or check for integrity of the message is done by computing the digest
value and comparing this with the original digest for size and content equality. Class Mes-
sageDigest even includes static method isEqual(byte[] digestA, byte[]
digestB) to perform this task.
Theoretically, because a much larger set of messages get mapped to a much smaller set of
digest values, it is possible that two or more messages will have the same digest value. For
example, the set of 1 KB messages has a total of 2(8*1024) distinct messages. If the size of the
digest value is 128 then there are only 2128 different digest values possible. What it means is that
there are, on the average, 2(8*1024—128) different 1KB messages with the same digest value.
However, a brute-force search for a message that results in a given digest value would still
require examining, on the average, 2127 messages. The problem becomes a bit simpler if one
were to look for any pair of messages that give rise to the same digest value, requiring, on the
average, only 264 attempts. This is known as the birthday attack, deriving its name from a
famous mathematics puzzle, whose result can be stated as: there is more than a 50 percent
chance that you will find someone with the same birthday as yours in a party of 183 persons.
However, this number drops to 23 for any pair to have the same day as their birthday.
The providers bundled with J2SE v1.4 support two message digest algorithms: SHA
(Secure Hash Algorithm) and MD5. SHA, also known as SHA-1, produces a message digest of
160 bits. It is a FIPS (Federal Information Processing Standard) approved standard. In
August 2002, NIST announced three more FIPS approved standards for computing message
digest: SHA-256, SHA-384 and SHA-512. These algorithms use a digest value of 256, 384 and
512 bits respectively, and hence provide much better protection against brute-force attacks. MD5
produces only 128 bits as message digest, and is considerably weaker.
Another difference between classes for MAC and message digest is that there are no
MacInputStream and MacOutputStream classes.
Ch03.fm Page 68 Tuesday, August 5, 2003 2:39 PM
The example program to illustrate MAC computation is similar to the one for Message
Digest.
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(56); // 56 is the keysize. Fixed for DES
SecretKey key = kg.generateKey();
Digital Signature
Encrypting the digest of a message with the private key using asymmetric cryptography creates
the digital signature of the person or entity known to own the private key. Anyone with the corre-
sponding public key can decrypt the signature to get the message digest and verify that the mes-
sage digest indeed corresponds to the original message and be confident that it must have been
encrypted with the private key corresponding to the public key. As the private key is not made
Ch03.fm Page 69 Tuesday, August 5, 2003 2:39 PM
Digital Signature 69
public, it can be deduced that the message was signed by the owner of the private key. Generally,
these are the same properties as the ones associated with a signature on paper.
Note that use of a digital signature requires a digest algorithm and an asymmetric encryp-
tion algorithm.
Algorithms
Currently, there are three FIPS-approved digital signature algorithms: DSA, RSA and ECDSA
(Elliptic Curve Digital Signature Algorithm). More information on these algorithms can be
found at http://csrc.nist.gov/CryptoToolkit/tkhash.html.
Java API
Java class java.security.Signature represents the signature service and has methods
to create and verify a signature. Like any engine class, a concrete Signature object is created by
invoking the static method getInstance(). For signing data bytes, it must be initialized
using initSign() with the private key as an argument. A subsequent signature creation oper-
ated, through the method sign(), produces the signature bytes. Similarly, the verification
operation, through the method verify(), after initialization using initVerify() with the
public key as the argument, verifies whether a particular signature has been created using the
corresponding private key or not.
The example program SignatureTest.java illustrates signing and verification.
return sig.sign();
}
private static boolean verify(String datafile, PublicKey pubKey,
String sigAlg, byte[] sigbytes) throws Exception {
Signature sig = Signature.getInstance(sigAlg);
sig.initVerify(pubKey);
FileInputStream fis = new FileInputStream(datafile);
byte[] dataBytes = new byte[1024];
int nread = fis.read(dataBytes);
while (nread > 0) {
sig.update(dataBytes, 0, nread);
nread = fis.read(dataBytes);
};
return sig.verify(sigbytes);
}
public static void main(String[] unused) throws Exception {
// Generate a key-pair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(512); // 512 is the keysize.
KeyPair kp = kpg.generateKeyPair();
PublicKey pubk = kp.getPublic();
PrivateKey prvk = kp.getPrivate();
Key Agreement
Secure exchange of data over an insecure channel requires the data packets to be encrypted by
the sender and decrypted by the receiver. In such a scenario, one could use symmetric cryptogra-
phy for encryption and decryption but that would require the communicating parties to use the
same secret key. This is not viable for an open communication medium like the Internet that
must allow secure exchange among unknown parties without prior agreement to share secret
keys.
Ch03.fm Page 71 Tuesday, August 5, 2003 2:39 PM
Key Agreement 71
One might think that public key cryptography is ideally suited to solve this problem. The
sender would do the encryption using the public key of the recipient and the recipient would
decrypt the message using its own private key. The whole scheme would only require each party
to have or generate its own key pair and share the public key with others.
In practice, this approach has a small problem. The performance overhead of public key
encryption and decryption is unacceptably high. However, there is a solution to this problem, for
the performance issue can be addressed by generating a secret key for encrypting the actual data
and encrypting the secret key with the public key. The recipient could now use his or her private
key to decrypt the secret key and then use this key to decrypt the data using much faster symmet-
ric decryption.
Even this scheme requires that every entity must have the public key of all other entities
with whom it wishes to communicate. This precondition will preclude secure communication
between parties that do not know each other beforehand.
One solution is to use the public key cryptography and a key agreement mechanism to
agree upon a secret key in such a way that the key itself is never transmitted and cannot be inter-
cepted or deduced from the intercepted traffic. Once such a secret key is agreed upon, it can be
used for data encryption and decryption.
J2SE v1.4 supports key agreement operations through the service class
javax.crypto.KeyAgreement. We do not get into the programmatic usage details of this
class but instead look at one of the key agreement algorithms supported by J2SE v1.4—Diffie-
Hellman.
1. The initiator generates a public and private key pair and sends the public key, along
with the algorithm specification, to the other party.
2. The other party generates its own public and private key pair using the algorithm speci-
fication and sends the public key to the initiator.
3. The initiator generates the secret key using its private key and the other party’s public
key.
4. The other party also generates the secret key using its private key and the initiator’s
public key. Diffie-Hellamn algorithm ensures that both parties generate the same secret
key.
This sequence of steps is pictorially illustrated in Figure 3-2.
As we see in Chapter 6, Securing the Wire, this mechanism is used by SSL to agree upon a
shared secret key and secure the exchange of data.
Ch03.fm Page 72 Tuesday, August 5, 2003 2:39 PM
Alice Bob
generate
pub-key(A) and
prv-key(A)
pub-key(A), algo. generate
pub-key(B) and
prv-key(B)
pub-key(B), algo.
1. Ensure that the data has not been accidentally corrupted. Alice computes the digest
of the data and sends the digest value along with the message. Bob, after receiving the
data, computes the digest and matches it against the received value. A successful match
implies that the data is not corrupted.
2. Ensure that the data has not been maliciously modified. In this case, Alice cannot
rely on digest value, as the malicious middleman could simply replace the digest value
after modifying the data. So, she arranges to share a secret key with Bob and uses this
key to compute the MAC of the data. Not being in the possession of the secret key, the
middleman now cannot replace the MAC.
3. Ensure that the data remains confidential. Alice shares a secret key with Bob and
uses this key to encrypt the data with a symmetric encryption algorithm.
4. Ensure that the data remains confidential but without a shared secret key. Alice
has Bob’s public key. She uses this key to encrypt the data. Bob decrypts it using his
private key.
5. Prove to Bob that the data has come from Alice. Alice uses her private key to sign
the data. Bob can verify the signature using her public key and be sure that the data
Ch03.fm Page 73 Tuesday, August 5, 2003 2:39 PM
Alice
Data
sym- asym-
digest MAC signature
ciphertext ciphertext
sym- asym-
digest MAC signature
ciphertext ciphertext
Data
Bob
indeed originated from Alice. This also guarantees that the data has not been modified
in transit.
6. Prove to Bob that the data came from Alice and keep it confidential. Alice signs the
data with her private key and then encrypts it using Bob’s public key. Bob decrypts it
using his private key and verifies the signature using Alice’s public key.
J2SE SDK includes classes to carry out these operations programmatically but includes no
ready-made tool. As mentioned earlier, JSTK software includes a tool called cryptool written
using these classes. The next section talks about this tool. You may find it useful to experiment
with different input values for cryptographic operation, keys, algorithm and input data.
Ch03.fm Page 74 Tuesday, August 5, 2003 2:39 PM
By default, crypttool uses the DSA algorithm with a key size of 512 bits to generate
key pair and SHA1withDSA algorithm for signature. Also, it knows to pick the private key for
signature creation and public key for verification from a file having a serialized KeyPair
object. In a real application, though, one would keep them in separate files and in a format
understood by widely used programs. The private and public keys are typically stored in a key-
store with the private key protected by a password and the public key embedded in a certificate.
crypttool can pick up private and public key from a keystore as well.
Use of crypttool to compute message digest and MAC is left as an exercise.
If you are within the U.S. and want to use a larger keysize, you can download JCE Unlim-
ited Strength Jurisdiction Policy files from Sun’s J2SE download page and replace the default
policy jar files in jre-home\lib\security directory with downloaded jar files. The
default_local.policy file for unlimited strength is shown below.
// File: default_local.policy
// Country-specific policy file for countries with no limits on
// crypto strength.
grant {
// There is no restriction to any algorithms.
permission javax.crypto.CryptoAllPermission;
};
The jar files having these policy files are signed and hence cannot be modified without
detection.
A brief overview of legal issues associated with cryptography can be found in the section
Legal Issues with Cryptography, on page 79.
These figures indicate that Blowfish is the fastest among all the reported algorithms. Inter-
estingly, the decryption is significantly slower than encryption with Blowfish.
How about signature creation and verification performance? Table 3-3 has the measure-
ment figures for signing and verifying the same document.
Ch03.fm Page 78 Tuesday, August 5, 2003 2:39 PM
It is quite obvious that signing and verifying are significantly faster than encryption and
decryption operations. Also, SHA1WithRSA is almost one and a half times faster than
SHA1WithDSA.
These measurements are taken with the “crypttool bench” command. Use it
within your environment to compare different algorithms and estimate crypto overhead for your
application.
There are many ways to speed up the performance of these operations. A commonly used
mechanism, especially for large volume applications, is to use special cryptographic accelerator
cards. As most of the cryptographic algorithms can have extremely efficient hardware-based
implementations, an order of magnitude improvement is not uncommon.
Practical Applications
Now that we have looked at most of the basic cryptographic services and have an idea of how
they work, let us ask this question: What good are they? What can they do for us? As we have
been saying all along, despite the abstract nature, cryptography is quite useful and can do pretty
mighty things.
Confidentiality. Encrypted information is virtually hidden from everyone who doesn’t
know how to decrypt it or doesn’t have the appropriate key. This makes it possible to share secret
information over insecure communication channels, such as the Internet, thus providing confi-
dentiality even though the network itself is quite open. The same applies to data stored on disk.
Encryption ensures confidentiality of stored data even if the computer itself gets compromised
or stolen.
Integrity. There are times when you want to detect intentional tampering or unintentional
corruption of data. This goal can be achieved by computing the digest value of the original and
the current data. A mismatch would indicate some sort of change in the data. If the threat of
intentional tampering exists for both the data and the digest value then MAC can be used as the
detection mechanism.
Non-repudiation. A physical signature on paper, along with the visually observable state
of the paper, proves the authenticity of the document and is legally binding. Public key cryptog-
raphy-based digital signature performs the same role for electronic documents.
Ch03.fm Page 79 Tuesday, August 5, 2003 2:39 PM
Although these are quite powerful capabilities, in reality, things are more complex. Pass-
words are prone to be easily guessed or to be captured by tricks or “stolen” by social engineer-
ing. The use of a private key by a computer program is not always same as the use by the stated
owner of the key. A compromised computer can trick a human user into doing things that the
user may never have done knowingly. Finally, the cryptography itself is not fully resistant to
attacks. Someone with good skill, sufficient determination and ample computing power can
defeat most cryptographic protection.
But before we proceed to dismiss cryptography as useless junk, let us think about the
physical world. Every now and then, the best-kept secrets become “public” due to careless-
ness or malicious intent of the parties in the know. Cases of forged documents or signatures
are not unheard of. Even the most wary are not immune from being duped by con artists. All
this is possible and happens more frequently than we care to admit. Still, life goes on. There
are safeguards, mostly in form of a legal and judicial system, to keep the occurrences of such
instances low.
The cyber world is no different. In the absence of a better technology, we have to rely on
cryptography and use it carefully.
However, cryptography by itself is quite inadequate for real life use. Exchange of
encrypted files may work as means to share secret information in a small group of people that
agree on the algorithm and a secret key or password beforehand, but is useless when the commu-
nicating parties may not know each other. Use of a digital signature as a means of proving
authenticity requires that someone with appropriate authority should be able to substantiate the
ownership claim of the private key. In cases where a private key is compromised, there has to be
a way to invalidate the key and minimize the damage. Even transportation of keys requires defin-
ing a format so that software from different vendors can use them appropriately.
The solution to these and many other related problems lies in using agreed upon standards
to store and communicate cryptographic information: conventions, policies and regulations for
trust relationships and other related aspects of doing business. As we see in subsequent chapters,
PKI standards, communication protocols like SSL and identification and authentication services
define exactly such standards and conventions.
These are only broad guidelines that you must consider before deploying solutions using
cryptographic components. Most of the time, it is the vendor of the security products who has to
worry about these, but don’t take chances. Extra care is required if you plan to use open source
software freely available for download over the Internet, as you don’t have the vendor to do the
homework for legal compliance. When in doubt, consult legal counsel for proper guidance.
Notwithstanding, anything stated in this section or in the whole book, the author and pub-
lisher take no responsibility for any legal consequences resulting from following the advice
offered or using any of the security techniques in this book. The laws regulating cryptography
are complex, jurisdiction-dependent and keep changing all the time. It is your responsibility to
ensure that you remain within the four walls of the law.
Ch03.fm Page 81 Tuesday, August 5, 2003 2:39 PM
Summary 81
Summary
Java cryptographic services are defined independent of the underlying algorithms and
implementations. This supports extensibility through the addition of newer algorithms in sepa-
rate provider implementation without changing or adding the programmer visible classes. This
extensibility is achieved through an architecture where the service engine classes expose the
functionality, but hide the coupling with the implementation class. It also allows security capa-
bilities to be extended by installing and configuring third-party providers. The same architecture
is used by all security APIs, bringing a good deal of uniformity and ease of use.
Keys—secret key for symmetric encryption, and public-private key pairs for asym-
metric encryption—are central to a number of cryptographic operations. Proper generation
and handling of keys is essential for realizing the security offered by cryptography. Java Security
API contains classes to handle keys as Java objects and has services to generate, store and load
these keys. An important point to remember is that not all secret keys or public-private key pairs
have the same structure or are generated by the same process—key pair used by RSA cannot be
used by DSA and vice versa.
JCA and JCE contain the engine classes for basic cryptographic operations. Exam-
ples include Signature class for creating and verifying digital signature, Cipher class for
symmetric and asymmetric encryption and decryption, MessageDigest class for computing
and verifying message digest, Mac class for computing and verifying MAC and KeyAgree-
ment class for key agreement operations. Encryption provides message confidentiality and
digest helps in detecting changes to the message. MAC should be used in place of digest to pre-
vent willful tampering when the complete message including the digest or MAC is exposed.
Digital signature combines public key encryption with digest to provide non-repudiation.
You can perform these cryptographic operations using the command line utility
crypttool. This allows experimentation with various combinations of services, algorithms
and providers without any programming. You can also examine the source code of crypttool
for sample code using the Java Security API.
Speed of cryptographic operations depends on the quality of implementation, algo-
rithm used and the keysize. For J2SE v1.4 bundled providers, we found 56-bit DES encryption
to be 2.5 times faster than 112-bit TripleDES encryption. For digital signature, we found RSA to
be approximately 1.5 times faster than DSA for both signature creation and verification.
Cryptography requires standards and protocols to be useful in real life. Most of the
applications require agreement about using cryptographic capabilities in a certain way. This is
achieved through standards and protocols.
Ch03.fm Page 82 Tuesday, August 5, 2003 2:39 PM
Further Reading
Most of the Java architecture for cryptography and API-related information presented in this
chapter can be found in J2SE v1.4 specification and reference guides. Refer to these guides
when in doubt. Authoritative documentation on Java classes and their methods can be found in
javadocs of J2SE SDK. The book Java Security by Scott Oaks includes comprehensive informa-
tion on security-related Java APIs and explains them with simple examples. This is a good book
to have if you are developing security software and need to use cryptographic APIs directly.
Look at the book Applied Cryptography by Bruce Schneier for very detailed, almost ency-
clopedic, information on cryptographic operations, algorithms, protocols, attack vulnerabilities,
performance and other related aspects such as patent and politico-legal issues. It’s a must have if
you plan to write your own provider and implement the cryptographic algorithms.
If you are interested in looking at working code as examples, dive into crypttool
source code. It is quite modular and you will have no difficulty identifying relevant portions.