/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.brixcms.rmiserver; import org.hibernate.exception.NestableRuntimeException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * {@link PasswordEncoder} that uses SHA1 to create a secure password hash. Before the hash is taken the password string * is combined with a 44 byte random salt value. This function produces a 128 character output that combines the hash * and the salt. * * @author igor.vaynberg */ public final class PasswordEncoder { private static final char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /* * (non-Javadoc) * * @see com.inertiabev.biggie.service.security.PasswordEncoder#matches(java.lang.String, * java.lang.String) */ public final boolean check(String password, String encodedPassword) { if (password == null) { throw new IllegalArgumentException("Argument `password` cannot be null"); } if (encodedPassword == null) { throw new IllegalArgumentException("Argument `encodedPassword` cannot be null"); } if (encodedPassword.length() != 128) { throw new IllegalArgumentException( "Argument `encodedPassword` does not contain a string encrypted using: " + getClass().getName()); } String salt = encodedPassword.substring(0, 44) + encodedPassword.substring(84, 128); String hash = encodedPassword.substring(44, 84); return hash.equals(hash(password, salt)); } /** * Creates the hash of the password and concatenated salt * * @param password password string * @param salt salt string * @return SHA1 hash of concatenated password and salt */ private String hash(String password, String salt) { String saltedPassword = salt + password; MessageDigest sha1; try { sha1 = MessageDigest.getInstance("SHA1"); sha1.update(saltedPassword.getBytes()); byte[] saltedHash = sha1.digest(); return bytesToHexString(saltedHash); } catch (NoSuchAlgorithmException e) { throw new NestableRuntimeException(e); } } /* * (non-Javadoc) * * @see com.inertiabev.biggie.service.security.PasswordEncoder#encode(java.lang.String) */ public final String encode(String password) { try { byte[] saltBytes = new byte[44]; SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.nextBytes(saltBytes); String salt = bytesToHexString(saltBytes); String hash = hash(password, salt); return salt.substring(0, 44) + hash + salt.substring(44, 88); } catch (Exception e) { throw new NestableRuntimeException(e); } } /** * Converts bytes to their hexadecimal string representation * * @param bytes bytes to convert * @return hexadecimal string */ private static String bytesToHexString(byte[] bytes) { StringBuffer sb = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { final byte b = bytes[i]; final char high = hexChars[(b & 0xF0) >> 4]; final char low = hexChars[b & 0x0F]; sb.append(high); sb.append(low); } return sb.toString(); } }