【问题标题】:Converting a hash to Base64 encoding: Should I be storing the hash as a String or Byte before encoding?将哈希转换为 Base64 编码:我应该在编码之前将哈希存储为字符串还是字节?
【发布时间】:2020-10-16 04:00:01
【问题描述】:

在生成 HMAC-SHA1 哈希时遇到问题。我将其转换为 Base64 并将其发送到外部系统,由其验证结果。但是,他们正在生成不同的 Base64 编码哈希。据我所知,他们使用 Java Mac (javax.crypto.mac) 来生成哈希,而我使用的是 Google Guava Codec API。

我检查了几个在线哈希生成器(即https://www.freeformatter.com/hmac-generator.html),它们生成的哈希值与我相同。我尝试了 Apache Commons Codec API,它也产生了相同的结果。然而,这个外部系统正在生成不同的 Base64 编码哈希。密钥/密钥和消息完全相同。

事实证明,首先将哈希转换为字符串而不是字节是问题所在。我首先将哈希转换为字符串,然后使用 Base64 进行编码。外部系统首先将散列转换为字节,然后使用 Base64 进行编码。我发现的在线生成器似乎首先将哈希转换为字符串,就像我一样,这就是我的哈希与在线哈希生成器匹配的原因。

有没有合适的方法来转换哈希?对于良好的实践,我应该使用 asBytes()、toString() 还是其他什么?

这是使用 Guava Codec API 的代码:

import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

import java.util.Base64;

public class HmacSha1TokenAuth {

 public static void main(String[] args) {

  String secret = "12345";
  String valueToDigest = "TestUser";

  byte[] key = secret.getBytes();

  Hasher hasher = Hashing.hmacSha1(key).newHasher();
  Hasher hasher2 = Hashing.hmacSha1(key).newHasher();

  hasher.putBytes(valueToDigest.getBytes());
  hasher2.putBytes(valueToDigest.getBytes());

  // This is what I used and also found a few online hash generators using the same approach
  String hashAsString = hasher.hash().toString();

  // This is what the external system used
  byte[] hashAsByte = hasher2.hash().asBytes();

  String base64AsString = Base64.getEncoder().encodeToString(hashAsString.getBytes());
  String base64AsByte = Base64.getEncoder().encodeToString(hashAsByte);

  System.out.println("Hash As String: " + hashAsString);
  System.out.println("Hash As String(Converted to Bytes): " + hashAsString.getBytes());  
  System.out.println("Base64 Value of Hash As String: " + base64AsString);
  System.out.println("Hash As Byte: " + hashAsByte);
  System.out.println("Base64 Value of Hash As Byte: " + base64AsByte);

  /*** My Results ***/
  // Hash As String: c8094bb1e0896a3f813036bdaeb37b753d9f4f5b
  // Hash As String(Converted to Bytes): [B@61443d8f
  // Base64 Value of Hash As String: YzgwOTRiYjFlMDg5NmEzZjgxMzAzNmJkYWViMzdiNzUzZDlmNGY1Yg==
  
  /*** External System Results ***/
  // Hash As Byte: [B@445b84c0
  // Base64 Value of Hash As Byte: yAlLseCJaj+BMDa9rrN7dT2fT1s=

 }

}

【问题讨论】:

    标签: java hash base64 guava apache-commons


    【解决方案1】:

    Base64 编码字节,哈希器产生字节,因此没有理由转换为字符串。

    我想你看到的例子是关于从字符串变量开始转换 base64 的。

    不同的结果是由于toString() 或getBytes() 转换的不同编码。

    从 guava 文档中您可以看到 hashcode.toString() 转换为十六进制,而 bytes.toString() 是平台相关的(应该是 UTF8)?

    https://guava.dev/releases/16.0/api/docs/com/google/common/hash/HashCode.html#toString() https://guava.dev/releases/16.0/api/docs/com/google/common/hash/HashCode.html#fromString(java.lang.String)

    【讨论】:

    • 感谢您的帮助!嗯,所以我猜在线哈希生成器应该只用于检查哈希本身是否正确,而不是作为 Base64 编码的起点。 toString(),iirc,使用 UTF-16。尝试将该值编码为 Base64 可能不是这些在线哈希检查器的预期用途。对于 Base64,我认为我应该始终先将哈希转换为字节,然后再进行 Base64 编码。我的误解可能来自如何利用这些在线哈希检查器,我错误地将其用作对哈希进行 Base64 编码的起点。再次感谢您的帮助。
    • @user3621633:您似乎对 Java 中的字节、字符和字符串存在根本性的困惑。 toString() 不“使用”utf-16,它产生一个字符串。 UTF-16 是一种将字符串编码/解码为字节数组的方法,反之亦然。 Guava 的散列类可以给你一个字节数组或十六进制字符串的结果。十六进制字符串主要用于人类观察。 正确的格式是什么?这取决于你想对结果做什么。
    • 对不起:我的意思是字符串在 Java iirc 中内部表示为 UTF-16。也感谢您对编码的进一步说明。
    猜你喜欢
    • 1970-01-01
    • 2016-10-21
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 2015-08-21
    • 1970-01-01
    相关资源
    最近更新 更多