Saturday, 19 September 2009 13:35

The J2ME Crypto Recipes

20.1 Overview of Recipes

The sample applications in this chapter provide ready-to-use code snippets for symmetric key encryption, password-based encryption, public key encryption, and digital signature tasks. You can use them in your custom J2ME applications. Brief descriptions of those common tasks are given in Table 20.1. For detailed discussions on those tasks, please refer to the relevant sections.

Table 20.1. Common Cryptographic Tasks Covered in This Chapter

The source code zip archive, which you can download from this book's Web site (see "Resources"), contains four sample applications, each demonstrating the API use of a crypto package (Bouncy Castle, IAIK, Phaos, and jNeo). Table 20.2 lists the toolkits versions used in this chapter. Inside each sample application, the most important class is CryptoEngine, which stores keys and provides thin wrappers over API methods. CryptoEngine sports a monolithic single class design not optimized for code reuse. Please do not consider it a best-practice example. Instead, the examples are designed to get you started with working code quickly. Each method in the CryptoEngine class demonstrates a complete application task (for example, the RSAEncrypt() method encrypts an input byte array using RSA).

Table 20.2. Toolkits Used in This Chapter

20.1.1 The Package Structure

All the J2ME crypto libraries used in the sample applications run on both CLDC and CDC platforms. To evaluate their performance in the most restricted environment, I provided user interface (UI) for the MIDP. The MIDlets drive the CryptoEngine and measure time spent on each task.

Building and running the sample applications is easy. You simply change the parameters in the build.xml file to reflect your system settings and run ANT tasks package and run. I bundled Bouncy Castle 1.16 in the BC sample. For other toolkit samples, you must contact vendors to obtain their software and evaluation licenses. You should put library JAR files in the lib/ directory.

20.1.2 Key Serialization

Besides basic encryption/decryption operations, key serialization is a core feature demonstrated in the examples. There are two important reasons for key serialization.

  • Except for the NTRUEncrypt algorithm, generating public key pairs on mobile devices is extremely time consuming.

  • In most applications, the sender and receiver use different devices, which requires keys to be transported over the network.

Note

In our examples, key serialization is used to minimize on-device key generation. All our encrypt/decrypt and sign/verify method pairs in the CryptoEngine class use the same in-memory key objects.


Classes in directory keygensrc pregenerate keys and serialize them to files in directory res/keys before MIDP suite packaging. CryptoEngine's constructor constructs pregenerated keys from files in the JAR's keys/ directory (res/keys directory in the build system). CryptoEngine also has methods to support direct key generation on mobile devices.


20.2 Symmetric Encryption

Symmetric encryption algorithms use randomly generated secret keys to encrypt and decrypt data. The sender and receiver must share the same key. The biggest advantage of symmetric algorithms is their speed. Symmetric algorithms are viable solutions even on small MIDP phones. However, the data security depends on the secrecy of the key. If an attacker somehow intercepted the key, he could easily forge or decrypt the entire communication content. The transportation and storage of secret keys is a big issue over the insecure networks. Our demo MIDlet for symmetric encryption and decryption is shown in Figure 20.1.

Figure 20.1. AES symmetric encryption using the BC library.

20.2.1 Bouncy Castle

The Bouncy Castle package supports a long list of symmetric algorithms, including AES, Rijndael, DES, triple DES, RC2, and RC4. Each cipher supports multiple buffering, padding, and ECB/CBC modes. The AES implementation is the most optimized. BC has three cipher engine classes for AES: AESLightEngine is an implementation optimized for low memory usage; AESFastEngine is optimized for speed; AESEngine is the compromise of the two.

The AES key and its initial vector (IV) are generated by random number generators in the generateAESKey() method in the CryptoEngine class (Listing 20.1).

Listing 20.1. Generate AES keys with BC
public void generateAESKey () throws Exception {

  SecureRandom sr = new SecureRandom();

  AESkey = new byte [16];

  sr.nextBytes(AESkey);

  AESinitV = new byte [16];

  sr.nextBytes(AESinitV);

}

Listing 20.2 illustrates how to serialize the key and IV to disk files (the GenerateAllKeys class). The CryptoEngine constructor reads out the serialized keys for later use on the mobile device (Listing 20.3). Since BC's AES keys are simple byte arrays, the serialization and deserialization involve only simple file stream operations.

Listing 20.2. Serialize AES keys to files offline
out = new FileOutputStream(outdir + "AESkey.dat");

out.write(AESkey);

out.flush(); out.close();



out = new FileOutputStream(outdir+"AESinitV.dat");

out.write(AESinitV);

out.flush(); out.close();

Listing 20.3. Read out AES keys in the CryptoEngine constructor
Class c = this.getClass();

InputStream is;



is = c.getResourceAsStream("/keys/AESkey.dat");

byte [] AESkey = readFromStream(is);

is.close();

is = c.getResourceAsStream("/keys/AESinitV.dat");

byte [] AESinitV = readFromStream(is);

is.close();

With keys generated, the AESFastEncrypt() method in the CryptoEngine class uses the AESFastEngine to encrypt a byte array of plain text into cipher text. The AESFastDecrypt() method decrypts the message into a plain text byte array. The code for both methods is shown in Listing 20.4.

Listing 20.4. AES encryption and decryption methods
public byte [] AESFastEncrypt (byte [] toEncrypt) throws Exception {

  BufferedBlockCipher cipher =

     new PaddedBufferedBlockCipher(

        new CBCBlockCipher(new AESFastEngine()));

  // If initV is not given, the program will

  // assume all zeros

  ParametersWithIV piv = new ParametersWithIV (

           (new KeyParameter(AESkey)), AESinitV);

  cipher.init(true, piv);

  byte[] result =

   new byte[cipher.getOutputSize(toEncrypt.length)];

  int len = cipher.processBytes(toEncrypt, 0,

                       toEncrypt.length, result, 0);



  try {

    cipher.doFinal(result, len);

  } catch (CryptoException ce) {

    // handles error

  }

  return result;

}



public byte [] AESFastDecrypt (byte [] toDecrypt) throws Exception {

  BufferedBlockCipher cipher =

    new PaddedBufferedBlockCipher(

      new CBCBlockCipher(new AESFastEngine()));

  ParametersWithIV piv = new ParametersWithIV (

      (new KeyParameter(AESkey)), AESinitV);

  cipher.init(false, piv);

  byte[] result = new byte[cipher.getOutputSize(toDecrypt.length)];

  int len = cipher.processBytes(toDecrypt, 0,

                       toDecrypt.length, result, 0);



  try {

    cipher.doFinal(result, len);

  } catch (CryptoException ce) {

    // Handle error

  }

  return result;

}

20.2.2 IAIK JCE-ME

In our IAIK JCE-ME example, the AES key serialization and deserialization part is exactly the same as the Bouncy Castle example. So, we only look at the key generation generateAESKey() method (Listing 20.5) in class CryptoEngine.

Listing 20.5. The generateAESKey() method
private byte [] AESkey;

private byte [] AESinitV;

// ... ...

public void generateAESKey () throws Exception {

  SecureRandom sr = new SecureRandom();

  AESkey = new byte [16];

  sr.nextBytes(AESkey);

  AESinitV = new byte [16];

  sr.nextBytes(AESinitV);

}

The encryption and decryption methods are shown in Listing 20.6.

Listing 20.6. AES encryption and decryption using IAIK JCE-ME
public byte [] AESEncrypt (byte [] toEncrypt) throws Exception {

  CryptoBag cipherKey = CryptoBag.makeSecretKey(AESkey);

  CryptoBag ivparam = CryptoBag.makeIV(AESinitV);

  Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

  cipher.init(Cipher.ENCRYPT_MODE, cipherKey, ivparam, null);

  return cipher.doFinal(toEncrypt);

}


public byte [] AESDecrypt (byte [] toDecrypt) throws Exception {

  CryptoBag cipherKey = CryptoBag.makeSecretKey(AESkey);

  CryptoBag ivparam = CryptoBag.makeIV(AESinitV);

  Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

  cipher.init(Cipher.DECRYPT_MODE, cipherKey, ivparam, null);

  return cipher.doFinal(toDecrypt);

} 

20.2.3 Phaos Micro Foundation

In the Phaos MF example, the generateAESKey() method (Listing 20.7) generates the AES key and IV.

Listing 20.7. Generate AES key and IV with Phaos MF
RandomBitsSource.setDefault(new SecureRBS());

RandomBitsSource.getDefault().seed();



public void generateAESKey () throws Exception {

  AlgorithmIdentifier algID = AlgIDList.AES_128_CBC;

  SymmetricKeyGenerator generator =

    new SymmetricKeyGenerator(algID,

               RandomBitsSource.getDefault());

  AESkey = generator.generateKey();



  // Create a new cipher, initialize it and then get

  // CBC init vector.

  Cipher cipher = BlockCipher.getInstance(algID,

       AESkey, BlockCipher.PADDING_PKCS5);

  CBCAlgID AESalgID =

    (CBCAlgID) cipher.createAlgID();

  AESinitV = AESalgID.iv;

  return;

}

In the Phaos MF package, the key serialization API is slightly different from BC and IAIK. Listings 20.8 and 20.9 illustrate the serialization and deserialization processes.

Listing 20.8. Serialize AES key and IV
FileOutputStream out = new FileOutputStream(outdir + "AESkey.der");

out.write(AESkey.keyMaterial);

out.flush(); out.close();

out = new FileOutputStream(outdir + "AESinitV.der");

out.write(AESinitV);

out.flush(); out.close();
Listing 20.9. AES key and IV deserialization in the CryptoEngine constructor
// 192 bit AES key

private SymmetricKey AESkey;

// CBC cipher init vector

private byte [] AESinitV;

// ... ...

Class c = this.getClass();

InputStream is;



// The AES init vector

is = c.getResourceAsStream("/keys/AESinitV.der");

AESinitV = readFromStream(is);



// The AES key

is = c.getResourceAsStream("/keys/AESkey.der");

byte[] keyMaterial = readFromStream(is);

AESkey = new SymmetricKey(keyMaterial, 0, keyMaterial.length);

The AESEncrypt() and AESDecrypt() methods demonstrate how to use the cipher (Listing 20.10).

Listing 20.10. The AESEncrypt() and AESDecrypt() methods
public byte [] AESEncrypt (byte [] toEncrypt) throws Exception {

  if ( AESkey == null || AESinitV == null)

    throw new Exception("Generate AES key first!");

  CBCAlgID AESalgID = new CBCAlgID(OIDList.AES_128_CBC, AESinitV);

  Cipher cipher = BlockCipher.getInstance(AESalgID,

       AESkey, BlockCipher.PADDING_PKCS5);

  PooledArray ciphertext =

    ((BlockCipher)cipher).encryptFinal(toEncrypt, 0, toEncrypt.length);

  return ciphertext.toByteArray(true);

}


public byte [] AESDecrypt (byte [] toDecrypt) throws Exception {

  if ( AESkey == null )

    throw new Exception("Generate AES key first!");

  CBCAlgID AESalgID = new CBCAlgID(OIDList.AES_128_CBC, AESinitV);

  Cipher cipher = BlockCipher.getInstance(AESalgID,

                   AESkey, BlockCipher.PADDING_PKCS5);

  PooledArray plaintext =

     ((BlockCipher)cipher).decryptFinal(toDecrypt, 0, toDecrypt.length);

  return plaintext.toByteArray(true);

}

20.2.4 NTRU jNeo

Symmetric key encryption is not the core innovation of NTRU but it is provided for the completeness of the package. AES key and IV are generated using code in Listing 20.11.

Listing 20.11. Generate AES key and IV with jNeo
RandomNumber rn =

  new RandomNumber(NTRUConst.NTRU_SHA1_HASH);



// ... ...



public void generateAESKey () throws Exception {

  AESinitV = new byte [RijndaelKey.BLOCK_SIZE];

  rn.getRandom(AESinitV, 0, AESinitV.length);



  byte [] keydata =

    new byte [RijndaelKey.BLOCK_SIZE];

  rn.getRandom(keydata, 0, keydata.length);

  AESkey = new RijndaelKey(keydata,

                   RijndaelKey.NTRU_SYM_KEYSTRENGTH_128);

  return;

}

The key serialization (Listing 20.12) and deserialization (Listing 20.13) processes are both very simple.

Listing 20.12. AES key and IV serialization
out = new FileOutputStream(outdir + "AESinitV.dat");

out.write(AESinitV);

out.flush(); out.close();

out = new FileOutputStream(outdir + "AESkeydata.dat");

out.write(keydata);

out.flush(); out.close();
Listing 20.13. AES key and IV deserialization
// AES init vector and 128 bit key

private byte [] AESinitV;

private RijndaelKey AESkey;

// ... ...

Class c = this.getClass();

InputStream is;

is = c.getResourceAsStream("/keys/AESkeydata.dat");

byte [] keydata = readFromStream(is);

AESkey = new RijndaelKey(keydata,

                 RijndaelKey.NTRU_SYM_KEYSTRENGTH_128);

is.close();



is = c.getResourceAsStream("/keys/AESinitV.dat");

AESinitV = readFromStream(is);

is.close();

Now, we can encrypt and decrypt messages (Listing 20.14). However, one thing important to note is that the plain text array to encrypt MUST have a length of a multiple of 16 bytes. If the message does not satisfy this requirement, the caller application must pad it properly. Please refer to the source code of the MIDlets for padding examples.

Listing 20.14. The AES encryption and decryption methods
// toEncrypt array length must be a multiple

// of 16 bytes

public byte [] AESEncrypt (byte [] toEncrypt) throws Exception {

  if ( AESkey == null || AESinitV == null )

    throw new Exception("Generate AES key first!");

  if ( toEncrypt.length \% 16 != 0 )

    throw new Exception("Not multiple of 16 bytes");



  int len = AESkey.ciphertextLength(

               RijndaelKey.NTRU_ENC_RIJNDAEL,

               RijndaelKey.NTRU_SYM_MODE_CBC,

               RijndaelKey.NTRU_SYM_KEYSTRENGTH_128,

               toEncrypt.length, false);

  byte [] result = new byte [len];

  AESkey.encrypt(toEncrypt, 0, toEncrypt.length,

                 result, 0, RijndaelKey.NTRU_SYM_MODE_CBC,

                 AESinitV, 0, false);

  return result;

}



public byte [] AESDecrypt (byte [] toDecrypt) throws Exception {

  if ( AESkey == null || AESinitV == null )

    throw new Exception("Generate AES key first!");



  int len = AESkey.plaintextLength(

              RijndaelKey.NTRU_ENC_RIJNDAEL,

              RijndaelKey.NTRU_SYM_MODE_CBC,

              RijndaelKey.NTRU_SYM_KEYSTRENGTH_128,

              toDecrypt.length, false);

  byte [] result = new byte [len];

  AESkey.decrypt(toDecrypt, 0, toDecrypt.length,

                 result, 0, RijndaelKey.NTRU_SYM_MODE_CBC,

                 AESinitV, 0, false);

  return result;

}

20.3 Password-Based Encryption

One problem with symmetric algorithms is that the users must keep the random keys. Since there is no way users can remember them in their heads, they store the keys in files, and that significantly increases the security risk. Password-based encryption (PBE) schemes use easy-to-remember passwords to generate keys for underlying symmetric parsers. PBE schemes are widely used to protect on-device data. In fact, we can use PBE-protected storages to keep other symmetric or private keys. Now, let's have a look at how PBE works in those libraries. Since the key is generated every time on-the-fly from the password, there is no key serialization process. All operations are contained in the encryption and decryption methods in the CryptoEngine class. For all algorithms, the underlying key generated from the password must be mixed in over and over again with a random "salt" to guarantee security. The iteration number is a programmable parameter and should be at least 1000. Our demo MIDlet for password-based encryption and decryption is shown in Figure 20.2.

Figure 20.2. Password-based encryption using BC's library

20.3.1 Bouncy Castle

Password-based encryption and decryption using the Bouncy Castle package are demonstrated in Listing 20.15.

Listing 20.15. Methods for PBE in the Bouncy Castle CryptoEngine example
// Generate random salt

private byte [] salt;

SecureRandom sr = new SecureRandom();

salt = new byte [16];

sr.nextBytes(salt);



// Get password to generate symmetric key with

// (or without IV) To be used in

// an AES underlying cipher

private CipherParameters getAESPasswdKey

          (char [] passwd) throws Exception {

  PBEParametersGenerator generator =

      new PKCS12ParametersGenerator(new SHA1Digest());

  generator.init(

PBEParametersGenerator.PKCS12PasswordToBytes(passwd),

        salt, 1024);

  // Generate a 128 bit key w/ 128 bit IV

  ParametersWithIV key =

(ParametersWithIV)generator.generateDerivedParameters(128, 128);

  // Generate a 128 kit key

  // CipherParameters key =

  // generator.generateDerivedParameters(128);

  return key;

 }



// Password based encryption using AES

public byte [] AESPasswdEncrypt (byte [] toEncrypt,

                  char [] passwd)throws Exception {

  ParametersWithIV key =

    (ParametersWithIV) getAESPasswdKey(passwd);

  // The following code uses an AES cipher to

  // encrypt the message

  BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(

                        new CBCBlockCipher(new AESFastEngine()));

  cipher.init(true, key);

  byte[] result = new byte[cipher.getOutputSize(toEncrypt.length)];

  int len = cipher.processBytes(toEncrypt, 0,

                     toEncrypt.length, result, 0);

  try {

    cipher.doFinal(result, len);

  } catch (CryptoException ce) {

    // handle error

  }

  return result;

}



// Password based decryption using AES

public byte [] AESPasswdDecrypt (byte [] toDecrypt,

                  char [] passwd) throws Exception {

  ParametersWithIV key = (ParametersWithIV) getAESPasswdKey(passwd);

  // The following code uses an AES cipher to

  // decrypt the message

  BufferedBlockCipher cipher =

       new PaddedBufferedBlockCipher(

         new CBCBlockCipher(new AESFastEngine()));

  cipher.init(false, key);

  byte[] result =

   new byte[cipher.getOutputSize(toDecrypt.length)];

  int len = cipher.processBytes(toDecrypt, 0,

                       toDecrypt.length, result, 0);

  try {

    cipher.doFinal(result, len);

  } catch (CryptoException ce) {

    // handle error

  }

  return result;

}

In the demo code package (see "Resources"), I have also included an example of PBE using underlying triple DES symmetric key. The triple DES key does not have an IV parameter.

20.3.2 IAIK JCE-ME

The use of PBE in IAIK JCE-ME is similar to that in Bouncy Castle but with a simpler API. I demonstrate the use of RC2 as the underlying cipher in the IAIK example (Listing 20.16).

Listing 20.16. PBE in the IAIK JCE-ME
private byte [] salt;

SecureRandom sr = new SecureRandom();

salt = new byte [16];

sr.nextBytes(salt);

// ... ...

public byte [] PBEEncrypt (byte [] toEncrypt,

             char [] passwd) throws Exception {

  PBE pbe = PBE.getInstance(PBE.OID_PKCS12_RC2_40_SHA);

  Cipher c = pbe.getCipher(Cipher.ENCRYPT_MODE,

                        passwd, salt, 1024, null);

  return c.doFinal(toEncrypt);

}



public byte [] PBEDecrypt (byte [] toDecrypt,

               char [] passwd) throws Exception {

  PBE pbe = PBE.getInstance(PBE.OID_PKCS12_RC2_40_SHA);

  Cipher c = pbe.getCipher(Cipher.DECRYPT_MODE,

                         passwd, salt, 1024, null);

  return c.doFinal(toDecrypt);

} 

20.3.3 Phaos Micro Foundation

In the Phaos MF example (Listing 20.17), we use the PKCS #5 scheme to generate the symmetric key. The salt mix-in iteration number is the default 1024 specified by the implementation. In the Phaos MF API, we need to generate the random IV AESinitV for the AES key ourselves. Also, please note that although handling PooledArray is tedious, it produces performance gains on small devices.

Listing 20.17. Password based encryption and decryption using Phaos MF
private byte [] salt;

RandomBitsSource.setDefault(new SecureRBS());;

RandomBitsSource.getDefault().seed();

salt = new byte[PBES2AlgID.DEFAULT_SALT_LENGTH];

RandomBitsSource.getDefault().randomBytes(salt);

// ... ...

// PKCS #5 password scheme 2 using

// AES as underlying cipher

public byte [] AESPasswdEncrypt(byte [] toEncrypt,

                String password) throws Exception {

  // Use SHA1 with AES

  AlgorithmIdentifier algID = new PBES2AlgID(salt,

               PBES2AlgID.DEFAULT_ITERATION_COUNT,

                PBES2AlgID.KEY_LENGTH_NOT_PRESENT,

                         AlgIDList.HMAC_WITH_SHA1,

      new CBCAlgID(OIDList.AES_128_CBC, AESinitV));

  ByteArrayInputStream in = new ByteArrayInputStream(toEncrypt);

  PooledArray plaintext = ByteArrayPool.getArray(in.available());

  in.read(plaintext.buffer, 0, plaintext.length);

  in.close();



  // PKCS #5 only uses lower 8 bits of

  // each password char

  PasswordBasedEncryptionScheme pbes =

    PasswordBasedEncryptionScheme.getInstance(algID,

                               password.getBytes());

  PooledArray ciphertext =

       pbes.encryptFinal(plaintext.buffer, 0,

                         plaintext.length);



  ByteArrayOutputStream out = new ByteArrayOutputStream ();

  algID.output(out);

  out.write(ciphertext.buffer, 0,

            ciphertext.length);

  byte [] encrypted = out.toByteArray();

  out.close();



  plaintext.release();

  ciphertext.release();

  return encrypted;

}



// PKCS #5 password scheme 2 decryption

//

// Since the algID is embedded inside the

// encrypted byte array, this method can decrypt

// any Phaos PBES messages with any

// underlying ciphers.

public byte [] AESPasswdDecrypt(byte [] toDecrypt,

                 String password) throws Exception {

  ByteArrayInputStream in = new ByteArrayInputStream(toDecrypt);

  AlgorithmIdentifier algID = new AlgorithmIdentifier(in);

  PooledArray ciphertext = ByteArrayPool.getArray(in.available());

  in.read(ciphertext.buffer, 0, ciphertext.length);

  in.close();



  PasswordBasedEncryptionScheme pbes =

    PasswordBasedEncryptionScheme.getInstance(algID,

                              password.getBytes());

  PooledArray plaintext =

      pbes.decryptFinal(ciphertext.buffer, 0,

                        ciphertext.length);



  ByteArrayOutputStream out = new ByteArrayOutputStream ();

  out.write(plaintext.buffer, 0, plaintext.length);

  byte [] decrypted = out.toByteArray();

  out.close();



  plaintext.release();

  ciphertext.release();

  return decrypted;

}

20.4 Public Key Encryption

Public key algorithms eliminate the key exchange problems associated with symmetric algorithms. Basically, each user has a pair of keys. The public key is available to anyone, and the private key is known only to the user. Public key algorithms are designed to be one-way trapdoor systems where a message encrypted by the public key can only be decrypted by the corresponding private key (see Figure 20.3). So, if Alice wants to send Bob a secret message, she uses Bob's public key to encrypt the data and sends it over any general insecure network. Only Bob himself has the correct private key to decrypt the message. The beauty of this scheme is that no secret key ever needs to be exchanged.

Figure 20.3. Public key algorithms are one-way trapdoors.
 

However, without built-in hardware acceleration or native long-integer support, public key encryption can be very slow on mobile devices. In reality, we often use public key algorithms to exchange symmetric keys and use symmetric key channels for further communication. Our demo MIDlet for RSA public key encryption and decryption is shown in Figure 20.4.

Figure 20.4. RSA public key encryption using the Phaos MF library.

20.4.1 Bouncy Castle

To generate an RSA key pair, BC requires the developer to pick a mathematical parameter called public exponent. A common pick is 65537 (Hex 10001) for strong security and fast performance. Listing 20.18 demonstrates how to generate an RSA key pair in the CryptoEngine class.

Listing 20.18. BC RSA key pair generation
public void generateRSAKeyPair () throws Exception {

  SecureRandom sr = new SecureRandom();

  BigInteger pubExp = new BigInteger("10001", 16);

  RSAKeyGenerationParameters RSAKeyGenPara =

       new RSAKeyGenerationParameters(pubExp, sr, 1024, 80);

  RSAKeyPairGenerator RSAKeyPairGen = new RSAKeyPairGenerator();

  RSAKeyPairGen.init(RSAKeyGenPara);

  AsymmetricCipherKeyPair keyPair = RSAKeyPairGen.generateKeyPair();



  RSAprivKey = (RSAPrivateCrtKeyParameters) keyPair.getPrivate();

  RSApubKey = (RSAKeyParameters) keyPair.getPublic();

}

Listings 20.19 and 20.20 demonstrate how to serialize the key pair offline to disk files and then read them back. Please note that since BC does not support a good key serialization mechanism in its lightweight API package, we have to manually serialize all key parameters, which is tedious.

Listing 20.19. Serialize the key pair to disk files
BigInteger mod = RSAprivKey.getModulus();

out = new FileOutputStream(outdir + "RSAmod.dat");

out.write(mod.toByteArray());

out.flush(); out.close();



BigInteger privExp = RSAprivKey.getExponent();

out = new FileOutputStream(outdir + "RSAprivExp.dat");

out.write(privExp.toByteArray());

out.flush(); out.close();



pubExp = RSAprivKey.getPublicExponent();

if ( !pubExp.equals(new BigInteger("10001", 16)) )

  throw new Exception("wrong public exponent");

out = new FileOutputStream(outdir + "RSApubExp.dat");

out.write(pubExp.toByteArray());

out.flush(); out.close();



BigInteger dp = RSAprivKey.getDP();

out = new FileOutputStream(outdir + "RSAdp.dat");

out.write(dp.toByteArray());

out.flush(); out.close();



BigInteger dq = RSAprivKey.getDQ();

out = new FileOutputStream(outdir + "RSAdq.dat");

out.write(dq.toByteArray());

out.flush(); out.close();



BigInteger p = RSAprivKey.getP();

out = new FileOutputStream(outdir + "RSAp.dat");

out.write(p.toByteArray());

out.flush(); out.close();



BigInteger q = RSAprivKey.getQ();

out = new FileOutputStream(outdir + "RSAq.dat");

out.write(q.toByteArray());

out.flush(); out.close();



BigInteger qInv = RSAprivKey.getQInv();

out = new FileOutputStream(outdir + "RSAqInv.dat");

out.write(qInv.toByteArray());

out.flush(); out.close();

Listing 20.20. Deserialize the key pair in CryptoEngine constructor
Class c = this.getClass();

InputStream is;

is = c.getResourceAsStream("/keys/RSAmod.dat");

BigInteger RSAmod = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSAprivExp.dat");

BigInteger RSAprivExp = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSApubExp.dat");

BigInteger RSApubExp = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSAdp.dat");

BigInteger RSAdp = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSAdq.dat");

BigInteger RSAdq = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSAp.dat");

BigInteger RSAp = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSAq.dat");

BigInteger RSAq = new BigInteger(readFromStream(is));

is.close();

is = c.getResourceAsStream("/keys/RSAqInv.dat");

BigInteger RSAqInv = new BigInteger(readFromStream(is));

is.close();



RSAprivKey = new RSAPrivateCrtKeyParameters(

    RSAmod, RSApubExp, RSAprivExp, RSAp,

    RSAq, RSAdp, RSAdq, RSAqInv);

RSApubKey = new RSAKeyParameters(false, RSAmod, RSApubExp);

Now, we can encrypt and decrypt messages using the public key RSApubKey and private key RSAprivKey respectively (Listing 20.21).

Listing 20.21. RSA encryption and decryption with BC
// Public key encrypt using RSA

public byte [] RSAEncrypt (byte [] toEncrypt) throws Exception {

  if (RSApubKey == null)

    throw new Exception("Generate RSA keys first!");



  AsymmetricBlockCipher eng = new RSAEngine();

  eng = new PKCS1Encoding(eng);

  eng.init(true, RSApubKey);

  return eng.processBlock(toEncrypt, 0, toEncrypt.length);

}



// private key decrypt

public byte [] RSADecrypt (byte [] toDecrypt) throws Exception {

  if (RSAprivKey == null)

    throw new Exception("Generate RSA keys first!");



  AsymmetricBlockCipher eng = new RSAEngine();

  eng = new PKCS1Encoding(eng);

  eng.init(false, RSAprivKey);

  return eng.processBlock(toDecrypt, 0, toDecrypt.length);

}

20.4.2 IAIK JCE-ME

Listing 20.22 illustrates how to generate an RSA key pair using the IAIK library.

Listing 20.22. Generate RSA keys using the IAIK library
public void generateRSAKeyPair () throws Exception {

  RSAKeyPairGenerator rsaKeyPairGenerator = new RSAKeyPairGenerator();

  rsaKeyPairGenerator.initialize(1024, null, null);

  CryptoBag cryptoBag = rsaKeyPairGenerator.generateKeyPair();

  RSApubKey =

      (PublicKey) cryptoBag.getCryptoBag(cryptoBag.V_KEY_PUBLIC);

  RSAprivKey =

      (PrivateKey) cryptoBag.getCryptoBag(cryptoBag.V_KEY_PRIVATE);

  return;

}

Listings 20.23 and 20.24 demonstrate the serialization and deserialization processes in the GenerateAllKeys class and the CryptoEngine constructor.

Listing 20.23. Serialize the keys to a disk file
out = new FileOutputStream(outdir + "RSApub.dat");

out.write(RSApubKey.getEncoded());

out.flush(); out.close();



out = new FileOutputStream(outdir + "RSApriv.dat");

out.write(RSAprivKey.getEncoded());

out.flush(); out.close();

Listing 20.24. Deserialize the RSA key pair
Class c = this.getClass();

InputStream is;



byte [] asnArray;

is = c.getResourceAsStream("/keys/RSApub.dat");

asnArray = readFromStream(is);

is.close();

RSApubKey = new PublicKey(new ASN1(asnArray));



is = c.getResourceAsStream("/keys/RSApriv.dat");

asnArray = readFromStream(is);

is.close();

RSAprivKey = new PrivateKey(new ASN1(asnArray));

With the key pair, we can now encrypt and decrypt messages (Listing 20.25). Notice how simple the API is!

Listing 20.25. RSA encryption and decryption
// Public key encrypt using RSA

public byte [] RSAEncrypt (byte [] toEncrypt) throws Exception {

  if (RSApubKey == null)

    throw new Exception("Generate RSA keys first!");



  Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");

  rsa.init(Cipher.ENCRYPT_MODE, RSApubKey, null, null);

  return rsa.doFinal(toEncrypt);

}



// private key decrypt

public byte [] RSADecrypt (byte [] toDecrypt) throws Exception {

  if (RSAprivKey == null)

    throw new Exception("Generate RSA keys first!");



  Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");

  rsa.init(Cipher.DECRYPT_MODE, RSAprivKey, null, null);

  return rsa.doFinal(toDecrypt);

}

20.4.3 Phaos Micro Foundation

Using the Phaos MF library, generating an RSA key pair is easy (Listing 20.26).

Listing 20.26. Generate a RSA key pair using Phaos MF
public void generateRSAKeyPair () throws Exception {

  RSAKeyGenParams params = new RSAKeyGenParams(1024);

  KeyPairGenerator kpg =

    KeyPairGenerator.getInstance(

            OIDList.RSA_ENCRYPTION, params);

  KeyPair kp = kpg.generateKeyPair();

  RSApubKey = (RSAPublicKey)kp.publicKey;

  RSAprivKey = (RSAPrivateKey)kp.privateKey;

  return;

}

Serializing and deserializing keys in the Phaos library are demonstrated in Listings 20.27 and 20.28.

Listing 20.27. Serialize RSA keys to files
RSApubKey.output(new FileOutputStream(outdir + "RSApubKey.der"));

RSAprivKey.output(new FileOutputStream(outdir + "RSAprivKey.der"));

Listing 20.28. Deserialize keys from the files
Class c = this.getClass();

InputStream is;



is = c.getResourceAsStream("/keys/RSApubKey.der");

RSApubKey = new RSAPublicKey(is);

is.close();

is = c.getResourceAsStream("/keys/RSAprivKey.der");

RSAprivKey = new RSAPrivateKey(is);

is.close();

Encryption and decryption are demonstrated in Listing 20.29.

Listing 20.29. RSA encryption and decryption
public byte [] RSAEncrypt (byte [] toEncrypt) throws Exception {

  if (RSApubKey == null)

    throw new Exception("Generate RSA keys first!");

  byte [] encrypted;



  PooledArray plaintext = ByteArrayPool.getArray(toEncrypt);

  AlgorithmIdentifier algID = new OAEPAlgID();

  Cipher cipher = Cipher.getInstance(algID, RSApubKey);

  PooledArray ciphertext =

    cipher.encrypt(plaintext.buffer, 0, plaintext.length);



  ByteArrayOutputStream out = new ByteArrayOutputStream ();

  algID.output(out);

  out.write(ciphertext.buffer, 0, ciphertext.length);

  encrypted = out.toByteArray();

  out.close();

  plaintext.release();

  ciphertext.release();

  return encrypted;

}



public byte [] RSADecrypt (byte [] toDecrypt) throws Exception {

  if (RSAprivKey == null)

    throw new Exception("Generate RSA keys first!");

  byte [] decrypted;



  ByteArrayInputStream in = new ByteArrayInputStream(toDecrypt);

  AlgorithmIdentifier algID = new AlgorithmIdentifier(in);

  PooledArray ciphertext = ByteArrayPool.getArray(in.available());

  in.read(ciphertext.buffer, 0, ciphertext.length);

  in.close();



  Cipher cipher = Cipher.getInstance(algID, RSAprivKey);

  PooledArray plaintext =

    cipher.decrypt(ciphertext.buffer, 0, ciphertext.length);

  decrypted = plaintext.toByteArray();



  plaintext.release();

  ciphertext.release();

  return decrypted;

}

20.4.4 NTRU jNeo

Generating an NTRU key pair for the NTRUEncrypt algorithm takes only one line of code. The following code snippet generates an NTRU 251-bit encryption key, which has cryptographic strength equivalent to a 1,024-bit RSA key (Listing 20.30).

Listing 20.30. Generate the NTRU encryption key pair
private RandomNumber rn =

   new RandomNumber(NTRUConst.NTRU_SHA1_HASH);

private Context ctx = new Context(rn);



// ... ...



public void generateNTRUencKeys () throws Exception {

  NTRUencKeys = new EncKeys(ctx,

       NTRUConst.NTRU_KEYSTRENGTH_251,

       NTRUConst.NTRU_SHA1_HASH);

  return;

}

NTRU jNeo provides its own key serialization methods. Let's see how it is done in the GenerateAllKeys class (Listing 20.31) and the CryptoEngine (Listing 20.32) constructor.

Listing 20.31. Serialize the key pair to disk files
byte [] pubKey = NTRUencKeys.exportPubKey(null, 0, true);

out = new FileOutputStream(outdir + "EncPubKey.dat");

out.write(pubKey); out.flush(); out.close();



byte [] privKey = NTRUencKeys.exportPrivKey(null, 0, true);

out = new FileOutputStream(outdir + "EncPrivKey.dat");

out.write(privKey); out.flush(); out.close();

Listing 20.32. Deserialize the key pair from disk files
Class c = this.getClass();

InputStream is;



is = c.getResourceAsStream("/keys/EncPubKey.dat");

byte [] encPubKeyData = readFromStream(is);

is.close();

is = c.getResourceAsStream("/keys/EncPrivKey.dat");

byte [] encPrivKeyData = readFromStream(is);

is.close();

NTRUencKeys = new EncKeys (encPubKeyData, o,

                           encPubKeyData.length,

                           encPrivKeyData, 0,

                           encPrivKeyData.length,

                           true);

The encryption and decryption methods are demonstrated in Listing 20.33. Notice that we take care of the proper block handling in our methods.

Listing 20.33. Encryption and decryption using the NTRU algorithm
public byte [] NTRUEncrypt (byte [] toEncrypt) throws Exception {

  if ( NTRUencKeys == null )

    throw new Exception("Generate keys first!");



  int cipherBlockSize =

     NTRUencKeys.ciphertextSize(NTRUConst.NTRU_KEYSTRENGTH_251);

  int plainBlockSize =

     NTRUencKeys.blockSize(NTRUConst.NTRU_KEYSTRENGTH_251);



  byte [] cipherBlock = new byte [cipherBlockSize];

  byte [] plainBlock = new byte [plainBlockSize];

  int psize;

  ByteArrayInputStream bais = new ByteArrayInputStream(toEncrypt);

  ByteArrayOutputStream baos = new ByteArrayOutputStream();

  while ( (psize = bais.read(plainBlock)) != -1 ) {

    // resize the last plain text block

    if ( psize < plainBlockSize ) {

      byte tmp [] = new byte [psize];

      for (int i = 0; i < psize; i++) {

        tmp[i] = plainBlock[i];

      }

      plainBlock = tmp;

    }



    NTRUencKeys.blockEncrypt(ctx, plainBlock, 0,

                            plainBlock.length, cipherBlock, 0);

    baos.write(cipherBlock);

  }

  baos.flush();

  byte [] result = baos.toByteArray();

  baos.close(); bais.close();

  return result;

}



public byte [] NTRUDecrypt (byte [] toDecrypt) throws Exception {

  if ( NTRUencKeys == null )

    throw new Exception("Generate keys first!");



  int cipherBlockSize =

    NTRUencKeys.ciphertextSize(NTRUConst.NTRU_KEYSTRENGTH_251);

  int plainBlockSize =

    NTRUencKeys.blockSize(NTRUConst.NTRU_KEYSTRENGTH_251);



  byte [] cipherBlock = new byte [cipherBlockSize];

  byte [] plainBlock = new byte [plainBlockSize];

  int psize;

  ByteArrayInputStream bais = new ByteArrayInputStream(toDecrypt);

  ByteArrayOutputStream baos = new ByteArrayOutputStream();

  while ( bais.read(cipherBlock) != -1 ) {

    psize = NTRUencKeys.blockDecrypt(ctx,

                     cipherBlock, 0,

                     cipherBlock.length,

                     plainBlock, 0);

    // resize the last plain text block

    if ( psize < plainBlockSize ) {

      byte tmp [] = new byte [psize];

      for (int i = 0; i < psize; i++) {

        tmp[i] = plainBlock[i];

      }

      plainBlock = tmp;

    }

    baos.write(plainBlock);

  }

  baos.flush();

  byte [] result = baos.toByteArray();

  baos.close(); bais.close();

  return result;

}

20.5 Digital Signature

Digital signature can guarantee message integrity and authenticity in an open network environment. To generate a signature, you first calculate a hash (also called digest) of your message. Then you encrypt that hash with your private key to generate a signature. The party at the receiving end first decrypts your signature into a hash using your public key. Then she calculates the hash from your message. If the two hashes match, the message is indeed from you and unaltered. The signing and verification processes are demonstrated in Figures 20.5 and 20.6 respectively.

Figure 20.5. Generate a digital signature.
 

If someone altered the message during its transmission and generated a new hash based on the modified message, public key algorithms guarantee that he cannot produce a matching signature without knowing your private key. The two parties must share four pieces of information: the message itself, its digital signature, the hash and signing algorithms, and the public key. Our demo MIDlet for digital signature is shown in Figure 20.7.

Figure 20.7. NTRUSign signature generation and verification.

20.5.1 Bouncy Castle

Bouncy Castle supports DSA, RSA, and ECC (Elliptic Curve Cryptography) digital signature algorithms. But only RSA seems to have an acceptable performance on MIDP devices. So, the code example here only shows how to work with RSA signatures. Since the RSA key pair generation and serialization part is already covered by the last section, we go directly to the code for signature generation and verification (Listing 20.34).

Listing 20.34. Sign and verify a message using the RSA algorithm in BC
public byte [] RSASign (byte [] toSign) throws Exception {

  if (RSAprivKey == null)

    throw new Exception("Generate RSA keys first!");

  SHA1Digest dig = new SHA1Digest();

  RSAEngine eng = new RSAEngine();



  PSSSigner signer = new PSSSigner(eng, dig, 64);

  signer.init(true, RSAprivKey);

  signer.update(toSign, O, toSign.length);

  return signer.generateSignature();

}



public boolean RSAVerify (byte [] mesg, byte [] sig)

                                 throws Exception {

  if (RSApubKey == null)

    throw new Exception("Generate RSA keys first!");

  SHA1Digest dig = new SHA1Digest();

  RSAEngine eng = new RSAEngine();



  PSSSigner signer = new PSSSigner(eng, dig, 64);

  signer.init(false, RSApubKey);

  signer.update(mesg, 0, mesg.length);

  return signer.verifySignature(sig);

} 

20.5.2 IAIK JCE-ME

Our IAIK digital signature example also uses the RSA algorithm. So, again, we do not show any code for the key generation and serialization. Listing 20.35 demonstrates the signing and verification processes.

Listing 20.35. RSA digital signature sign and verify methods in IAIK
// RSA signature

public byte [] RSASign (byte [] toSign) throws Exception {

  if (RSAprivKey == null)

    throw new Exception("Generate RSA keys first!");



  Signature sharsa = Signature.getInstance("SHA1withRSA");

  sharsa.initSign(RSAprivKey, null);

  sharsa.update(toSign);

  return sharsa.sign();

}



// RSA signature verification

public boolean RSAVerify (byte [] mesg, byte [] sig)

                                  throws Exception {

  if (RSApubKey == null)

    throw new Exception("Generate RSA keys first!");



  Signature sharsa = Signature.getInstance("SHA1withRSA");

  sharsa.initVerify(RSApubKey);

  sharsa.update(mesg);

  return sharsa.verify(sig);

} 

20.5.3 Phaos Micro Foundation

Phaos MF supports DSA and RSA digital signatures. DSA is certainly slower than RSA. But since we already have two RSA examples, we will have a look at a DSA example here. Generating DSA key pairs using the Phaos MF API is illustrated in Listing 20.36. Variable DSApubKeyDer is not the public key itself. Rather, it is a byte array representation of the public key in DER (Distinguished Encoding Rules) format. The public key can be reconstructed from this array using an appropriate algorithm identifier (see Listing 20.38).

Listing 20.36. Phaos MF DSA key pair generation
// 1024-bit DSA key

private DSAPrivateKey DSAprivKey;

// This is the DSA public key data you can serialize

private byte [] DSApubKeyDer;



public void generateDSAKeyPair () throws Exception {

  DSAKeyGenParams params =

     new DSAKeyGenParams(1024, RandomBitsSource.getDefault());

  KeyPairGenerator kpg =

      KeyPairGenerator.getInstance(OIDList.DSA, params);

  KeyPair kp = kpg.generateKeyPair();



  DSAprivKey = (DSAPrivateKey)kp.privateKey;



  ByteArrayOutputStream baos = new ByteArrayOutputStream ();

  ((DSAPublicKey)kp.publicKey).output(baos);

  DSApubKeyDer = baos.toByteArray();



  return;

}

The Phaos DSA key pair serialization and deserialization processes are illustrated in Listings 20.37 and 20.38.

Listing 20.37. Phaos MF DSA key pair serialization
DSAPublicKey pubKey = (DSAPublicKey)kp.publicKey;

DSAPrivateKey privKey = (DSAPrivateKey)kp.privateKey;

pubKey.output(new FileOutputStream(outdir + "DSApubKey.der"));

privKey.output(new FileOutputStream(outdir + "DSAprivKey.der"));

DSAParams dsaParams =

  new DSAParams(

    new ByteArrayInputStream(

      privKey.createAlgID(true).encodeParameters()));

dsaParams.output(new FileOutputStream(outdir + "DSAparams.der"));

Listing 20.38. Phaos MF DSA key pair deserialization
// The DSA private key

is = c.getResourceAsStream("/keys/DSAparams.der");

DSAParams params = new DSAParams(is);

AlgorithmIdentifier algID = getDSAalgID(params);

is.close();

is = c.getResourceAsStream("/keys/DSAprivKey.der");

DSAprivKey = new DSAPrivateKey(algID, is);

is.close();



// The DSA public key byte array

is = c.getResourceAsStream("/keys/DSApubKey.der");

baos = new ByteArrayOutputStream();

b = new byte[1];

while ( is.read(b) != -1 ) {

   baos.write(b);

}

is.close();

DSApubKeyDer = baos.toByteArray();

baos.close();

The method getDSAalgID() (Listing 20.38) retrieves the algorithm identifier from DSA parameters. It is also used in DSA sign and verify examples (Listing 20.39).

Listing 20.39. Phaos MF DSA sign and verify methods
private AlgorithmIdentifier getDSAalgID (DSAParams params)

                                           throws Exception {

  ByteArrayOutputStream paramsOut = new ByteArrayOutputStream();

  params.output(paramsOut);

  paramsOut.close();

  return new AlgorithmIdentifier(OIDList.DSA, paramsOut.toByteArray());

}



public byte [] DSASign (byte [] toSign) throws Exception {

  if (DSAprivKey == null)

    throw new Exception("Generate DSA keys first!");

  Signature signature =

    Signature.getInstance(AlgIDList.SHA1_WITH_DSA, DSAprivKey);

  byte [] result =

     signature.sign(toSign, 0, toSign.length).toByteArray(true);

  DSAParams params = new DSAParams(

    new ByteArrayInputStream(

      DSAprivKey.createAlgID(true).encodeParameters()));

  AlgorithmIdentifier algID = getDSAalgID (params);



  ByteArrayOutputStream baos = new ByteArrayOutputStream ();

  algID.output(baos);

  baos.write(result, 0, result.length);

  baos.flush(); baos.close();



  return baos.toByteArray();

}



public boolean DSAVerify (byte [] mesg, byte [] sig)

                                 throws Exception {

  InputStream is = new ByteArrayInputStream(sig);

  AlgorithmIdentifier algID = new AlgorithmIdentifier(is);

  PooledArray sigBytes = ByteArrayPool.getArray(is.available());

  is.read(sigBytes.buffer, 0, sigBytes.length);

  is.close();



  DSAPublicKey DSApubKey = new DSAPublicKey(algID,

          new ByteArrayInputStream(DSApubKeyDer));



  Signature signature =

    Signature.getInstance(AlgIDList.SHA1_WITH_DSA, DSApubKey);



  return signatur
Thursday, 20 August 2009 10:26

Layout managers in Java Swing

Layout Managers


Layout managers are used in Java Swing applications to arrange objects when they are added to a Container object. The setLayout() method is used to override default layout managers appropriated to JPanel (FlowLayout) and JFrame (BorderLayout) containers.

This section of the chapter discusses eight important layout managers:
❑ BorderLayout
❑ BoxLayout
❑ CardLayout
❑ FlowLayout
❑ GridbagLayout
❑ GridLayout
❑ SpringLayout
❑ GroupLayout

BorderLayout

The BorderLayout manager is the default layout for a frame. A BorderLayout uses five regions in its display space. Those regions are generally referred to as NORTH, SOUTH, WEST, EAST, and CENTER. Those regions generally refer to the same attributes that a map would use. The NORTH and SOUTH regions extend to the top and bottom areas of the Container, while the EAST and WEST regions extend from the bottom of the NORTH and top of the SOUTH regions and to the left and right sides of the Container, respectively. The CENTER region occupies all of the residual space that remains in the center of the Container.

The BorderLayout manager is typically generated by instantiating a new BorderLayout class with a constructor that has no parameters or with a constructor that specifies two integer values that specify the horizontal and vertical pixels between components in this fashion: new BorderLayout(int hGap, int vGap).

The following BorderLayout example emulates a test application that quizzes the user with five fairly simple arithmetic queries. As a user sequentially steps through the test questions, a progress bar will track where the test taker is with respect to the end of the test and what the running score is. Figure 4-1 provides a model of the application and shows how the different GUI components will occupy the BorderLayout panel.
 
Border Layout fig

BoxLayout

The BoxLayout manager arranges components horizontally from left to right, or vertically from top to bottom, without the wraparound capability in the FlowLayout manager. Implementation of the BoxLayout manager warrants the instantiation of the BoxLayout class with two parameters, the first being the Container panel that will be displayed, followed by an integer axis value that indicates the placement of the components on the panel. An axis value of Boxlayout.X_AXIS indicates left-to-right layout management, whereas a value of BoxLayout.Y_AXIS signifies a top-to-bottom layout.

The following BoxLayout example will apply the Decorator pattern in its implementation so that users can add behavior dynamically through drag-and-drop operations. The figure below provides a model of the application and how the different image components occupy the BoxLayout panel real estate.
 
Box Layout
Dynamic behavior transfer occurs when a user drags the Cheese condiment image onto one of the three food item images on the left panel. Some of the benefits of the Decorator pattern are defined in the following table.

FlowLayout


The FlowLayout manager arranges components from left to right in the Container space; if the space on a line is exhausted, the components that are part of this manager will flow to the next line. By default, all components of the FlowLayout manager are centered in a horizontal fashion on each line. Three different constructors can be invoked to instantiate a FlowLayout manager object. The first constructor requires no parameters, and the second constructor requires an integer alignment value that indicates how components will be justified during construction. The last constructor method uses an integer alignment value like the aforementioned method, but also requires two integer values that specify horizontal and vertical gap values for pixel spacing.

 Figure 4-5 provides a high-level view of the FlowLayoutPanel application and how the Swing components are positioned on the FlowLayout panel.

Request processing in the FlowLayputPanel application is handled with the Chain of Responsibility pattern that accepts the dollar amount from the user and cascades downward from the four different coin handlers (QuarterHandler, DimeHandler, NickelHandler, PennyHandler) until all the coins have been accounted for in the dollar amount specified by the user.

GridLayout

The GridLayout manager arranges its components in a rectangular, gridlike fashion. When components are added to the GridLayout manager, rows are populated first.
The following GridLayout example processes mouse events on buttons generated with Java 2D classes. Figure 4-7 shows how the buttons are organized on the GridLayoutPanel display.

GridBagLayout

The GridBagLayout manager manages its components both vertically and horizontally by maintaining a rectangular grid of cells in its display area. Components are manipulated through constraint parameters using the GridBagConstraints class. These constraints specify where a component’s display area should be positioned on the grid and its size using minimum and preferred size attributes.
The table here describes some attributes that can be implemented to position the GridBagLayout manager on a user display.

The following GridBagLayout example applies both the Command and Visitor patterns to handle user events and message generation from Swing component activities. Figure 4-9 provides a model of the application and the component distribution on the GridBagLayout and their listeners.

The GridBagLayoutPanel application will incorporate the Command and Visitor patterns to handle button requests for answers to the questions selected by the user in the different question components. Some of the benefits and shortcomings of these patterns are shown in the following table.

The GridBagLayoutPanel class incorporates the GridBagLayout manager, which allows for the placement of GUI components in a grid formation of rows and columns. The width and height of the rows and columns do not necessarily have to be the same size throughout a panel display, but this sample application maintains consistency across rows and columns for its GUI components:

SpringLayout

The SpringLayout manager lays out its Container components according to user-specified constraint parameters. Each constraint, represented by a Spring object, controls the vertical or horizontal distance between two component edges. The edges can belong to any child of the container or to the container its

The SpringLayout manager does not set the location of its components automatically like some of the other layout managers. Component locations need to be initialized through constraint parameters so that minimum, maximum, and preferred lengths can be contained and bound. The following are some of the fields used to describe the constraints for component placement.

Figure 4-11 demonstrates what the SpringLayout application will look like. Only one tabbed panel will be on display at a time, which will be dictated by the user navigations from the button components at the bottom of the application.

CardLayout

The CardLayout manager organizes its components as a stack of cards, where components are displayed one at a time. This allows components to be easily swapped in and out like a slide show presentation. The following CardLayout example employs the Command and Strategy patterns to encapsulate behavior that will be applied to the user text. Figure 4-13 shows the CardLayout model and the different Swing components applied to that layout manager panel.

The CardLayoutPanel application utilizes the Strategy pattern to apply different algorithms to userspecified text. The Command pattern is used to polymorphically determine what strategy to apply during runtime. Some of the benefits and drawbacks of these two patterns are shown in the following table.

GroupLayout

The GroupLayout manager is a component of the NetBeans framework that is being considered for inclusion in the SE 6 Java framework. The GroupLayout manager is important in that it allows for horizontal and vertical layout positioning in an independent, flexible manner.With this manager, layout groups can be formed in both a sequential and parallel fashion. Sequentially, they are placed one after another, and in parallel, they are placed on top of one another and are aligned with a common reference axis.

Published in Java