Reviewing Cryptographic Code

OWASP Code Review Guide Table of Contents

Contact Author: [mailto:jenelle.chapman@gmail.com Jenelle Chapman]

Introduction
There are two types of cryptography in this world: cryptography that will stop your kid sister from reading your files, and cryptography that will stop major governments from reading your files [1]. Developers are at the forefront of deciding which category a particular application resides in. Cryptography provides for security of data at rest (via encryption), enforcement of data integrity (via hashing/digesting), and non-repudiation of data (via signing). As a result, the coding in a secure manner of any of the above cryptographic processes within source code must conform in principle to the use of standard cryptographically secure algorithms with strong key sizes.

The use of non-standard cryptographic algorithms, custom implementation of cryptography (standard & non-standard) algorithms, use of standard algorithms which are cryptographically insecure (e.g. DES), and the implementation of insecure keys can weaken the overall security posture of any application. Implementation of the methods aforementioned enables the use of known cryptanalytic tools & techniques to decrypt sensitive data.

Related Security Activities
Guide to Cryptography Using the Java Cryptographic Extensions

Vulnerable Patterns Examples for Cryptography
A secure way to implement robust encryption mechanisms within source code is by implementing FIPS compliant algorithms with the use of the Microsoft Data Protection API (DPAPI) or the Java Cryptography Extension (JCE). The following should be identified when establishing your cryptographic code strategy:

 Standard Algorithms Strong Algorithms Strong Key Sizes  Additionally, all sensitive data the application handles should be identified and encryption should be enforced. This includes user sensitive data, configuration data, etc. Specifically presence of the following identifies issues with Cryptographic Code:

.NET  Check that the Data Protection API (DPAPI) is being used Verify no proprietary algorithms are being used Check that RNGCryptoServiceProvider is used for PRNG Verify key length is at least 128 bits 

Java  Check that the Java Cryptography Extension (JCE) is being used Verify no proprietary algorithms are being used Check that SecureRandom (or similar) is used for PRNG Verify key length is at least 128 bits </ol>

Bad Practice: Use of Insecure Cryptographic Algorithms>>

The following algorithms are cryptographically insecure: DES and SHA-0. Below outlines a cryptographic implementation of DES (available per Using the Java Cryptographic Extensions):

package org.owasp.crypto;

import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.Cipher;

import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.security.InvalidAlgorithmParameterException; import javax.crypto.NoSuchPaddingException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException;

import sun.misc.BASE64Encoder;

/** * @author Joe Prasanna Kumar * This program provides the following cryptographic functionalities * 1. Encryption using DES * 2. Decryption using DES * * The following modes of DES encryption are supported by SUNJce provider * 1. ECB (Electronic code Book) - Every plaintext block is encrypted separately * 2. CBC (Cipher Block Chaining) - Every plaintext block is XORed with the previous ciphertext block * 3. PCBC (Propogating Cipher Block Chaining) - * 4. CFB (Cipher Feedback Mode) - The previous ciphertext block is encrypted and this enciphered block is XORed with the plaintext block to produce the corresponding ciphertext block * 5. OFB (Output Feedback Mode) - * *	High Level Algorithm : * 1. Generate a DES key * 2. Create the Cipher (Specify the Mode and Padding) * 3. To Encrypt : Initialize the Cipher for Encryption * 4. To Decrypt : Initialize the Cipher for Decryption * * Need for Padding : * Block ciphers operates on data blocks on fixed size n. * Since the data to be encrypted might not always be a multiple of n, the remainder of the bits are padded. * PKCS#5 Padding is what will be used in this program * */

public class DES { public static void main(String[] args) { String strDataToEncrypt = new String; String strCipherText = new String; String strDecryptedText = new String; try{ /**		 * Step 1. Generate a DES key using KeyGenerator * 		 */		KeyGenerator keyGen = KeyGenerator.getInstance("DES"); SecretKey secretKey = keyGen.generateKey; /**		 * Step2. Create a Cipher by specifying the following parameters * 			a. Algorithm name - here it is DES * 			b. Mode - here it is CBC * 			c. Padding - PKCS5Padding */		Cipher desCipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); /**		 * Step 3. Initialize the Cipher for Encryption */		desCipher.init(Cipher.ENCRYPT_MODE,secretKey); /**		 * Step 4. Encrypt the Data * 		1. Declare / Initialize the Data. Here the data is of type String * 		2. Convert the Input Text to Bytes * 		3. Encrypt the bytes using doFinal method */		strDataToEncrypt = "Hello World of Encryption using DES "; byte[] byteDataToEncrypt = strDataToEncrypt.getBytes; byte[] byteCipherText = desCipher.doFinal(byteDataToEncrypt); strCipherText = new BASE64Encoder.encode(byteCipherText); System.out.println("Cipher Text generated using DES with CBC mode and PKCS5 Padding is " +strCipherText); /**		 * Step 5. Decrypt the Data * 		1. Initialize the Cipher for Decryption * 		2. Decrypt the cipher bytes using doFinal method */		desCipher.init(Cipher.DECRYPT_MODE,secretKey,desCipher.getParameters); //desCipher.init(Cipher.DECRYPT_MODE,secretKey); byte[] byteDecryptedText = desCipher.doFinal(byteCipherText); strDecryptedText = new String(byteDecryptedText); System.out.println(" Decrypted Text message is " +strDecryptedText); }		catch (NoSuchAlgorithmException noSuchAlgo) {			System.out.println(" No Such Algorithm exists " + noSuchAlgo); }			catch (NoSuchPaddingException noSuchPad) {				System.out.println(" No Such Padding exists " + noSuchPad); }				catch (InvalidKeyException invalidKey) {					System.out.println(" Invalid Key " + invalidKey); }				catch (BadPaddingException badPadding) {					System.out.println(" Bad Padding " + badPadding); }				catch (IllegalBlockSizeException illegalBlockSize) {					System.out.println(" Illegal Block Size " + illegalBlockSize); }				catch (InvalidAlgorithmParameterException invalidParam) {					System.out.println(" Invalid Parameter " + invalidParam); }	}

}

Additionally, SHA-1 and MD5 should be avoided in new applications moving forward.

Good Patterns Examples for Cryptography
Good Practice: Use Strong Entropy>> The following source code outlines secure key generation per use of strong entropy (available per Using the Java Cryptographic Extensions): package org.owasp.java.crypto;

import java.security.SecureRandom; import java.security.NoSuchAlgorithmException;

import sun.misc.BASE64Encoder;

/** * @author Joe Prasanna Kumar * This program provides the functionality for Generating a Secure Random Number. *  * There are 2 ways to generate a  Random number through SecureRandom. * 1. By calling nextBytes method to generate Random Bytes * 2. Using setSeed(byte[]) to reseed a Random object * */

public class SecureRandomGen {

/**	 * @param args */	public static void main(String[] args) { try { // Initialize a secure random number generator SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); // Method 1 - Calling nextBytes method to generate Random Bytes byte[] bytes = new byte[512]; secureRandom.nextBytes(bytes); // Printing the SecureRandom number by calling secureRandom.nextDouble System.out.println(" Secure Random # generated by calling nextBytes is " + secureRandom.nextDouble); // Method 2 - Using setSeed(byte[]) to reseed a Random object int seedByteCount = 10; byte[] seed = secureRandom.generateSeed(seedByteCount); // TBR System.out.println(" Seed value is " + new BASE64Encoder.encode(seed)); secureRandom.setSeed(seed); System.out.println(" Secure Random # generated using setSeed(byte[]) is " + secureRandom.nextDouble); } catch (NoSuchAlgorithmException noSuchAlgo) {			System.out.println(" No Such Algorithm exists " + noSuchAlgo); }	}

}

Good Practice: Use Strong Algorithms>>

Below illustrates the implementation of AES (available per Using the Java Cryptographic Extensions):

package org.owasp.java.crypto;

import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.Cipher;

import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.security.InvalidAlgorithmParameterException; import javax.crypto.NoSuchPaddingException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException;

import sun.misc.BASE64Encoder;

/** * @author Joe Prasanna Kumar * This program provides the following cryptographic functionalities * 1. Encryption using AES * 2. Decryption using AES * * High Level Algorithm : * 1. Generate a DES key (specify the Key size during this phase) * 2. Create the Cipher * 3. To Encrypt : Initialize the Cipher for Encryption * 4. To Decrypt : Initialize the Cipher for Decryption * *  */

public class AES { public static void main(String[] args) { String strDataToEncrypt = new String; String strCipherText = new String; String strDecryptedText = new String; try{ /**		 * Step 1. Generate an AES key using KeyGenerator * 		Initialize the keysize to 128 * 		 */		KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128); SecretKey secretKey = keyGen.generateKey; /**		 * Step2. Create a Cipher by specifying the following parameters * 			a. Algorithm name - here it is AES */		Cipher aesCipher = Cipher.getInstance("AES"); /**		 * Step 3. Initialize the Cipher for Encryption */		aesCipher.init(Cipher.ENCRYPT_MODE,secretKey); /**		 * Step 4. Encrypt the Data * 		1. Declare / Initialize the Data. Here the data is of type String * 		2. Convert the Input Text to Bytes * 		3. Encrypt the bytes using doFinal method */		strDataToEncrypt = "Hello World of Encryption using AES "; byte[] byteDataToEncrypt = strDataToEncrypt.getBytes; byte[] byteCipherText = aesCipher.doFinal(byteDataToEncrypt); strCipherText = new BASE64Encoder.encode(byteCipherText); System.out.println("Cipher Text generated using AES is " +strCipherText); /**		 * Step 5. Decrypt the Data * 		1. Initialize the Cipher for Decryption * 		2. Decrypt the cipher bytes using doFinal method */		aesCipher.init(Cipher.DECRYPT_MODE,secretKey,aesCipher.getParameters); byte[] byteDecryptedText = aesCipher.doFinal(byteCipherText); strDecryptedText = new String(byteDecryptedText); System.out.println(" Decrypted Text message is " +strDecryptedText); }		catch (NoSuchAlgorithmException noSuchAlgo) {			System.out.println(" No Such Algorithm exists " + noSuchAlgo); }			catch (NoSuchPaddingException noSuchPad) {				System.out.println(" No Such Padding exists " + noSuchPad); }				catch (InvalidKeyException invalidKey) {					System.out.println(" Invalid Key " + invalidKey); }				catch (BadPaddingException badPadding) {					System.out.println(" Bad Padding " + badPadding); }				catch (IllegalBlockSizeException illegalBlockSize) {					System.out.println(" Illegal Block Size " + illegalBlockSize); }				catch (InvalidAlgorithmParameterException invalidParam) {					System.out.println(" Invalid Parameter " + invalidParam); }	}

}