The SATSA-CRYPTO Optional Package allows applications to use cryptographic tools like message digests, digital signatures, and ciphers. These tools give applications the ability to support commerce, safeguard information, and protect privacy.
SATSA-CRYPTO is a subset of the J2SE platform Java Cryptography Extension (JCE). For more information on JCE, see http://java.sun.com/products/jce/.
This chapter describes how to use SATSA-CRYPTO from your own applications. It is not a comprehensive tutorial on cryptographic concepts, although it does include some introductory explanations. For a more detailed introduction to the concepts, read this article:
MIDP Application Security 1: Design Concerns and Cryptography
http://developers.sun.com/techtopics/mobility/midp/articles/security1/
For deeper coverage, try Bruce Schneier’s excellent Applied Cryptography (Wiley, 1995).
SATSA-CRYPTO provides an API for three cryptographic tools:
The classes and interfaces of SATSA-CRYPTO are located in the same packages as the J2SE platform JCE: java.security
, java.security.spec
, javax.crypto
, and javax.crypto.spec
.
A cipher is useful for keeping data secret. The cipher takes regular data, the plaintext, and converts it into an unreadable form, called ciphertext. This process is encryption. The cipher can also decrypt ciphertext to restore the original plaintext.
A cipher is valuable because you can encrypt sensitive information, like credit card numbers or your Aunt May’s medical records. As ciphertext, the data can be safely transmitted over an insecure medium like the Internet. At the receiving end, you can use the cipher to decrypt the ciphertext and retrieve the original information.
Cryptographic ciphers use keys to make unique ciphertext. Only the same key, or a matching key, will decrypt the ciphertext and restore the plaintext. A symmetric cipher uses the same key for encryption and decryption. An asymmetric cipher uses one key for encryption and a matched key for decryption. The matched set of keys is called a key pair. One key in a key pair, the public key, can be freely distributed and used by other parties for verification. The other part of the key pair, the private key, must be kept secret.
Ciphers use a mathematical algorithm to encrypt and decrypt data. Some of the widely known cipher algorithms are AES, DES, and RC4.
A stream cipher encrypts one byte at a time. A block cipher encrypts a block of bytes in a single operation.
Because plaintext is not always an even multiple of the block size, padding may be added to the plaintext. Padding is extra bytes of data that fill up the empty space in a block. Several standard padding schemes describe what values are placed in the padding bytes.
Block ciphers can operate in different modes. A mode describes exactly how plaintext is transformed into ciphertext and back again. In the simplest mode, each block of plaintext is encrypted to one block of ciphertext. More complicated modes use some combination of the current plaintext block and previous ciphertext as the input to the cipher. Some modes require an initialization vector, which is combined with the very first block of plaintext as input to the cipher.
Cryptography is based on mathematics. In essence, keys are numbers that are used in cryptographic algorithms. Different algorithms use different kinds of keys.
SATSA-CRYPTO provides three ways to think about a key:
java.security.Key
interface.java.security.spec.KeySpec
.Keys are typically stored in encoded form on some form of persistent storage. At runtime, the application loads a key as a byte array, then converts it into a key object that can be used with the rest of the API.
The way you convert byte stream representations of keys to key objects depends on the type of key.
For symmetric cipher keys, you can create an implementation of KeySpec
using a byte array. If the KeySpec
class implements the Key
interface, the object can be used directly as a key. For example, the SecretKeySpec
class can be used to create DES keys from byte arrays.
For asymmetric keys, a public key can be extracted by constructing a KeySpec
implementation from a byte array. Then you can use a KeyFactory
object to extract the public key from the KeySpec
. An example later in this chapter illustrates the technique.
SATSA-CRYPTO encapsulates cryptographic ciphers in the javax.crypto.Cipher
class. Cipher
is surprisingly easy to use:
Cipher
instance for the algorithm you want to use. You can also specify a mode and a padding scheme in this step.Cipher
with a key and a flag that indicates whether the Cipher
will be encrypting or decrypting.Cipher
using the update()
method.Cipher
with the doFinal()
method.
Rather than instantiate a Cipher
directly, use a factory method, getInstance()
, to request an instance that corresponds to a particular algorithm. Pass getInstance()
an algorithm name or a combination of an algorithm name, mode, and padding. If the underlying implementation has a matching implementation, a new Cipher
object is created and returned. Otherwise, a NoSuchAlgorithmException
is thrown.
The following example demonstrates several of these techniques.
It begins by constructing a DES secret key from a byte array using a SecretKeySpec
. It creates a Cipher
based on the DES algorithm in Electronic Code Book (ECB) mode, with no padding. The cipher is initialized for encryption using the secret key. Finally, this example encrypts a small plaintext message. For a complete example that uses SATSA-CRYPTO, see Appendix D.
byte[] keyBits = { (byte) 0x2b, (byte) 0x7e, (byte) 0x15, (byte) 0x16, (byte) 0x28, (byte) 0xae, (byte) 0xd2, (byte) 0xa6 }; byte[] plaintext = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef }; byte[] ciphertext = new byte[8]; SecretKeySpec key = new SecretKeySpec(keyBits, 0, keyBits.length, "DES"); Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key); int count = cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
The next example is slightly more complicated. It uses a DES cipher in Cipher Block Chaining (CBC) mode, in which the previous block of ciphertext is combined with the current block of plaintext before encryption. For the first block of plaintext, there is no previous ciphertext block, so an initialization vector is needed. This example demonstrates how to use IvParameterSpec
to pass an initialization vector to a Cipher
using one of its init()
methods.
byte[] keyBits = { (byte)0x2b, (byte)0x7e, (byte)0x15, (byte)0x16, (byte)0x28, (byte)0xae, (byte)0xd2, (byte)0xa6 }; byte[] ivBits = { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08 }; byte[] plaintext = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef }; byte[] ciphertext = new byte[8]; SecretKeySpec key = new SecretKeySpec(keyBits, 0, keyBits.length, "DES"); IvParameterSpec iv = new IvParameterSpec(ivBits, 0, ivBits.length); Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key, iv); int count = cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
The final example shows how to encrypt data using the public key of an RSA key pair. It demonstrates the use of the KeyFactory
class to extract a public key from an encoded representation.
byte[] publicKeyBits; // Assign publicKeyBits here. byte[] plaintext = "This is just an example".getBytes(); byte[] ciphertext = new byte[128]; X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBits); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int count = cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
A message digest is a short value that is constructed by processing an arbitrarily large set of data. It serves as a fingerprint of the data. If you change the data set, even a single bit, then compute the message digest value again, it will be different. Message digests are helpful for ensuring that data has not changed in transit from one place to another. You could use a message digest to check for communications errors, but if you’re worried about attackers, use a digital signature to verify data integrity.
Conceptually, a digital signature is a message digest value that has been encrypted using the private key of a key pair. Why is this useful?
Suppose you want to send this message to your friend:
Meet me at the theater at 10:30.
If you send this message via the Internet, your enemy could intercept the message in a dozen different ways and modify it at will, like this:
Meet me at the restaurant at 7:30.
You could try using a message digest to prevent this kind of tampering. Create a message digest value from the original message, then send the message and the digest value together. Unfortunately, your enemy is pretty sharp and could simply change the message, compute a new digest value from the new message, and send both to your friend. Your friend would see the message and its matching digest and assume the message is correct.
If you digitally sign the message, your enemy cannot change the message undetected. When you create a signature of the message, you encrypt the message digest value with your private key, which is a secret. When your friend receives the message, she uses your public key to decrypt the message, then compares the decrypted digest value with a freshly computed one. If they match, your friend is sure that the message is unaltered.
Your enemy could create a new message and compute a new digest value for it, but he cannot forge your signature because he does not know your private key.
Message digests are represented by the java.security.MessageDigest
class in SATSA-CRYPTO. The class is easy to use:
MessageDigest
object by passing an algorithm name to the getInstance()
factory method. (This follows the pattern for Cipher
and, as you’ll see later, Signature
.)update()
method.digest()
method.The following example shows how to calculate a SHA digest value. For a complete example, see Appendix D. Different digest algorithms generate differently sized digest values. The SHA algorithm produces a 20 byte value.
byte[] data = "abc".getBytes(); byte[] digest = new byte[20]; MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(data, 0, data.length); md.digest(digest, 0, 20);
Digital signatures are represented by the java.security.Signature
class. In SATSA-CRYPTO, Signature
can be used only to verify signatures, not to generate them. Signature
resembles MessageDigest
except that it is initialized like Cipher
. Follow these steps to use a Signature
:
Signature
object by passing an algorithm name to the getInstance()
factory method.Signature
by passing a public key to initVerify()
.update()
method.verify()
. The Signature
calculates its own signature value and compares it to the one you supplied. If the two signatures match, the method returns true
.The following example shows how to verify an RSA signature. For a complete example, see Appendix D.
byte[] publicKeyBits; // Assign publicKeyBits here. X509EncodedKeySpec pks = new X509EncodedKeySpec(publicKeyBits); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey publicKey = kf.generatePublic(pks); Signature signature = Signature.getInstance("SHA1withRSA"); signature.initVerify(publicKey); signature.update(kData, 0, kData.length); boolean pass = signature.verify(kSignature);
SATSA Developer's Guide SATSA Reference Implementation 1.0 |
Copyright © 2004 Sun Microsystems, Inc. All rights reserved.