2012-07-31 46 views
2

我尝试模仿Drupal 7在Java中检查正确密码的方式。 在这里找到一些代码作为指导:https://github.com/CraftFire/AuthDB-Legacy/blob/master/src/main/java/com/authdb/scripts/cms/Drupal.java并提取我需要的代码。Drupal密码的Java验证

但是,当我给密码和散列版本(为了提取所需的盐量和迭代量),我得到了不同的结果。

使用Drupal的密码哈希脚本 导致生成的密码:

Expected value = $S$DxVn7wubSRzoK9X2pkGx4njeDRkLEgdqPphc2ZXkkb8Viy8JEGf3 
Calculated value = $S$DxVn7wubSpQ1CpUnBZZHNqIXMp2XMVZHMYBqAs24NsUHMY7HBkYn 

Expected value = $S$DOASeKfBzZoqgSRl/mBnK06GlLESyMHZ81jyUueEBiCrkkxxArpR 
Calculated value = $S$DOASeKfBzs.XMVZ1NkYXNmIqMpEHAoEaMYJ1NmUHCZJaBZFnAZFX 

任何人能帮助我/告诉我什么,我做错了什么? 谢谢。

代码:

import java.io.UnsupportedEncodingException; 
import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 

class test { 

    public static void main(String args[]) { 
    // Passwords and hashes generated by Drupal. 
    checkPassword("test" , "$S$DxVn7wubSRzoK9X2pkGx4njeDRkLEgdqPphc2ZXkkb8Viy8JEGf3"); 
    checkPassword("barbaz", "$S$DOASeKfBzZoqgSRl/mBnK06GlLESyMHZ81jyUueEBiCrkkxxArpR"); 
    } 

    private static String itoa64 = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 
    private static final int DRUPAL_HASH_LENGTH = 55; 
    private static int password_get_count_log2(String setting) { return itoa64.indexOf(setting.charAt(3)); } 

    /** 
    * Note: taken from the default Drupal 7 password algorithm 
    * @param candidate 
    *  the clear text password 
    * @param saltedEncryptedPassword 
    *  the salted encrypted password string to check => NEEDS TO BE THE DEFAULT DRUPAL 7 PASSWORD HASH. 
    * @return true if the candidate matches, false otherwise. 
    */ 
    public static boolean checkPassword(String candidate, String saltedEncryptedPassword) { 
    if (candidate == null) { 
     return false; 
    } 
    if (saltedEncryptedPassword == null) { 
     return false; 
    } 

    String hash = password_crypt(candidate, saltedEncryptedPassword); 
    System.out.println("Tested value = " + saltedEncryptedPassword); 
    System.out.println("Calced value = " + hash); 

    return hash == saltedEncryptedPassword; 
    } 

    public static String SHA512(String text) { 
    byte[] sha1hash = new byte[40]; 
    try { 
     MessageDigest md = MessageDigest.getInstance("SHA-512"); 
     md.update(text.getBytes("UTF-8"), 0, text.length()); 
     sha1hash = md.digest(); 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
     e.printStackTrace(); 
    } 
    return convertToHex(sha1hash); 
    } 

    private static String convertToHex(byte[] data) { 
    StringBuffer buf = new StringBuffer(); 
    for (int i = 0; i < data.length; i++) { 
     int halfbyte = (data[i] >>> 4) & 0x0F; 
     int two_halfs = 0; 
     do { 
      if ((0 <= halfbyte) && (halfbyte <= 9)) 
      buf.append((char) ('0' + halfbyte)); 
      else 
      buf.append((char) ('a' + (halfbyte - 10))); 
      halfbyte = data[i] & 0x0F; 
     } 
     while(two_halfs++ < 1); 
    } 
    return buf.toString(); 
    } 

    private static String password_crypt(String password, String setting) { 
    // The first 12 characters of an existing hash are its setting string. 
    setting = setting.substring(0, 12); 
    int count_log2 = password_get_count_log2(setting); 

    String salt = setting.substring(4, 12); 
    // Hashes must have an 8 character salt. 
    if (salt.length() != 8) { 
     return null; 
    } 

    // Convert the base 2 logarithm into an integer. 
    int count = 1 << count_log2; 

    String hash; 
    try { 
     hash = SHA512(salt + password); 
     do { 
     hash = SHA512(hash + password); 
     } while (--count >= 0); 
    } catch(Exception e) { 
     return null; 
    } 

    int len = hash.length(); 
    String output = setting + password_base64_encode(hash, len);   
    return (output.length() > 0) ? output.substring(0, DRUPAL_HASH_LENGTH) : null; 
    } 

    private static String password_base64_encode(String input, int count) { 
    StringBuffer output = new StringBuffer(); 
    int i = 0, value; 
    do { 
     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(); 
    } 

} 

- PS。 有一件事我已经看到的是以下内容: 考虑以下功能:

public String convertToHex(byte[] data) { 
    StringBuffer buf = new StringBuffer(); 
    for (int i = 0; i < data.length; i++) { 
     int halfbyte = (data[i] >>> 4) & 0x0F; 
     int two_halfs = 0; 
     do { 
      if ((0 <= halfbyte) && (halfbyte <= 9)) 
      buf.append((char) ('0' + halfbyte)); 
      else 
      buf.append((char) ('a' + (halfbyte - 10))); 
      halfbyte = data[i] & 0x0F; 
     } 
     while(two_halfs++ < 1); 
    } 
    return buf.toString(); 
    } 

public String convertToHex(byte [] raw) { 
     StringBuilder hex = new StringBuilder(2 * raw.length); 
     for (final byte b : raw) { 
     int hiVal = (b & 0xF0) >> 4; 
     int loVal = b & 0x0F; 
     hex.append((char) ('0' + (hiVal + (hiVal/10 * 7)))); 
     hex.append((char) ('0' + (loVal + (loVal/10 * 7)))); 
     } 
     return hex.toString(); 
    } 

第一个函数返回字符串为小写,二是返回更高的情况下字符串。不知道使用哪一个,最终都返回不同的结果,但都不令人满意。

CNC中

几乎有??? ...

来到了一步,改变有点... 在Drupal是使用了以下功能的问题:

$hash = hash($algo, $salt . $password, TRUE); 

返回

'���Y�emb 
ӈ3����4��q����h�osab��V�!IS�uC�*[� 

正如你看到的,我们不需要十六进制版本,因为你会得到一个总不同的哈希回来...... 所以我改变了代码Java中:

public byte[] SHA512(String text) { 
    byte[] sha1hash = new byte[50]; 
    try { 
     MessageDigest md = MessageDigest.getInstance("SHA-512"); 
     md.update(text.getBytes("UTF-8"), 0, text.length()); 
     sha1hash = md.digest(); 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
     e.printStackTrace(); 
    } 
    return sha1hash; 
    } 

--snip-- 
    hash = new String(SHA512(salt + password)); 
    System.out.println(hash); 

返回:

'���Y�emb 
ӈ3����4��q���h�osab��V�!IS�uC�*[� 

正如你看到的,多数民众赞成几乎相同....

php: ӈ3����4��q����h�osab��V�!IS�uC�*[� 
java: ӈ3����4��q���h�osab��V�!IS�uC�*[� 

任何人都有线索如何解决最后一部分? 表单新字符串(SHA512(盐+密码,'Whatevercodec'));没有帮助我... 谢谢!

回答

6

我建议你做这样的事情:

import java.security.NoSuchAlgorithmException; 

public class hash { 

private static final int DRUPAL_HASH_LENGTH = 55; 

private static String _password_itoa64() { 
    return "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 
} 

public static void main(String args[]) throws Exception { 
    // Passwords and hashes generated by Drupal. 
    checkPassword("adrian", "$S$DNbBTrkalsPChLsqajHUQS18pBBxzSTQW0310SzivTy7HDQ.zgyG"); 
    checkPassword("test" , "$S$DxVn7wubSRzoK9X2pkGx4njeDRkLEgdqPphc2ZXkkb8Viy8JEGf3"); 
    checkPassword("barbaz", "$S$DOASeKfBzZoqgSRl/mBnK06GlLESyMHZ81jyUueEBiCrkkxxArpR"); 
} 


private static int password_get_count_log2(String setting) { 
    return _password_itoa64().indexOf(setting.charAt(3)); 
} 


private static byte[] sha512(String input) { 
    try { 
     return java.security.MessageDigest.getInstance("SHA-512").digest(input.getBytes()); 
    } catch (NoSuchAlgorithmException ex) { 
     ex.printStackTrace(); 
    } 
    return new byte[0]; 
} 

private static byte[] sha512(byte[] input) { 
    try { 
     return java.security.MessageDigest.getInstance("SHA-512").digest(input); 
    } catch (NoSuchAlgorithmException ex) { 
     ex.printStackTrace(); 
    } 
    return new byte[0]; 
} 

/** 
* Note: taken from the default Drupal 7 password algorithm 
* 
* @param candidate    the clear text password 
* @param saltedEncryptedPassword the salted encrypted password string to check => NEEDS TO BE THE DEFAULT DRUPAL 7 PASSWORD HASH. 
* @return true if the candidate matches, false otherwise. 
*/ 
public static boolean checkPassword(String candidate, String saltedEncryptedPassword) throws Exception { 
    if (candidate == null || saltedEncryptedPassword == null) { 
     return false; 
    } 

    String hash = password_crypt(candidate, saltedEncryptedPassword); 
    System.out.println("Expected value = " + saltedEncryptedPassword); 
    System.out.println("Calced value = " + hash); 
    System.out.println("Result Good? = " + saltedEncryptedPassword.equalsIgnoreCase(hash)); 


    return saltedEncryptedPassword.equalsIgnoreCase(hash); 
} 


private static String password_crypt(String password, String passwordHash) throws Exception { 
    // The first 12 characters of an existing hash are its setting string. 
    passwordHash = passwordHash.substring(0, 12); 
    int count_log2 = password_get_count_log2(passwordHash); 
    String salt = passwordHash.substring(4, 12); 
    // Hashes must have an 8 character salt. 
    if (salt.length() != 8) { 
     return null; 
    } 

    int count = 1 << count_log2; 


    byte[] hash; 
    try { 
     hash = sha512(salt.concat(password)); 

     do { 
      hash = sha512(joinBytes(hash, password.getBytes("UTF-8"))); 
     } while (--count > 0); 
    } catch (Exception e) { 
     System.out.println("error " + e.toString()); 
     return null; 
    } 

    String output = passwordHash + _password_base64_encode(hash, hash.length); 
    return (output.length() > 0) ? output.substring(0, DRUPAL_HASH_LENGTH) : null; 
} 

private static byte[] joinBytes(byte[] a, byte[] b) { 
    byte[] combined = new byte[a.length + b.length]; 

    System.arraycopy(a, 0, combined, 0, a.length); 
    System.arraycopy(b, 0, combined, a.length, b.length); 
    return combined; 
} 



private static String _password_base64_encode(byte[] input, int count) throws Exception { 

    StringBuffer output = new StringBuffer(); 
    int i = 0; 
    CharSequence itoa64 = _password_itoa64(); 
    do { 
     long value = SignedByteToUnsignedLong(input[i++]); 

     output.append(itoa64.charAt((int) value & 0x3f)); 
     if (i < count) { 
      value |= SignedByteToUnsignedLong(input[i]) << 8; 
     } 
     output.append(itoa64.charAt((int) (value >> 6) & 0x3f)); 
     if (i++ >= count) { 
      break; 
     } 
     if (i < count) { 
      value |= SignedByteToUnsignedLong(input[i]) << 16; 
     } 

     output.append(itoa64.charAt((int) (value >> 12) & 0x3f)); 
     if (i++ >= count) { 
      break; 
     } 
     output.append(itoa64.charAt((int) (value >> 18) & 0x3f)); 
    } while (i < count); 

    return output.toString(); 
} 


public static long SignedByteToUnsignedLong(byte b) { 
    return b & 0xFF; 
} 

} 
+0

这奇妙的作品!谢谢! – 2013-06-25 10:02:03

+0

完美!谢谢! – mykola 2016-04-14 21:41:29

0

谢谢阿德里安的出色代码。你为我节省了很多时间!我只是想发布一个不压缩异常的后续操作,删除main()和控制台输出以及正确命名的Java函数。单元测试也是如此。

import java.io.UnsupportedEncodingException; 
import java.security.NoSuchAlgorithmException; 

public class PhpassHashedPassword { 

    private static final int DRUPAL_HASH_LENGTH = 55; 

    private static final String ITOA_64 = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 

    private static int passwordGetCount(String setting) { 
     return ITOA_64.indexOf(setting.charAt(3)); 
    } 

    private static byte[] sha512(byte[] input) throws NoSuchAlgorithmException { 
     return java.security.MessageDigest.getInstance("SHA-512").digest(input); 
    } 

    /** 
    * Note: taken from the default Drupal 7 password algorithm 
    * 
    * @param candidate the clear text password 
    * @param saltedEncryptedPassword the salted encrypted password string to 
    * check => NEEDS TO BE THE DEFAULT DRUPAL 7 PASSWORD HASH. 
    * @return true if the candidate matches, false otherwise. 
    * @throws java.security.NoSuchAlgorithmException 
    * @throws java.io.UnsupportedEncodingException 
    */ 
    public static boolean validatePasswordHash(String candidate, String saltedEncryptedPassword) throws NoSuchAlgorithmException, UnsupportedEncodingException { 
     if (candidate == null || saltedEncryptedPassword == null) { 
      return false; 
     } 

     String hash = password_crypt(candidate, saltedEncryptedPassword); 
     return saltedEncryptedPassword.equalsIgnoreCase(hash); 
    } 

    private static String password_crypt(String password, String passwordHash) throws NoSuchAlgorithmException, UnsupportedEncodingException { 
     // The first 12 characters of an existing hash are its setting string. 
     passwordHash = passwordHash.substring(0, 12); 
     int count_log2 = passwordGetCount(passwordHash); 
     String salt = passwordHash.substring(4, 12); 
     // Hashes must have an 8 character salt. 
     if (salt.length() != 8) { 
      return null; 
     } 

     int count = 1 << count_log2; 

     byte[] hash = sha512(salt.concat(password).getBytes()); 

     do { 
      hash = sha512(joinBytes(hash, password.getBytes("UTF-8"))); 
     } while (--count > 0); 

     String output = passwordHash + passwordBase64Encode(hash, hash.length); 
     return (output.length() > 0) ? output.substring(0, DRUPAL_HASH_LENGTH) : null; 
    } 

    private static byte[] joinBytes(byte[] a, byte[] b) { 
     byte[] combined = new byte[a.length + b.length]; 

     System.arraycopy(a, 0, combined, 0, a.length); 
     System.arraycopy(b, 0, combined, a.length, b.length); 
     return combined; 
    } 

    private static String passwordBase64Encode(byte[] input, int count) { 

     StringBuilder output = new StringBuilder(); 
     int i = 0; 
     do { 
      long value = signedByteToUnsignedLong(input[i++]); 

      output.append(ITOA_64.charAt((int) value & 0x3f)); 
      if (i < count) { 
       value |= signedByteToUnsignedLong(input[i]) << 8; 
      } 
      output.append(ITOA_64.charAt((int) (value >> 6) & 0x3f)); 
      if (i++ >= count) { 
       break; 
      } 
      if (i < count) { 
       value |= signedByteToUnsignedLong(input[i]) << 16; 
      } 

      output.append(ITOA_64.charAt((int) (value >> 12) & 0x3f)); 
      if (i++ >= count) { 
       break; 
      } 
      output.append(ITOA_64.charAt((int) (value >> 18) & 0x3f)); 
     } while (i < count); 

     return output.toString(); 
    } 

    private static long signedByteToUnsignedLong(byte b) { 
     return b & 0xFF; 
    } 

} 

这是一个单元测试:

public class PhpassHashedPasswordTest { 

    public PhpassHashedPasswordTest() { 
    } 

    @BeforeClass 
    public static void setUpClass() { 
    } 

    @AfterClass 
    public static void tearDownClass() { 
    } 

    @Before 
    public void setUp() { 
    } 

    @After 
    public void tearDown() { 
    } 

    @org.junit.Test 
    public void testGoodPassword() throws NoSuchAlgorithmException, UnsupportedEncodingException { 
     if(!PhpassHashedPassword.validatePasswordHash("Y0dog!", "$S$EU44gErBh91wY1I8eEfEyymUNfFqh8OXLYeAdm.1xVnjgkN9KauF")) { 
      fail("expected password to match"); 
     } 
    } 

    @org.junit.Test 
    public void testMismatchPassword() throws NoSuchAlgorithmException, UnsupportedEncodingException { 
     if(PhpassHashedPassword.validatePasswordHash("THEWRONGPASSWORD", "$S$EU44gErBh91wY1I8eEfEyymUNfFqh8OXLYeAdm.1xVnjgkN9KauF")) { 
      fail("expected password match to fail"); 
     } 
    } 


}