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 }