Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This compliant solution addresses the problems from the previous noncompliant code exampleexamples:

Code Block
bgColor#ccccff
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
  
final class Password {
  private SecureRandom random = new SecureRandom();
  private final int SALT_BYTE_LENGTH = 12;
  private final int ITERATIONS = 100000;
  private final int KEY_BIT_LENGTH = 128;
  private final String ALGORITHM = "PBKDF2WithHmacSHA256";
    
  /* Set password to new value, zeroing out password */
  void setPassword(char[] pass)
      throws IOException, GeneralSecurityException  {
    byte[] salt = new byte[SALT_BYTE_LENGTH];
    random.nextBytes(salt);
    saveBytes(salt, "salt.bin");    
    byte[] hashVal = hashPassword( pass, salt); 
    saveBytes(hashVal,"password.bin");
    Arrays.fill(hashVal, (byte) 0);
  }

  /* Indicates if given password is correct */
  boolean checkPassword(char[] pass)
      throws IOException, GeneralSecurityException  {
    byte[] salt = loadBytes("salt.bin");
    byte[] hashVal1 = hashPassword( pass, salt);
    // Load the hash value stored in password.bin
    byte[] hashVal2 = loadBytes("password.bin");
    boolean arraysEqual = timingEquals( hashVal1, hashVal2);
    Arrays.fill(hashVal1, (byte) 0);
    Arrays.fill(hashVal2, (byte) 0);
    return arraysEqual;
  }
  
  /* Encrypts password & salt and zeroes both */
  private byte[] hashPassword(char[] pass, byte[] salt)
      throws GeneralSecurityException {
    KeySpec spec = new PBEKeySpec(pass, salt, ITERATIONS, KEY_BIT_LENGTH);
    Arrays.fill(pass, (char) 0);
    Arrays.fill(salt, (byte) 0);
    SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
    return f.generateSecret(spec).getEncoded();
  }

  /**
   * Indicates if both byte arrays are equal
   * but uses same amount of time if they are the same or different
   * to prevent timing attacks
   */
  public static boolean timingEquals(byte b1[], byte b2[]) {
    boolean result = true;
    int len = b1.length;
    if (len != b2.length) {
      result = false;
    }
    if (len > b2.length) {
      len = b2.length;
    }
    for (int i = 0; i < len; i++) {
      result &= (b1[i] == b2[i]);
    }
    return result;
  }

  private void saveBytes(byte[] bytes, String filename) throws IOException {
    // ... write bytes to the file
  }

  private byte[] loadBytes(String filename) throws IOException {
    // ... read bytes to the file
  }
}

...