CIPHER-BASED ENCRYPTION
Ahmad Tarmizi Bin Mohamad - SX110252CRS04Muhammad Nursalam Bin Mohd Amran - SX112040CRF04Zulhusni Zahini Bin Haron - SX112067CRF04
Internal
INTRODUCTION
Cipher−based encryption is part of the JCE, which contains an
engine (the cipher engine) that performs encryption as well as
several classes that support data encryption. All the classes in this
chapter are available only with the security provider that comes
with JCE.
Internal
MOST MODERN CIPHERS CAN BE CATEGORIZED IN SEVERAL WAYS
By whether they work on blocks of symbols usually of a fixed size (block
ciphers), or on a continuous stream of symbols (stream ciphers).
By whether the same key is used for both encryption and decryption
(symmetric key algorithms), or if a different key is used for each (asymmetric
key algorithms). If the algorithm is symmetric, the key must be known to the
recipient and sender and to no one else. If the algorithm is an asymmetric one,
the enciphering key is different from, but closely related to, the deciphering
key. If one key cannot be deduced from the other, the asymmetric key
algorithm has the public/private key property and one of the keys may be
made public without loss of confidentiality.Internal
THE CIPHER ENGINE
This engine is called the Cipher class (javax_crypto_chiper); it
provides an interface to encrypt and decrypt data either in arrays
within the program or as that data is read or written through Java's
stream interfaces:
Perform encryption and decryption of arbitrary data, using
(potentially) a wide array of encryption algorithms. Like all security
engines, the cipher engine implements named algorithms.
Internal
THE ALGORITHMS SPECIFIED BY THE SUNJCE SECURITY PROVIDER
Data Encryption Standard (DES)
DES is the Data Encryption Standard algorithm, a standard that has been
adopted by various organizations. There are known ways to attack this
encryption, though they require a lot of computing power to do so;
despite widespread predictions about the demise of DES, it continues to
be used in many applications and is generally considered secure
Internal
DESEDE
This is also known as triple−DES or multiple−DES. This algorithm
uses multiple DES keys to perform three rounds of DES encryption
or decryption; the added complexity greatly increases the amount
of time required to break the encryption.
Internal
PBEWITHMD5ANDDES
This is the password−based encryption defined in PKCS#5. This
algorithm entails using a password, a byte array known as salt, and
an iteration count along with an MD5 message digest to produce a
DES secret key; this key is then used to perform DES encryption or
decryption
Internal
BLOWFISH
This algorithm was designed by Bruce Schneier; it is an attractive
algorithm because it is not patented, and Mr. Schneier has placed
implementations of the algorithm in the public domain. It is best
used in applications where the key does not change often, though it
requires a lot of memory.
Internal
THE MODES SPECIFIED BY THE SUNJCE SECURITY
Electronic Cookbook mode (ECB)
This is the electronic cookbook mode. ECB is the simplest of all modes; it
takes a simple block of data (8 bytes in the SunJCE implementation, which
is standard) and encrypts the entire block at once. No attempt is made to
hide patterns in the data, and the blocks may be rearranged without
affecting decryption (though the resulting plaintext will be out of order).
Internal
CIPHER BLOCK CHAINING MODE (CBC)
This is the cipher block chaining mode. In this mode, input from one
block of data is used to modify the encryption of the next block of
data; this helps to hide patterns (although data that contains
identical initial text such as email messages will still show an initial
pattern).
Internal
CIPHER FEEDBACK MODE (CFB)
Cipher−feedback mode. This mode is very similar to CBC, but its internal implementation is slightly different. CBC requires a full block (8 bytes) of data to begin its encryption, while CFB can begin encryption with a smaller amount of data. So this mode is suitable for encrypting text, especially when that text may need to be processed a character at a time
Internal
OUTPUT FEEDBACK MODE (OFB)
The output−feedback mode. This mode is also suitable for text; it is
used most often when there is a possibility that bits of the
encrypted data may be altered in transit (e.g., over a noisy
modem). While a 1−bit error would cause an entire block of data to
be lost in the other modes, it only causes a loss of 1 bit in this
mode.
Internal
PROPAGATING CIPHER BLOCK CHAINING (PCBC)
This is the propagating cipher block chaining mode. It is popular in
a particular system known as Kerberos if you need to speak to a
Kerberos version 4 system, this is the mode to use.
Internal
THE PADDING SCHEMES
PKCS5Padding
This padding scheme ensures that the input data is padded to a multiple of 8
bytes.
No Padding
When this scheme is specified, no padding of input is done. In this case, the
number of input bytes presented to the encryption cipher must be a multiple of
the block size of the cipher; otherwise, when the cipher attempts to encrypt or
decrypt the data, it generates an error.Internal
USING THE CIPHER CLASS FOR ENCRYPTION/DECRYPTION
Cipher engine that can perform encryption and decryption by
implementing the named algorithm. The engine is provided by the
given security provider, or the list of installed security providers is
searched for an appropriate engine
Internal
One of these methods (for obtain instance obtain cipher class):
If an implementation of the given algorithm cannot be found, a NoSuchAlgorithmException is thrown. If the named provider cannot be found, a NoSuchProviderException is thrown.
public static Cipher getInstance(String algorithmName)public static Cipher getInstance(String algorithmName, String provider)
Internal
THE CIPHER: USING THE CIPHER CLASS FOR ENCRYPTION/DECRYPTION getInstance() method:
1) can be simple algorithm (e.g DES)
2) an algorithm name that specifies a mode and padding in this format (e.g DES/ECB/PKCS5Padding)
3) if not specified- default to an implementation-specific valuein the SunJCE security provider, the mode defaults to ECB (CBC if the algorithm is PBEWithMD5andDES) and padding defaults to PKCS5
If an implementation of the given algorithm cannot be found, a NoSuchAlgorithmException is thrown. If the named provider cannot be found, a NoSuchProviderException is thrown.
Internal
ONCE YOU'VE OBTAINED A CIPHER OBJECT, YOU MUST INITIALIZE IT. AN OBJECT=INITIALIZED FOR ENCRYPTION, DECRYPTION, OR FOR KEY WRAPPING, BUT YOU MUST PROVIDE A KEY.IF THE ALGORITHM IS A SYMMETRIC CIPHER, YOU SHOULD PROVIDE A SECRET KEY; OTHERWISE, YOU SHOULD PROVIDE A PUBLIC KEY TO ENCRYPT DATA AND A PRIVATE KEY TO DECRYPT DATA (IN FACT, THE KEY MUST MATCH THE ALGORITHM TYPE: A DES CIPHER MUST USE A DES KEY, AND SO ON). INITIALIZATION IS ACHIEVED WITH ONE OF THESE METHODS:
public final void init(int op, Key k) public final void init(int op, Certificate c)
public final void init(int op, Key k, AlgorithmParameterSpec aps)
public final void init(int op, Key k, AlgorithmParameterSpec aps, SecureRandom sr)
public final void init(int op, Key k, SecureRandom sr)
public final void init(int op, Certificate c, SecureRandom sr)
public final void init(int op, Key k, AlgorithmParameters ap)
public final void init(int op, Key k, AlgorithmParameters ap, SecureRandom sr)
Internal
INITIALIZED AN ENGINE BEFORE FED A DATA Initialize the cipher to encrypt or decrypt data
Cipher.ENCRYPT_MODE : the cipher is initialized to encrypt data.
Cipher.DECRYPT_MODE : the cipher is initialized to decrypt data.
The cipher is initialized with the given key or the public key contained within the given certificate.
These calls reset the engine to an initial state, discarding any previous data that may have been fed to the engine. Hence, a single cipher object can be used to encrypt data and then later to decrypt data.
Many algorithm modes we discussed earlier require an initialization vector to be specified when the cipher is initialized for decrypting. In these cases, the initialization vector must be passed to the init() method within the algorithm parameter specification or algorithm parameters; typically, the IvParameterSpec class is used to do this for DES encryption.
In the SunJCE security provider, specifying an initialization vector for a mode that does not support it will eventually lead to a NullPointerException Failure to specify an initialization vector for a mode that requires one will generate incorrect decrypted data.
Internal
THE FIRST SET CAN BE USED ANY NUMBER OF TIMES
First method:
public final byte[] update(byte[] input)
public final byte[] update(byte[] input, int offset, int length)
public final int update(byte[] input, int offset, int length, byte[] output)
public final int update(byte[] input, int offset, int length, byte[] output, int outOffset)
Second method
public final byte[] doFinal( )
public final int doFinal(byte[] output, int offset) public final byte[] doFinal(byte[] input)
public final byte[] doFinal(byte[] input, int offset, int length)
public final int doFinal(byte[] input, int offset, int length, byte[] output)
public final int doFinal(byte[] input, int offset, int length, byte[] output, int outOffset)
This second set of methods should only be called once:
After an engine has been initialized, it must be fed data. There are two sets of methods to accomplish this.
Internal
FIRST METHOD Encrypt or decrypt the data in the input array (starting at the given offset for the given length, if applicable). The resulting data is either placed in the given output array (in which case the size of the output data is returned) or returned in a new array. If the cipher has not been initialized, an IllegalStateException is thrown.
If the length of the data passed to this method is not an integral number of blocks, any extra data is buffered internally within the cipher engine; the next call to an update() or doFinal() method processes that buffered data as well as any new data that is just being provided.
If the given output buffer is too small to hold the data ShortBufferException is thrown. The required size of the output buffer can be obtained from the getOutputSize method. A ShortBufferException does not clear the state of the cipher: any buffered data is still held, and the call can be repeated (with a correctly sized buffer) with no ill effects.
Encrypt or decrypt the data in the input array as well as any data that has been previously buffered in the cipher engine. This method behaves exactly the same as the update() method, except that this method signals that all data has been fed to the engine. If the engine is performing padding, the padding scheme will be used to process the pad bytes (i.e., add padding bytes for encryption and remove padding bytes for decryption). If the cipher engine is not performing padding and the total of all processed data is not a multiple of the mode's block size, an IllegalBlockSizeException is thrown.
These methods throw an IllegalStateException or a ShortBufferException in the same circumstances as the update() methods.
SECOND METHOD
Internal
to send it to someone who will eventually need to decrypt the data:
1) public final byte[] getIV( )
Return the initialization vector that was used to initialize this cipher. If a system−provided initialization vector is used, that vector is not available until after the first call to an update() or doFinal() method.
2) public final int getOutputSize(int inputLength)
Return the output size for the next call to the update() or doFinal() methods, assuming that one of those methods is called with the specified amount of data. Note that the size returned from this call includes any possible padding that the doFinal() method might add. A call to the update() method may actually generate less data than this method would indicate because it will not create any padding.
two miscellaneous methods of this class:
3) public final Provider getProvider( )
Return the provider class that defined this engine.
4) public final int getBlockSize( )
Get the block size of the mode of the algorithm that this cipher implements.
Internal
SAMPLE CODING package javasec.samples.ch13;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class CipherTest {
public static void main(String args[]) {
try {
// First, we need a key; we'll just generate one
// though we could look one up in the keystore or
// obtain one via a key agreement algorithm.
KeyGenerator kg = KeyGenerator.getInstance("DES");
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
Key key = kg.generateKey( );
// Now we'll do the encryption. We'll also retrieve
// the initialization vector, which the engine will
// calculate for us.
c.init(Cipher.ENCRYPT_MODE, key);
byte input[] = "Stand and unfold yourself".getBytes( );
byte encrypted[] = c.doFinal(input);
byte iv[] = c.getIV( );
// Now we'll do the decryption. The initialization
// vector can be transmitted to the recipient with
// the ciphertext, but the key must be transmitted
// securely.
IvParameterSpec dps = new IvParameterSpec(iv);
c.init(Cipher.DECRYPT_MODE, key, dps);
byte output[] = c.doFinal(encrypted);
System.out.println("The string was ");
System.out.println(new String(output));
} catch (Exception e) {
e.printStackTrace( );
}
}
}
Internal
ENCYPT/DECRYPT EXPLAINATION IN SAMPLE CODING Within the try block, the second block of code performs the encryption:
1. We initialize the cipher engine for encrypting.
2. We pass the bytes we want to encrypt to the doFInal() method. Of course, we might have had any number of calls to the update() method preceding this call, with data in any arbitrary amounts. Since we've specified a padding scheme, we don't have to worry about the size of the data we pass to the doFinal() method.
3. Finally, we save the initialization vector the system provided to perform the encryption. Note that this step would not be needed for ECB mode, which doesn't require an initialization vector.
Performing the decryption is similar:
1. First, we initialize the cipher engine for decrypting. In this case, however, we must provide an initialization vector to initialize the engine in order to get the correct results (again, this would be unnecessary for ECB mode).
2. Next, we pass the encrypted data to the doFinal() method. Again, we might have had multiple calls to the update() method first.
Internal
THE CIPHER: PERFORMING YOUR OWN PADDING In this example, we used the PKCS5 padding scheme to provide the necessary padding. This is by far the simplest way. If you want to do your own padding -- if, for example, you're using a CFB32 mode for some reason -- you need to do something like this:
Cipher c = Cipher.getInstance("DES/CFB32/NoPadding");
c.init(Cipher.ENCRYPT_MODE, desKey);
int blockSize = c.getBlockSize( );
byte b[] = "This string has an odd length".getBytes( );
byte padded[] = new byte[b.length + blockSize -(b.length % blockSize)];
System.arraycopy(b, 0, padded, 0, b.length);
for (int i = 0; i < blockSize - (b.length % blockSize); i++)
padded[b.length + i] = 0;
byte output[] = c.doFinal(padded);
The problem with this code is that when the data is decrypted, there is no indication of how many bytes
should be discarded as padding. PKCS5 and other padding schemes solve this problem by encoding that
information into the padding itself.
Internal
THE CIPHER: INITIALIZATION OF A PBEWITHMD5ANDDES CIPHER
As we mentioned earlier, a password-based cipher cannot be initialized without special data that is passed via the algorithm specification. This data is known as the salt and iteration count. Hence, a password-based cipher is initialized as follows:
String password = "Come you spirits that tend on mortal thoughts";
byte[] salt = { (byte) 0xc9, (byte) 0x36, (byte) 0x78, (byte) 0x99,
(byte) 0x52, (byte) 0x3e, (byte) 0xea, (byte) 0xf2 };
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 20);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray( ));
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = kf.generateSecret(keySpec);
Cipher c = Cipher.getInstance("PBEWithMD5AndDES");
c.init(Cipher.ENCRYPT_MODE, key, paramSpec);
it allows the password to be shared verbally (or otherwise) between participants in the cipher, the user would presumably enter the password. Since these types of passwords are often easy to guess.
the iteration and salt provide a means to massage the password into something more secure. The salt itself should be random, and the higher the iteration count, the more expensive a brute-force attack against the key becomes (though it also takes longer to generate the key itself).
Of course, despite the presence of the salt and iteration, the password chosen in the method should not be easy to guess in the first place: it should contain special characters, not be known quotes from literature, and follow all the other usual rules that apply to selecting a password.
Internal
THE CIPHER: USING THE CIPHER CLASS FOR KEY WRAPPING
When you need to send a secret key between two parties, you may need to encrypt the key in order to protect it. This is often done because symmetric cipher algorithms are generally faster than asymmetric ones, but sharing public keys is generally much easier than sharing secret keys. So you can use an asymmetric cipher to encrypt a secret key that is sent between two parties; that way the only out-of-band data that must be transferred between the parties is a public key certificate, and you can still use a fast encryption algorithm.
Because this is a common operation, the Cipher class provides methods that wrap (encrypt) and unwrap (decrypt) keys. These operations could be accomplished by using the update( ) and/or doFinal( ) methods of the Cipher class directly, but the methods we're about to examine operate directly on Key objects rather than on arrays of bytes, so they are more convenient to use.
Process to wrap/unwrap keys
1) to wrap key, first use INIT() METHODS and SPECIFY Cipher.WRAP_mode
2) Unwrap key. Process is same but specify Cipher.UNWRAP_MODE then use update() and doFinal() methods.
public final byte[] wrap(Key key)
Wrap (encrypt) a key. The cipher must be initialized for WRAP_MODE.
public final Key unwrap(byte[] key, String algorithm, int type)
Unwrap a key. To unwrap the key, you must have the key data, and you must know the algorithm used to generate the key (e.g., DES or RSA) and the type of the key. The type must be either Cipher.SECRET_KEY, Cipher.PUBLIC_KEY, or Cipher.PRIVATE_KEY.
Internal
THE CIPHER: IMPLEMENTING THE CIPHER CLASS public abstract class CipherSpi
The SPI for the Cipher class. This class is responsible for performing the encryption or decryption according to its internal algorithm. Support for various modes or padding schemes must be handled by this class as well.
public abstract void engineSetMode(String s)
Set the mode of the cipher engine according to the specified string. If the given mode is not supported by this cipher, a NoSuchAlgorithmException should be thrown.
public abstract void engineSetPadding(String s)
Set the padding scheme of the cipher engine according to the specified string. If the given padding scheme is not supported by this cipher, a NoSuchPaddingException should be thrown.
protected abstract int engineGetBlockSize( )
Return the number of bytes that comprise a block for this engine. Unless the cipher is capable of performing padding, input data for this engine must total a multiple of this block size (though individual calls to the update( ) method do not necessarily have to provide data in block-sized chunks).
protected abstract byte[] engineGetIV( )
Return the initialization vector that was used to initialize the cipher. If the cipher was in a mode where no initialization vector was required, this method should return null.
protected abstract int engineGetOutputSize(int inputSize)
Return the number of bytes that the cipher will produce if the given amount of data is fed to the cipher. This method should take into account any data that is presently being buffered by the cipher as well as any padding that may need to be added if the cipher is performing padding.
Internal
CONTINUE THE CIPHER: IMPLEMENTING THE CIPHER CLASS protected void engineInit(int op, Key key, SecureRandom sr)
protected void engineInit(int op, Key key, AlgorithmParameterSpec aps, SecureRandom sr)
protected void engineInit(int op, Key key, AlgorithmParameters ap, SecureRandom sr)
This method should ensure that the key is of the correct type and throw an InvalidKeyException if it is not (or if it is otherwise invalid), and use the given random number generator (and algorithm parameters, if applicable) to initialize its internal state. If algorithm parameters are provided but not supported or are otherwise invalid, this method should throw an InvalidAlgorithmParameterException.
protected byte[] engineWrap(Key key)
Wrap the key, returning the encrypted bytes that can be used to reconstitute the key. Note that this method is not abstract (for backward-compatibility reasons). You must override it if you want to support key wrapping; otherwise it will throw an UnsupportedOperationException. If the key is not the correct format, you should throw an InvalidKeyException; if the cipher cannot handle the correct block size of the key then it should throw an IllegalBlockSizeException.
protected abstract byte[] engineUpdate(int input[], int offset, int len)
protected abstract int engineUpdate(int input[], int offset, int len, byte[] output, int outOff)
Encrypt or decrypt the input data. The data that is passed to these methods is not necessarily an integral number of blocks. It is the responsibility of these methods to process as much of the input data as possible and to buffer the remaining data internally. Upon the next call to an engineUpdate( ) or engineDoFinal( ) method, this buffered data must be processed first, followed by the input data of that method (and again leaving any leftover data in an internal buffer).
protected abstract byte[] engineDoFinal(int input[], int offset, int len)
protected abstract int engineDoFinal(int input[], int offset, int len, byte[] output, int outOff)
Encrypt or decrypt the input data. Like the update( ) method, this method must consume any buffered data before processing the input data.
Internal
CIPHER STREAM:THE CIPHEROUTPUTSTREAM CLASS public class CipherOutputStream extends FilterOutputStream
Provide a class that will encrypt data as it is written to the underlying stream.
public CipherOutputStream(OutputStream outputStream, Cipher cipher)
Create a cipher output stream, associating the given cipher object with the existing output stream. The given cipher must already have been initialized, or an IllegalStateException will be thrown.
Internal
THE CIPHERINPUTSTREAM CLASS public class CipherInputStream extends FilterInputStream
Create a filter stream capable of decrypting data as it is read from the underlying input stream.
public CipherInputStream(InputStream is, Cipher c)
Create a cipher input stream that associates the existing input stream with the given cipher. The cipher must previously have been initialized.
Internal
THE CIPHERINPUTSTREAM CLASS: CODING package javasec.samples.ch13;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class Receive {
public static void main(String args[]) {
try {
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("keyfile"));
DESKeySpec ks = new DESKeySpec((byte[]) ois.readObject( ));
CipherInputStream cis = new CipherInputStream(
new FileInputStream("ciphertext"), c);
BufferedReader br = new BufferedReader(
new InputStreamReader(cis));
System.out.println("Got message");
System.out.println(br.readLine( ));
} catch (Exception e) {
System.out.println(e);
}
}
}
Internal
SEALED OBJECTS
To seal object:
public SealedObject(Serializable obj, Cipher c)
Construct a sealed object. The sealed object serializes the given object to an embedded byte array,
effectively making a copy of the object. It then uses the given cipher to encrypt the embedded byte
array. If the object is unable to be serialized, an IOException is thrown; an error in encrypting the
byte array results in an IllegalBlockSizeException. If the cipher object has not been
initialized, an IllegalStateException is generated.
To retrieve:
public Object getObject(Cipher c)
Decrypt the embedded byte array and deserialize it, returning the reconstituted object. The cipher
must have been initialized with the same mode and key as the cipher that was passed to the
constructor when the object was first created, otherwise a BadPaddingMethodException or an
IllegalBlockSizeException is thrown. If the cipher was not initialized, an
IllegalStateException is generated; failure to find the serialized class results in a
ClassNotFoundException, and generic deserialization errors result in an IOException.
Internal
COMPARISON WITH PREVIOUS RELEASES Version 1.2 does not support key wrapping. The init( ) methods to the Cipher class that require a certificate are not available in 1.2.
Version 1.1 of JCE supports only the DES, DESede, and PBEWithMD5AndDES encryption algorithms.
Internal
SUMMARY
In this chapter, we explored cipher-based encryption engines. These engines perform encryption of arbitrary chunks or streams of data according to various algorithms. Cipher-based encryption engines separate the encryption of data from its transmission, so they are suitable for many purposes. They can be used to encrypt data to be sent over a socket, but they may also be used to encrypt data to be stored in a file, on a Java smart card, or even held in memory on an insecure machine.
Top Related