【问题标题】:Php and Java Sha1 methods gives different resultsPhp 和 Java Sha1 方法给出不同的结果
【发布时间】:2021-06-06 07:00:33
【问题描述】:

我正在尝试将 php 代码转换为 java 代码,但是在 sha1 ,我从 php 和 java 得到不同的字符串。例如,我为 php 编写了代码;

$password = 'pass123';
$hassedPasswordInDB = 'e1NTSEF9ZWxtNWlJcjJOVkVaenphZjA5dnlnUWFYdVlHaEU3dzQ=';
$sPasswordHash = base64_decode(substr(base64_decode($hassedPasswordInDB), 6));
$sSecretSalt = substr($sPasswordHash, 20); // $sSecretSalt is ��8 here
$sPasswordHash = substr($sPasswordHash, 0, 20);
$hashedPassword = sha1($password.$sSecretSalt); // $hashedPassword is 7a59b9888af6355119cf369fd3dbf2810697b981

我也为 java 写过代码;

    String password = "pass123";
    String hassedPasswordInDB = "e1NTSEF9ZWxtNWlJcjJOVkVaenphZjA5dnlnUWFYdVlHaEU3dzQ=";
    String subFirstDecode = new String(Base64Coder.decode(hassedPasswordInDB)).substring(6);
    String sPasswordHash = new String(Base64Coder.decode(subFirstDecode));
    String secretSalt = sPasswordHash.substring(19); // secretSalt is ��8
    sPasswordHash = sPasswordHash.substring(0, 19);
    String hashedPassword = sha1(password + secretSalt); //hashedPassword is b15fe1e7e0abce8284d3695af6c57d7540387ae4

还有Java sha1方法;

private static String sha1(String input) {
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
        digest.update(input.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < messageDigest.length; i++)
            hexString.append(String.format("%02X", 0xFF & messageDigest[i]));
        return hexString.toString().toLowerCase();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

因此,当我尝试使用像“abcabc”这样的 Utf-8 字符串时,它可以工作,但是具有复杂输入的相同代码,例如“��8”,会给出两个不同的输出。

Java : b15fe1e7e0abce8284d3695af6c57d7540387ae4,
PHP  : 7a59b9888af6355119cf369fd3dbf2810697b98

为什么会这样?

【问题讨论】:

  • 我对 Java 的了解不够,无法提供帮助,但我会进一步分解。您可以从任一端开始,但例如,忽略除 SHA-1 之外的所有内容并继续处理。使用诸如A????♠ 之类的复杂字符串来确保您没有编码问题。获取适用于 PHP 和 Java 的该字符串的 SHA-1 的工作示例。一旦你知道这是可行的,退后一步,处理你的字符串/编码的事情。对于 Java 代码,始终创建一个单一步骤(函数)的变量,并将结果与​​ PHP 版本进行比较,然后逐个函数重复。

标签: java php unicode utf-8 decode


【解决方案1】:

问题出在您的 Java 代码中:在 PHP 中,字符串只是 字节 序列(参见documentation),而在 Java 中,字符串是 字符 序列强>。 Java 中的字符是 UTF-16 码位。

byte[]String 之间的所有转换都使用编码,因此您应该尽可能避免此类转换,并在这样做时指定编码(使用 String(byte[], Charset) 构造函数或等效项)。否则,您最终将使用系统的默认编码(在您的情况下可能是 UTF-8)。

你的情况是:

  • sPasswordHash 字符串长度为 23,而原始字节数组为 24 字节:存在大量编码错误,因此许多字节序列被 replacement character 替换,
  • 为了补偿你调用sPassword.substring(19)(应该是20),它不包括前19个字符。最终得到的盐略有不同。

您应该更愿意在 byte 数组上工作:它不太舒服,但更安全。

      String password = "pass123";
      String hashedPasswordInDB = "e1NTSEF9ZWxtNWlJcjJOVkVaenphZjA5dnlnUWFYdVlHaEU3dzQ=";
      // This should be ASCII
      final String firstDecode = new String(Base64.getDecoder().decode(hashedPasswordInDB), StandardCharsets.US_ASCII);
      final byte[] hashAndSalt = Base64.getDecoder().decode(firstDecode.substring("{SSHA}".length()));
      final byte[] passwordHash = Arrays.copyOf(hashAndSalt, 20);
      final byte[] salt = Arrays.copyOfRange(hashAndSalt, 20, hashAndSalt.length);
      final byte[] passwordBytes = password.getBytes();
      final byte[] passwordAndSalt = Arrays.copyOf(passwordBytes, passwordBytes.length + salt.length);
      System.arraycopy(salt, 0, passwordAndSalt, passwordBytes.length, salt.length);
      System.out.println(sha1(passwordAndSalt));

(我稍微修改了sha1方法以接受字节数组而不是String

备注:从 Java 8 开始,JRE 中有一个 base 64 编码器/解码器(参见Base64)。

【讨论】:

  • 它有效。这是一个完美的解决方案。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 2011-03-13
相关资源
最近更新 更多