Coverage Summary for Class: BCryptPasswordEncryptor (com.acciente.oacc.encryptor.bcrypt)

Class Class, % Method, % Line, %
BCryptPasswordEncryptor 100% (1/ 1) 100% (8/ 8) 100% (26/ 26)


1 /* 2  * Copyright 2009-2017, Acciente LLC 3  * 4  * Acciente LLC licenses this file to you under the 5  * Apache License, Version 2.0 (the "License"); you 6  * may not use this file except in compliance with the 7  * License. You may obtain a copy of the License at 8  * 9  * http://www.apache.org/licenses/LICENSE-2.0 10  * 11  * Unless required by applicable law or agreed to in 12  * writing, software distributed under the License is 13  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 14  * OR CONDITIONS OF ANY KIND, either express or implied. 15  * See the License for the specific language governing 16  * permissions and limitations under the License. 17  */ 18 package com.acciente.oacc.encryptor.bcrypt; 19  20 import com.acciente.oacc.encryptor.PasswordEncryptor; 21 import com.acciente.oacc.normalizer.TextNormalizer; 22 import org.bouncycastle.crypto.generators.OpenBSDBCrypt; 23  24 import java.io.Serializable; 25 import java.security.SecureRandom; 26  27 /** 28  * Password encryptor implementation that uses the OpenBSD BCrypt algorithm for creating password hashes. 29  */ 30 public class BCryptPasswordEncryptor implements PasswordEncryptor, Serializable { 31  private static final long serialVersionUID = 1L; 32  33  public static final String NAME = "bcrypt"; 34  35  private static final int BCRYPT_COST_FACTOR_MIN = 4; 36  private static final int BCRYPT_COST_FACTOR_MAX = 31; 37  private static final int BCRYPT_SALT_SIZE = 16; 38  39  private static final PasswordEncoderDecoder passwordEncoderDecoder = new PasswordEncoderDecoder(); 40  private static final SecureRandom secureRandom = new SecureRandom(); 41  42  private final int costFactor; 43  44  /** 45  * Returns a password encryptor that uses the BCrypt algorithm with the specified cost factor. 46  * 47  * @param costFactor the BCrypt cost factor, must be between {@value BCRYPT_COST_FACTOR_MIN} and 48  * {@value BCRYPT_COST_FACTOR_MAX} (inclusive). 49  * @return a BCryptPasswordEncryptor instance configured as described above. 50  * @throws IllegalArgumentException if the specified BCrypt cost factor is not between {@value BCRYPT_COST_FACTOR_MIN} 51  * and {@value BCRYPT_COST_FACTOR_MAX} (inclusive). 52  */ 53  public static BCryptPasswordEncryptor newInstance(int costFactor) { 54  assertCostFactorValid(costFactor); 55  return new BCryptPasswordEncryptor(costFactor); 56  } 57  58  private BCryptPasswordEncryptor(int costFactor) { 59  this.costFactor = costFactor; 60  } 61  62  @Override 63  public String encryptPassword(char[] plainPassword) { 64  if (plainPassword == null) { 65  return null; 66  } 67  final char[] normalizedChars = TextNormalizer.getInstance().normalizeToNfc(plainPassword); 68  69  final String bcryptString = OpenBSDBCrypt.generate(normalizedChars, gensalt(), costFactor /* log rounds */); 70  71  return passwordEncoderDecoder.encode(bcryptString); 72  } 73  74  @Override 75  public boolean checkPassword(char[] plainPassword, String storedPassword) { 76  if (plainPassword == null) { 77  return (storedPassword == null); 78  } 79  else if (storedPassword == null) { 80  return false; 81  } 82  83  final String bcryptString = passwordEncoderDecoder.decode(storedPassword); 84  final char[] normalizedChars = TextNormalizer.getInstance().normalizeToNfc(plainPassword); 85  86  return OpenBSDBCrypt.checkPassword(bcryptString, normalizedChars); 87  } 88  89  /** 90  * Returns the cost factor in use by this instance. 91  * 92  * @return the integer cost factor used by this instance. 93  */ 94  public int getCostFactor() { 95  return costFactor; 96  } 97  98  private static byte[] gensalt() { 99  final byte[] saltBytes = new byte[BCRYPT_SALT_SIZE]; 100  secureRandom.nextBytes(saltBytes); 101  return saltBytes; 102  } 103  104  private static void assertCostFactorValid(int computedCostFactorMin) { 105  if (computedCostFactorMin < BCRYPT_COST_FACTOR_MIN || computedCostFactorMin > BCRYPT_COST_FACTOR_MAX) { 106  throw new IllegalArgumentException("The cost factor must be between " + BCRYPT_COST_FACTOR_MIN + " and " + 107  BCRYPT_COST_FACTOR_MAX + " (inclusive)"); 108  } 109  } 110 }