/* * To change this template, choose Tools | Templates and open the template in * the editor. */ package fr.xephi.authme.security.crypts; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * @author stefano * @version $Revision: 1.0 $ */ public class PHPBB implements EncryptionMethod { private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /** * Method md5. * * @param data String * * @return String */ public static String md5(String data) { try { byte[] bytes = data.getBytes("ISO-8859-1"); MessageDigest md5er = MessageDigest.getInstance("MD5"); byte[] hash = md5er.digest(bytes); return bytes2hex(hash); } catch (GeneralSecurityException | UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Method hexToInt. * * @param ch char * * @return int */ static int hexToInt(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; ch = Character.toUpperCase(ch); if (ch >= 'A' && ch <= 'F') return ch - 'A' + 0xA; throw new IllegalArgumentException("Not a hex character: " + ch); } /** * Method bytes2hex. * * @param bytes byte[] * * @return String */ private static String bytes2hex(byte[] bytes) { StringBuilder r = new StringBuilder(32); for (byte b : bytes) { String x = Integer.toHexString(b & 0xff); if (x.length() < 2) r.append('0'); r.append(x); } return r.toString(); } /** * Method pack. * * @param hex String * * @return String */ static String pack(String hex) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < hex.length(); i += 2) { char c1 = hex.charAt(i); char c2 = hex.charAt(i + 1); char packed = (char) (hexToInt(c1) * 16 + hexToInt(c2)); buf.append(packed); } return buf.toString(); } /** * Method phpbb_hash. * * @param password String * @param salt String * * @return String */ public String phpbb_hash(String password, String salt) { String random_state = salt; StringBuilder random = new StringBuilder(); int count = 6; for (int i = 0; i < count; i += 16) { random_state = md5(salt + random_state); random.append(pack(md5(random_state))); } String hash = _hash_crypt_private(password, _hash_gensalt_private(random.substring(0, count), itoa64)); if (hash.length() == 34) { return hash; } return md5(password); } /** * Method _hash_gensalt_private. * * @param input String * @param itoa64 String * * @return String */ private String _hash_gensalt_private(String input, String itoa64) { return _hash_gensalt_private(input, itoa64, 6); } /** * Method _hash_gensalt_private. * * @param input String * @param itoa64 String * @param iteration_count_log2 int * * @return String */ private String _hash_gensalt_private(String input, String itoa64, int iteration_count_log2) { if (iteration_count_log2 < 4 || iteration_count_log2 > 31) { iteration_count_log2 = 8; } String output = "$H$"; output += itoa64.charAt(Math.min(iteration_count_log2 + 3, 30)); // PHP_VERSION >= 5 ? 5 : 3 output += _hash_encode64(input, 6); return output; } /** * Encode hash * * @param input String * @param count int * * @return String */ private String _hash_encode64(String input, int count) { StringBuilder output = new StringBuilder(); int i = 0; do { int value = input.charAt(i++); output.append(itoa64.charAt(value & 0x3f)); if (i < count) value |= input.charAt(i) << 8; output.append(itoa64.charAt((value >> 6) & 0x3f)); if (i++ >= count) break; if (i < count) value |= input.charAt(i) << 16; output.append(itoa64.charAt((value >> 12) & 0x3f)); if (i++ >= count) break; output.append(itoa64.charAt((value >> 18) & 0x3f)); } while (i < count); return output.toString(); } /** * Method _hash_crypt_private. * * @param password String * @param setting String * * @return String */ String _hash_crypt_private(String password, String setting) { String output = "*"; if (!setting.substring(0, 3).equals("$H$")) return output; int count_log2 = itoa64.indexOf(setting.charAt(3)); if (count_log2 < 7 || count_log2 > 30) return output; int count = 1 << count_log2; String salt = setting.substring(4, 12); if (salt.length() != 8) return output; String m1 = md5(salt + password); String hash = pack(m1); do { hash = pack(md5(hash + password)); } while (--count > 0); output = setting.substring(0, 12); output += _hash_encode64(hash, 16); return output; } /** * Method phpbb_check_hash. * * @param password String * @param hash String * * @return boolean */ public boolean phpbb_check_hash(String password, String hash) { if (hash.length() == 34) return _hash_crypt_private(password, hash).equals(hash); else return md5(password).equals(hash); } /** * Method getHash. * * @param password String * @param salt String * @param name String * * @return String * @throws NoSuchAlgorithmException * @see fr.xephi.authme.security.crypts.EncryptionMethod#getHash(String, String, String) */ @Override public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return phpbb_hash(password, salt); } /** * Method comparePassword. * * @param hash String * @param password String * @param playerName String * * @return boolean * @throws NoSuchAlgorithmException * @see fr.xephi.authme.security.crypts.EncryptionMethod#comparePassword(String, String, String) */ @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { return phpbb_check_hash(password, hash); } }