Using Cryptography in Java Applications
This post describes how to use the Java Cryptography Architecture (JCA) that allows you to use cryptographic services in your applications.
Java Cryptography Architecture Services
The JCA provides a number of cryptographic services, like message digests and signatures.
These services are accessible through service specific APIs, like MessageDigest
and Signature
.
Cryptographic services abstract different algorithms. For digests, for instance, you could use MD5 or SHA1. You specify the algorithm as a parameter to the getInstance()
method of the cryptographic service class:
MessageDigest digest = MessageDigest.getInstance("MD5");
You find the value of the parameter for your algorithm in the JCA Standard Algorithm Name Documentation.
Some algorithms have parameters. For instance, an algorithm to generate a private/public key pair will take the key size as a parameter. You specify the parameter(s) using the initialize()
method:
KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA"); generator.initialize(1024);
If you don’t call the initialize()
method, some default value will be used, which may or may not be what you want.
Unfortunately, the API for initialization is not 100% consistent across services.
For instance, the Cipher
class uses init()
with an argument indicating encryption or decryption, while the Signature
class uses initSign()
for signing and initVerify()
for verification.
Java Cryptography Architecture Providers
The JCA keeps your code independent from a particular cryptographic algorithm’s implementation through the provider system.
Providers are ranked according to a preference order, which is configurable (see below). The best preference is 1, the next best is 2, etc. The preference order allows the JCA to select the best available provider that implements a given algorithm.
Alternatively, you can specify a specific provider in the second argument to getInstance()
:
Signature signature = Signature.getInstance("SHA1withDSA", "SUN");
The JRE comes with a bunch of providers from Oracle by default. However, due to historical export restrictions, these are not the most secure implementations. To get access to better algorithms and larger key sizes, install the Java Cryptography Extension Unlimited Strength Jurisdiction Policy Files.
Update: Note that the above statement is true for the Oracle JRE. OpenJDK doesn’t have the same limitation.
Make Your Use of Cryptography Configurable
You should always make sure that the cryptographic services that your application uses are configurable.
If you do that, you can change the cryptographic algorithm and/or implementation without issuing a patch.
This is particularly valuable when a new attack on an (implementation of an) algorithm becomes available.
The JCA makes it easy to configure the use of cryptography.
The getInstance()
method accepts both the name of the algorithm and the name of the provider implementing that algorithm. You should read both and any values for the algorithm’s parameters from some sort of configuration file.
Also make sure you keep your code DRY and instantiate cryptographic services in a single place.
Check that the requested algorithm and/or provider are actually available.
The getInstance()
method throws NoSuchAlgorithmException
when a given algorithm or provider is not available, so you should catch that. The safest option then is to fail and have someone make sure the system is configured properly. If you continue despite a configuration error, you may end up with a system that is less secure than required.
Note that Oracle recommends not specifying the provider. The reasons they provide is that not all providers may be available on all platforms, and that specifying a provider may mean that you miss out on optimizations.
You should weigh those disadvantages against the risk of being vulnerable.
Deploying specific providers with known characteristics with your application may neutralize the disadvantages that Oracle mentions.
Adding Cryptographic Service Providers
The provider system is extensible, so you can add providers.
For example, you could use the open source Bouncy Castle or the commercial RSA BSAFE providers.
In order to add a provider, you must make sure that its jar is available to the application. You can put it on the classpath for this purpose.
Alternatively, you can make it an installed extension by placing it in the $JAVA_HOME/lib/ext
directory, where $JAVA_HOME
is the location of your JDK/JRE distribution.
The major difference between the two approaches is that installed extensions are granted all permissions by default whereas code on the classpath is not. This is significant when (part of) your code runs in a sandbox.
Some services, like Cipher
, require the provider jar to be signed.
The next step is to register the provider with the JCA provider system. The simplest way is to use Security.addProvider()
:
Security.addProvider(new BouncyCastleProvider());
You can also set the provider’s preference order by using the Security.insertProviderAt()
method:
Security.insertProviderAt (new JsafeJCE(), 1);
One downside of this approach is that it couples your code to the provider, since you have to import the provider class. This may not be an important issue in an modular system like OSGi.
Another thing to look out for is that code requires SecurityPermission
to add a provider programmatically.
The provider can also be configured as part of your environment via static registration by adding an entry to the java.security
properties file (found in $JAVA_HOME/jre/lib/security/java.security
):
security.provider.1=com.rsa.jsafe.provider.JsafeJCE security.provider.2=sun.security.provider.Sun
The property names in this file start with security.provider.
and end with the provider’s preference. The property value is the fully qualified name of the class implementing Provider
.
Implementing Your Own Cryptographic Service Provider
Don’t do it. You will get it wrong and be vulnerable to attacks.
Using Cryptographic Service Providers
The documentation for the provider should tell you what provider name to use as the second argument to getInstance()
. For instance, Bouncy Castle uses BC
, while RSA BSAFE uses JsafeJCE
.
Most providers have custom APIs as well as JCA conformant APIs. Do not use the custom APIs, since that will make it impossible to configure the algorithms and providers used.
Not All Algorithms and Implementations Are Created Equal
It’s important to note that different algorithms and implementations have different characteristics and that those may make them more or less suitable for your situation.
For instance, some organizations will only allow algorithms and implementations that are FIPS 140-2 certified or are on the list of NSA Suite B cryptographic algorithms.
Always make sure you understand your customer’s cryptographic needs and requirements.
Using JCA in an OSGi environment
The getInstance()
method is a factory method that uses the Service Provider Interface (SPI). That is problematic in an OSGi world, since OSGi violates the SPI framework’s assumption that there is a single classpath.
Another potential issue is that JCA requires some jars to be signed. If those jars are not valid OSGi bundles, you can’t run them through bnd to make them so, since that would make the signature invalid.
Fortunately, you can kill both birds with one stone. Put your provider jars on the classpath of your main program, that is the program that starts the OSGi framework.
Then export the provider package from the OSGi system bundle using the org.osgi.framework.system.packages.extra
system property. This will make the system bundle export that package.
Now you can simply use Import-Package
on the provider package in your bundles.
There are other options for resolving these problems if you can’t use the above solution.
Reference: Using Cryptography in Java Applications from our JCG partner Remon Sinnema at the Secure Software Development blog.