Project: brix-cms
/**
 * 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(044) + encodedPassword.substring(84128); 
        String hash = encodedPassword.substring(4484); 
 
        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(044) + hash + salt.substring(4488); 
        } 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(); 
    } 
}