20.2 Symmetric EncryptionSymmetric 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 CastleThe 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 BCpublic 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 offlineout = 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 constructorClass 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 methodspublic 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-MEIn 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() methodprivate 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-MEpublic 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 FoundationIn 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 MFRandomBitsSource.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 IVFileOutputStream 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() methodspublic 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 jNeoSymmetric 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 jNeoRandomNumber 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 serializationout = 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; } |