Showing posts with label Security. Show all posts
Showing posts with label Security. Show all posts

Wednesday, July 04, 2012

Using the Java Cryptographic Extensions

Overview

Java Cryptographic Extensions (JCE) is a set of Java API's which provides cryptographic services such as encryption, secret Key Generation, Message Authentication code and Key Agreement. The ciphers supported by JCE include symmetric, asymmetric, block and stream ciphers. JCE was an optional package to JDK v 1.2.x and 1.3.x. JCE has been integrated into JDK v1.4.

JCE API's are implemented by Cryptographic Service Providers. Each of these cryptographic service providers implements the Service Provider Interface which specifies the functionalities which needs to be implemented by the service providers. Programmers can plugin any Service Providers for performing cryptographic functionalities provided by JCE. J2SE comes with a default provider named SunJCE.

Symmetric Encryption Algorithms provided by SunJCE

  1. DES - default keylength of 56 bits
  2. AES -
  3. RC2, RC4 and RC5
  4. IDEA
  5. Triple DES – default keylength 112 bits
  6. Blowfish – default keylength 56 bits
  7. PBEWithMD5AndDES
  8. PBEWithHmacSHA1AndDESede
  9. DES ede

Modes of Encryption

  1. ECB
  2. CBC
  3. CFB
  4. OFB
  5. PCBC

Asymmetric Encryption Algorithms implemented by SunJCE

  1. RSA
  2. Diffie-Hellman – default keylength 1024 bits

Hashing / Message Digest Algorithms implemented by SunJCE

  1. MD5 – default size 64 bytes
  2. SHA1 - default size 64 bytes

Examples

SecureRandom

SecureRandom class is used to generate a cryptographically strong pseudo random number by using a PRNG Algorithm. The following are the advantages of using SecureRandom over Random. 1. SecureRandom produces a cryptographically strong pseudo random number generator. 2. SecureRandom produces cryptographically strong sequences as described in RFC 1750: Randomness Recommendations for Security
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);
  }
 }

}

AES Encryption and Decryption

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);
    }
 }

}

Des Encryption and Decryption

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);
    }
 }

}

Hashing Password Using Java

Introduction

Most of today’s applications use login/password in order to authenticate. Users often use the same login/password for different kinds of applications. If the couple is stolen, everybody can access all the applications the user has access to.
Too often passwords are stored as clear text. Thus the password can be read directly by the database’s administrator, super users or SQL Injection attack etc. The backup media is also vulnerable. In order to solve this problem, passwords must be stored encrypted. Two kinds of encryption are available:
  • One way functions (SHA-256 SHA-1 MD5, ..;) also known as Hashing functions
  • Reversible encryption functions (DES, AES, …).
However, the reversible property of encryption function is useless for credentials storing (cf. OWASP Guide v2.0.1) :
Passwords are secrets. There is no reason to decrypt them under any circumstances. Helpdesk staff should be able to set new passwords (with an audit trail, obviously), not read back old passwords. Therefore, there is no reason to store passwords in a reversible form.

Definition of cryptographic Hashing function:

A Hash function creates a fixed length small fingerprint (or message digest) from an unlimited input string.
hash(X) ->Y X is a infinite set and Y is a finite set.
A good cryptographic Hash function must have these properties:
  • Preimage resistant : From the function output y it must impossible to compute the input x such that hash(x)=y.
  • Second preimage resistant : from an input x1 it must impossible to compute another input x2 (different of x1) such that hash(x1)=hash(x2).
  • Collision resistant : It must be difficult to find two inputs x1 and x2 (x1<>x2) such that hash(x1)=hash(x2).
Sample java code :
 import java.security.MessageDigest;
 
 public byte[] getHash(String password) throws NoSuchAlgorithmException {
       MessageDigest digest = MessageDigest.getInstance("SHA-1");
       digest.reset();
       byte[] input = digest.digest(password.getBytes("UTF-8"));
 }

Credential storage.

If the password’s digest is stored in a database, an attacker should be unable to recover the password thanks to the preimage resistance. The only way to go past this would be a brute force attack, i.e. computing the hash of all possible passwords or a dictionary attack, i.e. computing all the often used password.

Why add salt ?

If each password is simply hashed, identical passwords will have the same hash. There are two drawbacks to choosing to only storing the password’s hash:
In order to solve these problems, a salt can be concatenated to the password before the digest operation.
A salt is a random number of a fixed length. This salt must be different for each stored entry. It must be stored as clear text next to the hashed password.
In this configuration, an attacker must handle a brute force attack on each individual password. The database is now birthday attack/rainbow crack resistant.
A 64 bits salt is recommended in RSA PKCS5 standard.
Sample java code :
 import java.security.MessageDigest;
 
 
 public byte[] getHash(String password, byte[] salt) throws NoSuchAlgorithmException {
       MessageDigest digest = MessageDigest.getInstance("SHA-256");
       digest.reset();
       digest.update(salt);
       return digest.digest(password.getBytes("UTF-8"));
 }

Hardening against the attacker's attack

To slow down the computation it is recommended to iterate the hash operation n times. While hashing the password n times does slow down hashing for both attackers and typical users, typical users don't really notice it being that hashing is such a small percentage of their total time interacting with the system. On the other hand, an attacker trying to crack passwords spends nearly 100% of their time hashing so hashing n times gives the appearance of slowing the attacker down by a factor of n while not noticeably affecting the typical user. A minimum of 1000 operations is recommended in RSA PKCS5 standard.
The stored password looks like this : Hash(hash(hash(hash(……….hash(password||salt)))))))))))))))
To authenticate a user, the operation same as above must be performed, followed by a comparison of the two hashes.
The hash function you need to use depends of your security policy. SHA-256 or SHA-512 is recommended for long term storage.
Sample java code :
 import java.security.*;
 
 
  public byte[] getHash(int iterationNb, String password, byte[] salt) throws NoSuchAlgorithmException {
       MessageDigest digest = MessageDigest.getInstance("SHA-1");
       digest.reset();
       digest.update(salt);
       byte[] input = digest.digest(password.getBytes("UTF-8"));
       for (int i = 0; i < iterationNb; i++) {
           digest.reset();
           input = digest.digest(input);
       }
       return input;
   }

Complete Java Sample

In order to create the table needed by this application, call the method creerTable(). It creates a TABLE called CREDENTIAL, with these fields :
  • LOGIN VARCHAR (100) PRIMARY KEY
  • PASSWORD VARCHAR (32)
  • SALT VARCHAR (32)
In this database, the password and the salt are stored in Base64 representation.
The method authenticate is used in order to authenticate a user, the method createUser is used to create a new user.




 package org.psafix.memopwd;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.io.IOException;
 import sun.misc.BASE64Decoder;
 import sun.misc.BASE64Encoder;
 import java.sql.*;
 import java.util.Arrays;
 import java.security.SecureRandom;
 
 public class Owasp {
   private final static int ITERATION_NUMBER = 1000;
 
   public Owasp() {
   }
 
   /**
    * Authenticates the user with a given login and password
    * If password and/or login is null then always returns false.
    * If the user does not exist in the database returns false.
    * @param con Connection An open connection to a databse
    * @param login String The login of the user
    * @param password String The password of the user
    * @return boolean Returns true if the user is authenticated, false otherwise
    * @throws SQLException If the database is inconsistent or unavailable (
    *           (Two users with the same login, salt or digested password altered etc.)
    * @throws NoSuchAlgorithmException If the algorithm SHA-1 is not supported by the JVM
    */
   public boolean authenticate(Connection con, String login, String password)
           throws SQLException, NoSuchAlgorithmException{
       boolean authenticated=false;
       PreparedStatement ps = null;
       ResultSet rs = null;
       try {
           boolean userExist = true;
           // INPUT VALIDATION
           if (login==null||password==null){
               // TIME RESISTANT ATTACK
               // Computation time is equal to the time needed by a legitimate user
               userExist = false;
               login="";
               password="";
           }
 
           ps = con.prepareStatement("SELECT PASSWORD, SALT FROM CREDENTIAL WHERE LOGIN = ?");
           ps.setString(1, login);
           rs = ps.executeQuery();
           String digest, salt;
           if (rs.next()) {
               digest = rs.getString("PASSWORD");
               salt = rs.getString("SALT");
               // DATABASE VALIDATION
               if (digest == null || salt == null) {
                   throw new SQLException("Database inconsistant Salt or Digested Password altered");
               }
               if (rs.next()) { // Should not append, because login is the primary key
                   throw new SQLException("Database inconsistent two CREDENTIALS with the same LOGIN");
               }
           } else { // TIME RESISTANT ATTACK (Even if the user does not exist the
               // Computation time is equal to the time needed for a legitimate user
               digest = "000000000000000000000000000=";
               salt = "00000000000=";
               userExist = false;
           }
 
           byte[] bDigest = base64ToByte(digest);
           byte[] bSalt = base64ToByte(salt);
 
           // Compute the new DIGEST
           byte[] proposedDigest = getHash(ITERATION_NUMBER, password, bSalt);
 
           return Arrays.equals(proposedDigest, bDigest) && userExist;
       } catch (IOException ex){
           throw new SQLException("Database inconsistant Salt or Digested Password altered");
       }
       finally{
           close(rs);
           close(ps);
       }
   }
 
 
 
   /**
    * Inserts a new user in the database
    * @param con Connection An open connection to a databse
    * @param login String The login of the user
    * @param password String The password of the user
    * @return boolean Returns true if the login and password are ok (not null and length(login)<=100
    * @throws SQLException If the database is unavailable
    * @throws NoSuchAlgorithmException If the algorithm SHA-1 or the SecureRandom is not supported by the JVM
    */
   public boolean createUser(Connection con, String login, String password)
           throws SQLException, NoSuchAlgorithmException
   {
       PreparedStatement ps = null;
       try {
           if (login!=null&&password!=null&&login.length()<=100){
               // Uses a secure Random not a simple Random
               SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
               // Salt generation 64 bits long
               byte[] bSalt = new byte[8];
               random.nextBytes(bSalt);
               // Digest computation
               byte[] bDigest = getHash(ITERATION_NUMBER,password,bSalt);
               String sDigest = byteToBase64(bDigest);
               String sSalt = byteToBase64(bSalt);
 
               ps = con.prepareStatement("INSERT INTO CREDENTIAL (LOGIN, PASSWORD, SALT) VALUES (?,?,?)");
               ps.setString(1,login);
               ps.setString(2,sDigest);
               ps.setString(3,sSalt);
               ps.executeUpdate();
               return true;
           } else {
               return false;
           }
       } finally {
           close(ps);
       }
   }
 
 
   /**
    * From a password, a number of iterations and a salt,
    * returns the corresponding digest
    * @param iterationNb int The number of iterations of the algorithm
    * @param password String The password to encrypt
    * @param salt byte[] The salt
    * @return byte[] The digested password
    * @throws NoSuchAlgorithmException If the algorithm doesn't exist
    */
   public byte[] getHash(int iterationNb, String password, byte[] salt) throws NoSuchAlgorithmException {
       MessageDigest digest = MessageDigest.getInstance("SHA-1");
       digest.reset();
       digest.update(salt);
       byte[] input = digest.digest(password.getBytes("UTF-8"));
       for (int i = 0; i < iterationNb; i++) {
           digest.reset();
           input = digest.digest(input);
       }
       return input;
   }
 
 
   public void creerTable(Connection con) throws SQLException{
       Statement st = null;
       try {
           st = con.createStatement();
           st.execute("CREATE TABLE CREDENTIAL (LOGIN VARCHAR(100) PRIMARY KEY, PASSWORD VARCHAR(32) NOT NULL, SALT VARCHAR(32) NOT NULL)");
       } finally {
           close(st);
       }
   }
 
 
 
   /**
    * Closes the current statement
    * @param ps Statement
    */
   public void close(Statement ps) {
       if (ps!=null){
           try {
               ps.close();
           } catch (SQLException ignore) {
           }
       }
   }
 
   /**
    * Closes the current resultset
    * @param ps Statement
    */
   public void close(ResultSet rs) {
       if (rs!=null){
           try {
               rs.close();
           } catch (SQLException ignore) {
           }
       }
   }
 
 
   /**
    * From a base 64 representation, returns the corresponding byte[] 
    * @param data String The base64 representation
    * @return byte[]
    * @throws IOException
    */
   public static byte[] base64ToByte(String data) throws IOException {
       BASE64Decoder decoder = new BASE64Decoder();
       return decoder.decodeBuffer(data);
   }
 
   /**
    * From a byte[] returns a base 64 representation
    * @param data byte[]
    * @return String
    * @throws IOException
    */
   public static String byteToBase64(byte[] data){
       BASE64Encoder endecoder = new BASE64Encoder();
       return endecoder.encode(data);
   }
 
 
 }