/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.utils; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import nl.strohalm.cyclos.entities.Application.PasswordHash; import nl.strohalm.cyclos.services.application.ApplicationServiceLocal; import org.apache.commons.lang.RandomStringUtils; /** * Helper class used to hash passwords and manage salts * @author luis */ public class HashHandler { public static final String SHA_256 = "SHA-256"; public static final String MD5 = "MD5"; /** * Apply a SHA-256 hash * @param string */ public static String sha2(final String string) { return digest(SHA_256, null, string); } private static String digest(final String algorithm, final String salt, final String string) { if (string == null) { return null; } MessageDigest md = null; try { md = MessageDigest.getInstance(algorithm); } catch (final NoSuchAlgorithmException e) { throw new IllegalStateException(e); } md.reset(); try { if (salt != null) { md.update(salt.getBytes("UTF-8")); } return toHex(md.digest(string.getBytes("UTF-8"))); } catch (final UnsupportedEncodingException e) { // Never happens as UTF-8 is always supported return null; } } private static String toHex(final byte[] bytes) { final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; final char[] chars = new char[bytes.length * 2]; int j = 0; int k; for (final byte element : bytes) { k = element; chars[j++] = hexDigits[(k >>> 4) & 0x0F]; chars[j++] = hexDigits[k & 0x0F]; } return new String(chars); } private ApplicationServiceLocal applicationService; /** * Hashes the given string according to the current password hash algorithm */ public String hash(final String salt, final String string) { switch (getPasswordHash()) { case SHA2_SALT: return digest(SHA_256, salt, string); case SHA2: return digest(SHA_256, null, string); case SHA2_MD5: return digest(SHA_256, null, digest(MD5, null, string)); } return null; } /** * Returns a new salt, but only if the password hash is SHA2 / SALT. Otherwise, returns null */ public String newSalt() { if (getPasswordHash() != PasswordHash.SHA2_SALT) { return null; } return RandomStringUtils.randomAlphanumeric(32); } public void setApplicationServiceLocal(final ApplicationServiceLocal applicationService) { this.applicationService = applicationService; } private PasswordHash getPasswordHash() { return applicationService == null ? PasswordHash.SHA2_SALT : applicationService.getPasswordHash(); } }