The purpose of this document is to provide an introduction to the Java APIs that provide security and cryptographic services. These APIs are known as Java Cryptography Architecture (JCA) and Java Cryptography Extension (JCE).
Whilst reading this document it is suggested to have both the JCA and JCE API documentation at hand. The JCA documentation can be found in the Java2 release or online at http://java.sun.com/products/jdk/1.2/docs/api/index.html, and the JCE documentation is currently available at http://java.sun.com/security/JCE1.2/earlyaccess/apidoc/index.html. Finally, your vendor's JCE provider should include reference documentation detailing the specific algorithms they have implemented and any extensions to the API.
This document assumes the reader is familiar with the Java programming language.
The Java platform provides two APIs for dealing with security and cryptographic services. The first is known as the Java Cryptography Architecture (JCA) and provides a framework for basic security functions such as certificates, digital signatures and message digests. The JCA and a default provider (the Sun provider) are included with Java2. The Java Cryptography Extension (JCE) extends the JCA to provide encryption, key exchange, key generation and message authentication services. The JCE is released as a standard extension to the Java2 platform.
The JCA/JCE do not directly provide specific implementations of the various algorithms. Rather they are an interface between the application and a number of specific implementations of the algorithms. Generally a vendor will group the algorithms they have developed into a Provider
which may then be installed into the Java runtime environment. Once installed, an application may specifically request a particular provider's implementation or, if not the framework will choose an implementation from the highest priority Provider
that implements the requested algorithm.
This architecture is achieved by providing "factory classes" that are used to create object instances that implement a specific algorithm. Each of the factory classes has a private
constructor, instances can only be constructed by calling a public static
method which returns an instance of the desired type. When an algorithm is requested, the factory class will iterate through the installed providers and return the first implementation it finds (unless the application has requested a specific Provider
).
The java.security.Security
class is responsible for maintaining a list of available providers. When this class is initialised it will read the java.security
properties file (which is located at $JAVA_HOME/lib/security/java.security
). This properties file has a list of the installed providers, ordered by preference. For example the Sun and Acme providers could be listed as;
security.provider.1=sun.security.provider.Sun security.provider.2=org.acme.crypto.provider.Acme
A Provider
may also be installed dynamically by an application at runtime. This is achieved by using the Security.addProvider()
method, passing the Provider
instance of the vendor to be installed. For example:
Security.addProvider(new org.acme.crypto.provider.Acme());
The following packages are provided as part of the JCA:
java.security
public
constructors, rather they consist of factory methods which will create Provider
based implementations of the requested algorithms. Here you will find the KeyFactory
, KeyPairGenerator
, KeyStore
, MessageDigest
and Signature
classes.
java.security.cert
java.security.interfaces
java.security.spec
The following packages are provided as part of the JCE:
javax.crypto
Cipher
, KeyGenerator
and Mac
classes.
javax.crypto.interfaces
javax.crypto.spec
The JCE supports encryption and decryption using symmetric algorithms (such as DES and RC4) and asymmetric algorithms (such as RSA and ElGamal). The algorithms may be stream or block ciphers, with each algorithm supporting different modes, padding or even algorithm-specific parameters.
The basic interface used to encipher or decipher data is the javax.crypto.Cipher
class. The class provides the necessary mechanism for encrypting and decrypting data using arbitrary algorithms from any of the installed providers.
To create a Cipher
instance, use one of the Cipher.getInstance()
methods. This method will accept a transformation string and an optional provider name. The transformation string is used to specify the encryption algorithm as well as the cipher mode and padding. The transformation is specified in the form;
In the first instance, we are requesting the algorithm with its default mode and padding mechanism. The second instance fully qualifies all options. For a list of support algorithms consult the provider's documentation. Some common transformations are;
The following code will create a cipher for performing RC4 encryption or decryption, a cipher for doing RSA in ECB mode with PKCS#1 padding provided by the ABA provider and a cipher for performing DESede encryption/decryption in CBC mode with PKCS#5 padding:
Cipher rc4Cipher = Cipher.getInstance("RC4"); Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "ABA"); Cipher desEdeCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
Once we have a Cipher
instance, we will need to initialise the Cipher
for encryption or decryption. We will also need to provide a Key
, see section 8.0 for a discussion of key management.
Key desKey, rsaKey; desCipher.init(Cipher.ENCRYPT_MODE, desKey); rsaCipher.init(Cipher.DECRYPT_MODE, rsaKey);
As you can see, the first value passed to the Cipher.init()
method indicates whether we are initialising for encryption or decryption. The second argument provides the key to use during encryption or decryption.
There are a number of other initialisation methods for providing algorithm specific parameters (such as Initialisation Vectors, the number of rounds to use etc.). See section 4.4 for a discussion on algorithm parameters.
Now that our Cipher
is initialised, we can start processing data. To do so we use the Cipher.update()
and Cipher.doFinal()
methods. The Cipher.update()
methods may be used to incrementally process data. Once all the data is processed, one of the Cipher.doFinal()
methods must be called.
In the simplest usage, a single Cipher.doFinal()
call may be passed all the data:
byte[] plainText = "hello world".getBytes(); byte[] cipherText = desCipher.doFinal(plainText);
Once the Cipher.doFinal()
method has been called, the Cipher
instance will be reset to the state it was in after the last call to the Cipher.init()
method. That means the Cipher
may be reused to encipher or decipher more data using the same Key
and parameters that were specified in the initialisation.
Rather than deal with the complications of buffering enciphered or deciphered data produced by the Cipher.update()
methods, it may be desirable to use a Java Input/Output Stream type interface. Fortunately, the JCE provides us with such a mechanism.
The javax.crypto.CipherInputStream
and javax.crypto.CipherOutputStream
are based on the Java IO filter streams. This allows them to process data and pass on that data to an underlying stream.
To create a cipher stream, firstly create and initialise a javax.crypto.Cipher
instance and the underlying stream and then instantiate the required stream type with these two arguments.
For example, the following code fragment will create a CipherOutputStream
that will encipher its data (using DES) and pass the result to a ByteArrayOutputStream
. We can access the ciphertext by calling ByteArrayOutputStream.toByteArray()
.
Key desKey; Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, desKey); ByteArrayOutputStream bout = new ByteArrayOutputStream(); CipherOutputStream cout = new CipherOutputStream(bout, cipher); cout.write("hello world".getBytes()); cout.close(); byte[] cipherText = bout.toByteArray();
Once we can encipher and decipher data using a simple stream, interface, we can create much more complicated scenarios. For example the OutputStream
could just as easily be a SocketOutputStream
or we could construct an ObjectOutputStream
on top of our cipher stream and encipher Java objects directly.
The javax.crypto.SealedObject
class provides the mechanism to encipher a Serializable
object. This class allows the application to encipher a Java object and then recover the object all through a simple interface. The SealedObject
is also Serializable
to simplify the transport and storage of the enciphered objects.
A SealedObject
can be constructed through either serialisation or by its constructor. The constructor is used to create a new enciphered object. The constructor's arguments are the object to encipher and the Cipher
to use. The provided Cipher
instance must be initialised for encryption before the SealedObject
is created. This means calling a Cipher.init()
method with Cipher.ENCRYPT_MODE
as the mode, the required encryption Key
and any algorithm parameters.
The following fragment will create a new SealedObject
containing the enciphered String
"hello world":
Key desKey = ... Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, deskey); SealedObject so = new SealedObject("hello world", cipher);
To recover the original object, the SealedObject.getObject()
methods may be used. These methods take either a Cipher
or Key
object. When providing the Cipher
parameter, the instance must be initialised in the Cipher.DECRYPT_MODE
mode, with the appropriate decryption key and the same algorithm parameters as the original Cipher
. When providing a Key
parameter, the encryption algorithm and algorithm parameters are extracted from the SealedObject
.
The following fragment will extract a SealedObject
object from an ObjectInputStream
and then recover the protected object:
ObjectInputStream oin ... Key desKey = ... SealedObject so = (SealedObject)oin.readObject(); String plainText = (String)so.getObject(deskey);
One important security aspect to note with this class is that it does not use a digital signature to ensure the object is not tampered with in its serialised form. It is therefore possible that the object could be altered in storage or transport without detection. Fortunately, the JCA provides the java.security.SignedObject
mechanism which can be used in conjunction with the SealedObject
class to avoid this problem. (See section 7.2 for a discussion on the SignedObject
class).
Some cipher algorithms support parameterisation, for example the DES cipher in CBC mode can have an initialisation vector as an algorithm parameter and other ciphers may have a selectable block size or round count. The JCE provides support for algorithm-independent initialisation via the java.security.spec.AlgorithmParameterSpec
and java.security.AlgorithmParameters
classes.
The java.security.spec.AlgorithmParameterSpec
derived classes can be constructed programatically by an application. The following classes are provided by the JCA/JCE:
java.security.spec
| |
DSAParameterSpec
| Used to specify the parameters used with the DSA algorithm. The parameters consist of the base g, prime p and sub-prime q. |
javax.crypto.spec
| |
DHGenParameterSpec
| The set of parameters used for generating Diffie-Hellman parameters for use in Diffie-Hellman key agreement. |
DHParameterSpec
| The set of parameters used with Diffie-Hellman as specified in PKCS#3. |
IvParameterSpec
| An initialisation vector for use with a feedback cipher. That is an array of bytes of length equal to the block size of the cipher. |
RC2ParameterSpec
| Parameters for the RC2 algorithm. The parameters are the effective key size and an optional 8-byte initialisation vector (only in feedback mode). |
RC5ParameterSpec
| Parameters for the RC5 algorithm. The parameters are a version number, number of rounds, a word size and an optional initialisation vector (only in feedback mode). |
Your provider may also include further classes for passing parameters to the algorithms it implements.
The JCA also has mechanisms for dealing with the provider-dependent AlgorithmParameters
. This class is used as an opaque representation of the parameters for a given algorithm and allows an application to store persistently the parameters used by a Cipher
.
There are three situations where an application may encounter an AlgorithmParameters
instance:
Cipher.getParameters()
Cipher
has been initialised, it may have generated a set of parameters (based on supplied and/or default values). The value returned by the getParameters()
method allows the Cipher
to be re-initialised to exactly the same state.
AlgorithmParameters.getInstance()
Cipher
class, it is possible to generate them either based on an encoded format or an AlgorithmParameterSpec
instance. To do so create an uninitialised instance using the getInstance
method and then initialise it by calling the appropriate init()
method.
AlgorithmParameterGenerator.getParameters()
AlgorithmParameterGenerator
. Firstly, a generator is created for the required algorithm using the getInstance()
method. Then the generator is initialised by calling one of the init()
methods, finally to create the instance use the getParameters
method.
This class provides the concept of algorithm-independent parameter generation, in that the initialisation can be based on a "size" and a source of randomness. In this case the "size" value is interpreted differently for each algorithm.
The JCA provides support for the generation of message digests via the java.security.MessageDigest
class. This class uses the standard factory class design, so to create a MessageDigest
instance use the getInstance()
method with the desired algorithm name and optional provider as parameters.
Once created use the various update()
methods to process the message data and then finally call the digest()
method to calculate the final digest. At this point the instance may be re-used to calculate a digest for a new message.
MessageDigest digest = MessageDigest.getInstance("SHA"); byte[] msg = "The message".getBytes(); digest.update(msg); byte[] result = digest.digest();
The javax.crypto.Mac
API is used to access a "Message Authentication Code" (MAC) algorithm. These algorithms are used to check the integrity of messages upon receipt. There are two classes of MAC algorithms in general, those that are based on message digests (known as HMAC algorithms) and those on encryption algorithms. In both cases a shared secret is required.
A Mac
is used in the same fashion as a Cipher
. First, use the factory method Mac.getInstance()
to get the provider implementation of the required algorithm, then initialise the algorithm with the appropriate key via the Mac.init()
method. Then, use the Mac.update()
method to process the message and finally use the Mac.doFinal()
method to calculate the MAC for the message.
To verify the message, follow the same procedure and compare the supplied MAC with the calculated MAC.
Note that it is not necessary to use the Mac.init()
method to check multiple messages if the shared secret has not changed. The Mac
will be reset after the call to Mac.doFinal()
(or a call to Mac.reset()
).
/* * on the sender */ Mac senderMac = Mac.getInstance("HMAC-SHA1"); senderMac.init(shaMacKey); byte[] mac = senderMac.doFinal(data); /* * now transmit message and mac to receiver */ Mac recMac = Mac.getInstance("HMAC-SHA1"); recMac.init(shaMacKey); byte[] calcMac = recMac.doFinal(data); for (int i = 0; i < calcMac.length; i ++) { if (calcMac[i] != mac[i]) { /* bogus MAC! */ return false; } } /* all okay */ return true;
The java.security.Signature
class provides the functionality of a digital signature algorithm. Digital signatures are the digital equivalent of the traditional pen and paper signature. They can be used to authenticate the originator of a document, as well as to prove that a person signed the document. Generally, digital signatures are based on public-key encryption which means that, unlike a MAC, anyone that has access to the public key (and the document) can check the validity of the document.
The Signature
interface supports generation and verification of signatures. Once a signature instance has been created using the Signature.getInstance()
method, it needs to be initialised with the Signature.initSign()
method for creation of a signature, or Signature.initVerify()
method for verification of a signature.
Once initialised, the document to be processed should be passed to
the signature via the Signature.update()
methods. Once the
entire document has been processed, the Signature.sign()
method may be called to generate the signature, or the Signature.verify()
method to verify a supplied signature against a previously generated signature.
After a signature has been generated or verified, the Signature
instance is reset to the state it was in after it was last initialised, allowing another signature to be generated or verified using the same key.
One such signature algorithm is "MD5 with RSA" and is defined in PKCS#1. This algorithm specifies that the document to be signed is passed through the MD5 digest algorithm and then an ASN.1 block containing the digest, along with a digest algorithm identifier, is enciphered using RSA.
To create such a signature;
/* * Assume this private key is initialised */ PrivateKey rsaPrivKey; /* * Create the Signature instance and initialise * it for signing with our private key */ Signature rsaSig = Signature.getInstance("MD5withRSA"); rsaSig.initSign(rsaPrivKey); /* * Pass in the document data via the update() methods */ byte[] document = "The document".getBytes(); rsaSig.update(document); /* * Generate the signature */ byte[] signature = rsaSig.sign();
To verify the generated signature:
/* * Assume this public key is initialised */ PublicKey rsaPubKey; /* * Create the Signature instance and initialise * it for signature verification with the public key */ Signature rsaSig = Signature.getInstance("MD5withRSA"); rsaSig.initVerify(rsaPubKey); /* * Pass in the document data via the update() methods */ byte[] document = "The document".getBytes(); rsaSig.update(document); /* * Check the generated signature against the supplied * signature */ if (rsaSig.verify(signature)) { // signature okay } else { // signature fails }
The java.security.SignedObject
provides a mechanism for ensuring that a Java object can be authenticated and cannot be tampered with without detection. The mechanism used is similar to the SealedObject
in that the object to be protected is serialised and then a signature is attached. The SealedObject
is Serializable
so it may be stored or transmitted via the object streams.
To create a SignedObject
, firstly create an instance of the signature algorithm to use via the Signature.getInstance()
method, then create the new SignedObject
instance by providing the object to be signed, the signing key and the Signature
instance. Note that there is no need to initialise the Signature
instance; the SignedObject
constructor will perform that function.
Signature signingEngine = Signature.getInstance( "MD5withRSA"); SignedObject so = new SignedObject("hello world", privateKey, signingEngine);
To verify a SignedObject
, simply create the Signature
instance for the required algorithm and then use the SignedObject.verify()
method with the appropriate PublicKey
. Again, there is no need to initialise the Signature
instance.
Signature verifyEngine = Signature.getInstance( "MD5withRSA"); if (so.verify(publicKey, verifyEngine)) { // object okay, extract it Object obj = so.getObject(); } else { // object not authenticated }
Note that this class only provides a mechanism for authentication and verification, it does not provide confidentiality (i.e. encryption). The SealedObject
may be used for this purpose (see section 4.3). The following example combines these two classes to provide a confidential, authenticated, tamper-proof object:
/* * sealedObj will contain the signed, enciphered data */ SignedObject signedObj = new SignedObject( "hello world", privateKey, signingEngine); SealedObject sealedObj = new SealedObject( signedObj, cipher); /* * to verify and recover the original object */ SignedObject newObj = sealedObject.getObject(cipher); if (newObj.verify(publicKey, verificationEngine)) { // object verified tampered String str = (String)newObj.getObject(); } else { // object tampered with! }
The JCA/JCE framework manages keys in two forms, a provider-dependent format and a provider-independent format.
The provider-dependent keys will implement either the java.security.Key
interface (or one of its sub classes) for public-key algorithms or the javax.crypto.SecretKey
interface for secret-key algorithms. Provider keys can be generated randomly, via a key agreement algorithm or from their associated provider-independent format.
The provider-independent formats will implement the java.security.spec.KeySpec
interface. Subclasses of this type exist for both specific key types and for different encoding types. For example, the java.security.spec.RSAPublicKeySpec
can be used to construct an RSA public key from its modulus and exponent and a java.security.spec.PKCS8EncodedKeySpec
can be used to construct a private key encoded using PKCS#8.
Each Provider
will supply a number of mechanisms that will create the provider-dependent keys or convert the provider-independent keys into provider based keys.
The simplest mechanism to create keys for a given provider is to use their random key generators. Random keys are most often generated for use as "session-keys" which will be used for a given dialogue or session and are then no longer required. In the case of public-key algorithms, however, they may be generated once and then stored for later use. The JCE framework provides key generation via the following classes:
javax.crypto.KeyGenerator
java.security.KeyPairGenerator
For instance, to create a random 128-bit key for RC4 and initialise a Cipher
for encryption with this key;
/* * Create the key generator for the desired algorithm, * and then initialise it for the required key size. */ KeyGenerator rc4KeyGen = KeyGenerator.getInstance("RC4"); rc4KenGen.init(128); /* * Generate the key and then initialise the Cipher */ SecretKey rc4Key = rc4KeyGen.generateKey(); Cipher rc4Cipher = Cipher.getInstance("RC4"); rc4Cipher.init(Cipher.ENCRYPT_MODE, rc4Key);
Here, the SecretKey
returned by the KeyGenerator.generateKey()
method is a provider-dependent key. The returned key can then be used with that provider's algorithms.
Some algorithms have keys that are considered weak, for example with a weak DES key the ciphertext may be the same as the plaintext! Generally the KeyGenerator
will not generate those keys, however it is best to check the provider documentation for details on the specific algorithm.
The code to generate a public/private key pair is quite similar;
KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA"); rsaKeyGen.initialize(1024); KeyPair rsaKeyPair = rsaKeyGen.generateKeyPair(); Cipher rsaCipher = Cipher.getInstance("RSA"); rsaCipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPrivate());
Two interfaces are provided to convert between a provider-dependent Key
and the provider-independent KeySpec
; java.security.KeyFactory
and javax.crypto.SecretKeyFactory
. The KeyFactory
class is used for public-key algorithms and the SecretKeyFactory
class for secret-key algorithms.
An application may choose to store its keys in some way and then re-create the key using a KeySpec
. For example, the application may contain an embedded RSA public key as two integers; the RSAPublicKeySpec
along with a KeyFactory
that can process RSAPublicKeySpec
instances could then be used to create the provider-dependent key.
Each provider will generally supply a number of KeyFactory
/SecretKeyFactory
classes that will accept the various KeySpec
classes and produce Key
instances that may be used with the provider algorithms. These factories are not likely to support all KeySpec
types, so the provider documentation should provide the details as to what conversions will be accepted.
There are a number of KeySpec
classes provided by the JCA/JCE;
java.security.spec
| |
PKCS8EncodedKeySpec
| A DER encoding of a private key according to the format specified in the PKCS#8 standard. |
X509EncodedKeySpec
| A DER encoding of a public or private key, according to the format specified in the X.509 standard. |
RSAPublicKeySpec
| An RSA public key |
RSAPrivateKeySpec
| An RSA private key |
RSAPrivateCrtKeySpec
| An RSA private key, with the Chinese Remainder Theorem (CRT) values |
DSAPublicKeySpec
| A DSA public key |
DSAPrivateKeySpec
| A DSA private key |
javax.crypto.spec
| |
DESKeySpec
| A DES secret key |
DESedeKeySpec
| A DESede secret key |
PBEKeySpec
| A user-chosen password that can be used with password base encryption (PBE) |
SecretKeySpec
| A key that can be represented as a byte array and have no associated parameters. The encoding type is know as RAW. |
To convert a KeySpec
instance into a provider based Key
, firstly create a KeyFactory
or SecretKeyFactory
of the appropriate type using the getInstance()
method. Once the instance has been created, use the KeyFactory.generatePrivate()
, KeyFactory.generatePublic()
or SecretKeyFactory.generateSecret()
method.
In the following example we will create a Key
from a KeySpec
and then recover the KeySpec
from the Key
.
/* * This is the raw key */ byte[] keyBytes = { (byte)0x1, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08 }; /* * Create the provider independent KeySpec */ DESKeySpec desKeySpec = new DESKeySpec(keyBytes); /* * Create the KeyFactory to do the Key<->KeySpec translation */ SecretKeyFactory keyFact = KeyFactory.getInstance("DES"); /* * Create the provider based SecretKey */ SecretKey desKey = keyFact.generateSecret(desKeySpec); /* * Convert the provider Key into a generic KeySpec */ DESKeySpec desKeySpec2 = keyFact.getKeySpec(desKey, DESKeySpec.class);
Keys may also be generated using the javax.crypto.KeyAgreement
API. This interface provides the functionality of a key agreement (or key exchange) protocol. For example, a Diffie-Hellman KeyAgreement
instance would allow two or more parties to generate a shared Diffie-Hellman Key.
To generate the key, it is necessary to call KeyAgreement.doPhase()
for each party in the exchange with a Key
object that represents the current state of the key agreement. The last call to KeyAgreement.doPhase()
should have the lastPhase
set to true
.
Once all the key agreement phases have been processed, the shared SecretKey
may be generated by calling the KeyAgreement.generateSecret()
method.
The KeyAgreement
API does not define how each of the parties communicates the necessary information for each exchange in the protocol. The required information is passed to the KeyAgreement.doPhase()
method as a Key
. This Key
will generally be generated using either a KeyGenerator
or a KeyFactory
. The provider documentation will detail the specific steps required for a given protocol.
/* * Create the KeyAgreement instance for the required * protocol and initialise it with our key. In the * case of Diffie-Hellman this would be our private * key. */ KeyAgreement keyAg = KeyAgreement.getInstance("DH"); keyAg.init(ourKey); /* * Exchange information as per the key exchange * protocol. For DH we would exchange public keys. * Note since there is only two parties in this * case the return value is not relevant. */ keyAg.doPhase(remotePubKey, true); /* * Create the shared secret-key */ SecretKey key = keyAg.generateSecret("DES");
Once a Key
has been generated you may wish to store it for future use. Generally, you'll be saving public/private keys so that you can reuse them at a later date in a key exchange.
The java.security.KeyStore
API provides one mechanism for management of a number of keys and certificates. There are two types of entries in a KeyStore
; Key
entries and Certificate
entries. Key entries are sensitive information whereas certificates are not.
As Key
entries are sensitive, they are therefore are protected by the KeyStore
. The API allows for a password, or pass phrase, to be attached to each key entry. What the actual implementation does with the password is not defined, although it may be used to encipher the entry. A key entry may either be a SecretKey
, or a PrivateKey
. In the case of a PrivateKey
, the entry is saved along with a Certificate
chain which is the chain of trust. The chain of trust starts with the Certificate
containing the corresponding PublicKey
and ends with a self-signed certificate.
A certificate entry represents a "trusted certificate entry", that is a Certificate
whose identity we trust. This type of entry can be used to authenticate other parties.
To create a KeyStore
instance, use the KeyStore.getInstance()
method. This will return an empty key store which may then be populated by calling the KeyStore.load()
method. This method accepts an InputStream
instance and an optional password. Each individual KeyStore
will treat these parameters differently, so check the provider documentation for details.
The Sun provider supplies a KeyStore
known as "JKS". This KeyStore
is used by the keytool
and jarsigner
applications.
/* * Create an instance of the Java Key Store (defined by Sun) */ KeyStore keyStore = KeyStore.getInstance("JKS");
To add a new entry into the KeyStore
, use either setCertificateEntry()
or one of the setKeyEntry()
methods. This will add the new entry with the associated alias.
char[] myPass; SecretKey secretKey; /* * Store a SecretKey in the KeyStore, with "mypass" * as the password. */ keyStore.setKeyEntry("mysecretkey", secretKey, myPass, null); /* * assume that privateKey contains my PrivateKey * and myCert contains a Certificate with the * corresponding PublicKey */ PrivateKey privateKey; Certificate myCert; keyStore.setKeyEntry("myprivatekey", privateKey, myPass, myCert);
To extract an entry, use the getKey()
method to extract a Key
or getCertificate()
for a Certificate
.
/* * recover the SecretKey */ SecretKey key = (SecretKey)keyStore.getKey("mysecretkey", myPass); /* * recover the PrivateKey */ PrivateKey privKey = (PrivateKey)keyStore.getKey("myprivatekey", myPass); /* * recover the Certificate (containing the PublicKey) corresponding * to our PrivateKey */ Certificate cert = keyStore.getCertificate("myprivatekey");
If the KeyStore
supports persistence via the store()
and load()
methods, the provider documentation will explain what types of Key
types may be stored.
The JCA framework provides support for generic certificates, as well as X.509v3 certificates. Certificates may be stored using the KeyStore
API, or they may be generated from their encoded format (either the PEM or PKCS#7 encoding).
To create a java.security.cert.Certificate
instance from its encoded format, firstly create a java.security.cert.CertificateFactory
instance of the required type (eg X.509). Then use the generateCertificate()
or generateCertificates()
methods to convert your InputStream
into Certificate
instances.
CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate( inputStream);
Two useful methods of the Certificate
class are getPublicKey()
and verify()
. The first of these allows access to the PublicKey
of the certificate's owner and the second allows an application to verify that the certificate was signed using the private key that corresponds to the provided public key.
The java.security.cert.X509Certificate
class, which extends the Certificate
class, provides methods to access the other attributes of a X.509 certificate such as the Issuer's distinguished name or its validity period.
The keytool
application provided with JDK1.2 can be used to generate certificates and store them in a KeyStore
. Check the JDK documentation for information on how to use this application.
The JCA/JCE framework includes a number of specialised exception classes:
java.security
| |
DigestException
| Thrown if an error occurs during the final computation of the digest. Generally this indicates that the output buffer is of insufficient size. |
InvalidAlgorithmParameterException
| Thrown by classes that use AlgorithmParameters or AlgorithmParameterSpec instances where the supplied instance is not compatible with the algorithm or the supplied parameter was null and the algorithm requires a non-null parameter.
|
InvalidKeyException
| Thrown by the various classes that use Key objects, such as Signature , Mac and Cipher when the provided Key is not compatible with the given instance.
|
InvalidParameterException
| Only used in the deprecated interfaces in the Signature class and the deprecated class Signer .
|
KeyStoreException
| Thrown by the KeyStore class when the object has not been initialised properly.
|
NoSuchAlgorithmException
| Thrown by the getInstance() methods when the requested algorithm is not available.
|
NoSuchProviderException
| Thrown by the getInstance() methods when the requested provider is not available.
|
SignatureException
| Thrown by the Signature class during signature generation or validation if the object has not been initialised correctly or an error occurs in the underlying ciphers.
|
javax.crypto
| |
BadPaddingException
| Thrown by the Cipher class (or classes which use a Cipher class to process data) if this cipher is in decryption mode, (un)padding has been requested, and the deciphered data is not bounded by the appropriate padding bytes.
|
IllegalBlockSizeException
| Thrown by the Cipher class (or classes which use a Cipher class to process data) if this cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size
|
NoSuchPaddingException
| Thrown by the Cipher class by the getInstance() method when a transformation is requested that contains a padding scheme that is not available.
|
ShortBufferException
| Thrown by the Cipher class when an output buffer is supplied that is too small to hold the result.
|