在PHP和Java之间进行散列 [英] Hashing between PHP and Java

查看:119
本文介绍了在PHP和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中创建/验证哈希的代码

 函数create_hash($ password)
{
//格式:algorithm:iterations:salt:hash
$ salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE,MCRYPT_DEV_URANDOM));
返回PBKDF2_HASH_ALGORITHM。 :。 PBKDF2_ITERATIONS。 :。 $盐。 :。
base64_encode(pbkdf2(
PBKDF2_HASH_ALGORITHM,
$密码,
$ salt,
PBKDF2_ITERATIONS,
PBKDF2_HASH_BYTE_SIZE,
true
) );


函数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],
$密码,
$ params [HASH_SALT_INDEX],
(int)$ params [HASH_ITERATION_INDEX],
strlen($ pbkdf2),
true

);
}

//比较两个字符串$ a和$ b的长度 - 常量时间。
函数slow_equals($ a,$ b)
{
$ diff = strlen($ a)^ strlen($ b); ($ i = 0; $ i< strlen($ a)&& $ i< strlen($ b); $ i ++)
{
$ diff | = ord($ a [$ i])^ ord($ b [$ i]);
}
return $ diff === 0;

$ b $函数pbkdf2($ algorithm,$ password,$ salt,$ count,$ key_length,$ raw_output = false)
{
$ algorithm = strtolower( $算法);
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);
$ b $ if(function_exists(hash_pbkdf2)){
//如果$ raw_output为false,则输出长度为NIBBLES(4位)!
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 =;
为($ i = 1; $ i <= $ block_count; $ i ++){
// $ i编码为4字节,大端。
$ last = $盐。包(N,$ i);
//第一次迭代
$ last = $ xorsum = hash_hmac($ algorithm,$ last,$ password,true); ($ j = 1; $ j< $ count; $ j ++){
$ xorsum ^ =($ last = hash_hmac(
)//执行另一个$ count - 1迭代
$ 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);

$ b $ public static boolean validatePassword(char [] password,String correctHash)
throws NoSuchAlgorithmException,InvalidKeySpecException {
//将散列解码为其参数
String [] params = correctHash.split(:);
int迭代= Integer.parseInt(params [ITERATION_INDEX]);
byte [] salt = Base64.decodeBase64(params [SALT_INDEX]);
byte [] hash = Base64.decodeBase64(params [PBKDF2_INDEX]);
//使用相同的salt,
//迭代计数和哈希长度
byte [] testHash = pbkdf2(password,salt,iterations,hash)计算提供的密码的哈希值。长度);
//在恒定时间比较哈希值。如果
//两个哈希匹配,则密码正确。
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&< b< b.length; i ++)
diff | = a [i] ^ b [i];
返回diff == 0;

$ b private static byte [] pbkdf2(char [] password,byte [] salt,int iterations,
int bytes)throws NoSuchAlgorithmException,InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(密码,salt,迭代,字节* 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
返回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));


解决方案

我知道这是一个真正的但我在找出自己的过程中发现它,发现除了字节转换为<$ c $之外,几乎所有东西都是正确的c> salt 和 hash



无论如何,对于你的问题, Base64.decodeBase64(params [SALT_INDEX]),只需使用 params [SALT_INDEX] .getBytes()。这应该返回适​​当的字节码用于哈希使用。



作为一个方面说明,我不知道你使用的库是什么, em>

com.sun.org.apache.xerces.internal.impl.dv.util.Base64

因此,我的方法实际上是 Base64.decode()



结果是这样的:



pre $ public static boolean validatePassword(String password,String correctHash)throws NoSuchAlgorithmException,InvalidKeySpecException {
返回validatePassword(password.toCharArray(),correctHash);

$ b $ public static boolean validatePassword(char [] password,String correctHash)
throws NoSuchAlgorithmException,InvalidKeySpecException {
//将散列解码为其参数
String [] params = correctHash.split(:);
int迭代= Integer.parseInt(params [ITERATION_INDEX]);

//这两行被更改
byte [] salt = params [SALT_INDEX] .getBytes();
byte [] hash = params [PBKDF2_INDEX] .getBytes();

//使用相同的salt,
//迭代计数和哈希长度
byte [] testHash = pbkdf2计算提供的密码的哈希值(password,salt,迭代,hash.length);
//在恒定时间比较哈希值。如果
//两个哈希匹配,则密码正确。
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&< b< b.length; i ++)
diff | = a [i] ^ b [i];
返回diff == 0;

$ b private static byte [] pbkdf2(char [] password,byte [] salt,int iterations,
int bytes)throws NoSuchAlgorithmException,InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(密码,salt,迭代,字节* 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
返回skf.generateSecret(spec).getEncoded();
}

我测试了这个方法,可以用 slowEquals 函数和字符串比较:
$ b

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



我用我自己的salt和hash生成了 PHP 的加密并作为字符串到我自己的代码中,但是我使用了所有的 PHP 代码作为我的脚本的基础,甚至使用了 pbkdf2 按原样运行。


I'm trying to create a hash in PHP store it in a database then verify the hash in Java. So far they both work fine independently from each other ... Java can hash and verify java, Php can hash and verify php, but they aren't playing nicely together despite my best efforts.

  1. I've changed the algorithm to sha1 in php to match javas PBKDF2WithHmacSHA1 - Do they match?
  2. I've used Base64.decodeBase64() from the apache commons codec library to decode the php base64_encode() function - is this ok?
  3. I'm manually removing the algorithm "sha1:" part for java for now.

Here's the code, can you spot any reasons why the java implementation wouldn't be able to verify the hash produced by the php version? - I'm not getting error, it's just the verification is failing when the correct password is "password" for that hash.

Part 1 ... I define all the variables for PHP, the only different variable is the algorithm in use

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);

Part 2 ... The code to create / validate hashes in 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));
}

This is the java verification code:

Part 3 ... Setup the variables in 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;

Part 4 ... Setup the verification part for 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();
}

Step 5 ... Call it

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

解决方案

I know this is a really old question, but I found it in the process of figuring it out myself, and found out that you had pretty much everything right except for the bytes conversion for the salt and hash.

Anyway, for your problem, instead of using Base64.decodeBase64(params[SALT_INDEX]), just use params[SALT_INDEX].getBytes(). This should return the proper bytecode for hash usage.

As a side note, I don't know what library you were using, I ended up with
com.sun.org.apache.xerces.internal.impl.dv.util.Base64
and thus my method was actually Base64.decode()

So, part 4 ends up being like this:

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();
}

I tested this working with both your slowEquals function and with String comparison:

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

I used my own salt and hash generated with PHP's crypto and transferred as strings into my own code, but I used all of your PHP code as the base of my script, and even used your pbkdf2 function as-is.

这篇关于在PHP和Java之间进行散列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆