/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.security;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.primitives.Bytes;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import static java.util.Objects.requireNonNull;
/**
* SHA-512 based encryptor {@code hash = sha512(password + salt) + salt}.
*
* @author Yevhenii Voevodin
*/
public class SHA512PasswordEncryptor implements PasswordEncryptor {
/** 64 bit salt length is based on the <a href="https://www.ietf.org/rfc/rfc2898.txt">source</a>. */
private static final int SALT_BYTES_LENGTH = 64 / 8;
/** SHA-512 produces 512 bits. */
private static final int ENCRYPTED_PASSWORD_BYTES_LENGTH = 512 / 8;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private static final Charset PWD_CHARSET = Charset.forName("UTF-8");
@Override
public String encrypt(String password) {
requireNonNull(password, "Required non-null password");
// generate salt
final byte[] salt = new byte[SALT_BYTES_LENGTH];
SECURE_RANDOM.nextBytes(salt);
// sha512(password + salt)
final HashCode hash = Hashing.sha512().hashBytes(Bytes.concat(password.getBytes(PWD_CHARSET), salt));
final HashCode saltHash = HashCode.fromBytes(salt);
// add salt to the hash, result length (512 / 8) * 2 + (64 / 8) * 2 = 144
return hash.toString() + saltHash.toString();
}
@Override
public boolean test(String password, String passwordHash) {
requireNonNull(password, "Required non-null password");
requireNonNull(passwordHash, "Required non-null password's hash");
// retrieve salt from the hash
final int passwordHashLength = ENCRYPTED_PASSWORD_BYTES_LENGTH * 2;
if (passwordHash.length() < passwordHashLength + SALT_BYTES_LENGTH * 2) {
return false;
}
final HashCode saltHash = HashCode.fromString(passwordHash.substring(passwordHashLength));
// sha1(password + salt)
final HashCode hash = Hashing.sha512().hashBytes(Bytes.concat(password.getBytes(PWD_CHARSET), saltHash.asBytes()));
// test sha1(password + salt) + salt == passwordHash
return (hash.toString() + saltHash.toString()).equals(passwordHash);
}
}