簡體   English   中英

在PHP和Java之間散列

[英]Hashing between PHP and Java

我試圖在PHP中創建一個哈希,將其存儲在數據庫中,然后在Java中驗證哈希。 到目前為止,它們彼此獨立運行良好... Java可以哈希並驗證Java,Php可以哈希並驗證php,但是盡管我盡了最大努力,但它們並不能很好地協作。

  1. 我已將算法更改為php中的sha1以匹配javas PBKDF2WithHmacSHA1它們匹配嗎?
  2. 我已經使用了Apache Commons編解碼器庫中的Base64.decodeBase64()來解碼php base64_encode()函數-可以嗎?
  3. 我現在正在手動刪除Java的算法“ sha1:”部分。

這是代碼,您能找到導致Java實現無法驗證php版本產生的哈希值的任何原因嗎? -我沒有收到錯誤,只是當正確的密碼是該哈希的“密碼”時,驗證失敗。

第1部分...我定義了PHP的所有變量,唯一不同的變量是使用的算法

define("PBKDF2_HASH_ALGORITHM", "sha1");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

第2部分...在php中創建/驗證哈希的代碼

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .
    base64_encode(pbkdf2(
        PBKDF2_HASH_ALGORITHM,
        $password,
        $salt,
        PBKDF2_ITERATIONS,
        PBKDF2_HASH_BYTE_SIZE,
        true
    ));
}

function validate_password($password, $correct_hash)
{
$params = explode(":", $correct_hash);
if(count($params) < HASH_SECTIONS)
   return false;
$pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
return slow_equals(
    $pbkdf2,
    pbkdf2(
        $params[HASH_ALGORITHM_INDEX],
        $password,
        $params[HASH_SALT_INDEX],
        (int)$params[HASH_ITERATION_INDEX],
        strlen($pbkdf2),
        true
    )
);
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
    $diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}

function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
    trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if($count <= 0 || $key_length <= 0)
    trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);

if (function_exists("hash_pbkdf2")) {
    // The output length is in NIBBLES (4-bits) if $raw_output is false!
    if (!$raw_output) {
        $key_length = $key_length * 2;
    }
    return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}

$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);

$output = "";
for($i = 1; $i <= $block_count; $i++) {
    // $i encoded as 4 bytes, big endian.
    $last = $salt . pack("N", $i);
    // first iteration
    $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
    // perform the other $count - 1 iterations
    for ($j = 1; $j < $count; $j++) {
        $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
    }
    $output .= $xorsum;
}

if($raw_output)
    return substr($output, 0, $key_length);
else
    return bin2hex(substr($output, 0, $key_length));
}

這是java驗證代碼:

第3部分...在Java中設置變量

public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
public static final int SALT_BYTE_SIZE = 24;
public static final int HASH_BYTE_SIZE = 24;
public static final int PBKDF2_ITERATIONS = 1000;

public static final int ITERATION_INDEX = 0;
public static final int SALT_INDEX = 1;
public static final int PBKDF2_INDEX = 2;

第4部分...設置Java的驗證部分

public static boolean validatePassword(String password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException {
return validatePassword(password.toCharArray(), correctHash);
}

public static boolean validatePassword(char[] password, String correctHash)
    throws NoSuchAlgorithmException, InvalidKeySpecException {
// Decode the hash into its parameters
String[] params = correctHash.split(":");
int iterations = Integer.parseInt(params[ITERATION_INDEX]);
byte[] salt = Base64.decodeBase64(params[SALT_INDEX]);
byte[] hash = Base64.decodeBase64(params[PBKDF2_INDEX]);
// Compute the hash of the provided password, using the same salt,
// iteration count, and hash length
byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
// Compare the hashes in constant time. The password is correct if
// both hashes match.
return slowEquals(hash, testHash);
}

private static boolean slowEquals(byte[] a, byte[] b) {
int diff = a.length ^ b.length;
for (int i = 0; i < a.length && i < b.length; i++)
    diff |= a[i] ^ b[i];
return diff == 0;
}

private static byte[] pbkdf2(char[] password, byte[] salt, int iterations,
    int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
return skf.generateSecret(spec).getEncoded();
}

步驟5 ...調用它

public static void main(String[] args) throws NoSuchAlgorithmException,
    InvalidKeySpecException {
System.out.println(validatePassword("password", "1000:PoTTC/xEqAgH9A4vCnagBPioC71cPm+C:bLBiDjW8+VukY9PnRTOrMy/JDSfPEW8Y"));
}

我知道這是一個確實很老的問題,但是我是在自己弄清楚這個問題的過程中發現的,發現除了salthashbytes轉換之外,您幾乎沒有其他問題。

無論如何,對於您的問題,不要使用Base64.decodeBase64(params[SALT_INDEX]) ,而要使用params[SALT_INDEX].getBytes() 這應該返回正確的字節碼以供哈希使用。

附帶說明,我不知道您使用的是什么庫,最后我得到了
com.sun.org.apache.xerces.internal.impl.dv.util.Base64
因此我的方法實際上是Base64.decode()

因此,第4部分最終如下所示:

public static boolean validatePassword(String password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException {
return validatePassword(password.toCharArray(), correctHash);
}

public static boolean validatePassword(char[] password, String correctHash)
    throws NoSuchAlgorithmException, InvalidKeySpecException {
// Decode the hash into its parameters
String[] params = correctHash.split(":");
int iterations = Integer.parseInt(params[ITERATION_INDEX]);

// these two lines were changed
byte[] salt = params[SALT_INDEX].getBytes();
byte[] hash = params[PBKDF2_INDEX].getBytes();

// Compute the hash of the provided password, using the same salt,
// iteration count, and hash length
byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
// Compare the hashes in constant time. The password is correct if
// both hashes match.
return slowEquals(hash, testHash);
}

private static boolean slowEquals(byte[] a, byte[] b) {
int diff = a.length ^ b.length;
for (int i = 0; i < a.length && i < b.length; i++)
    diff |= a[i] ^ b[i];
return diff == 0;
}

private static byte[] pbkdf2(char[] password, byte[] salt, int iterations,
    int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
return skf.generateSecret(spec).getEncoded();
}

我使用您的slowEquals函數和String比較測試了此功能:

if(Base64.encode(testHash).equals(params[PBKDF2_INDEX]))

我使用了自己的用PHP的crypto生成的鹽和哈希值,並作為字符串轉移到了自己的代碼中,但是我使用了所有PHP代碼作為腳本的基礎,甚至直接使用了pbkdf2函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM