我正在尝试使用Java来散列Java中的String ripemd160来模拟以下php的输出:
$string = 'string';
$key = 'test';
hash_hmac('ripemd160', $string, $key);
// outputs: 37241f2513c60ae4d9b3b8d0d30517445f451fa5
Run Code Online (Sandbox Code Playgroud)
尝试1
最初我尝试使用以下方法模拟它...但是我不相信它可以ripemd160用作getInstance`算法吗?
或许它是,我只是没有在本地启用它?
public String signRequest(String uri, String secret) {
try {
byte[] keyBytes = secret.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
Mac mac = Mac.getInstance("ripemd160");
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(uri.getBytes());
// Convert raw bytes to Hex
byte[] hexBytes = new Hex().encode(rawHmac);
// Covert array of Hex bytes to a String
return new String(hexBytes, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Run Code Online (Sandbox Code Playgroud)
尝试2
这导致我寻找其他方法来实现上述目标,通过SO和Google我发现,看看BouncyCastle可能是一个更好的方法.
然后我发现这篇文章讨论了使用与我想要的算法相同的哈希以及BouncyCastle,它只是不使用密钥.(无法在Java中输出正确的哈希值.有什么问题?)
public static String toRIPEMD160(String in) {
try {
byte[] addr = in.getBytes();
byte[] out = new byte[20];
RIPEMD160Digest digest = new RIPEMD160Digest();
byte[] rawSha256 = sha256(addr);
String encodedSha256 = getHexString(rawSha256);
byte[] strBytes = base64Sha256.getBytes("UTF-8");
digest.update(strBytes, 0, strBytes.length);
digest.doFinal(out, 0);
return getHexString(out);
} catch (UnsupportedEncodingException ex) {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
我的工作正如预期的那样.
问题
您会注意到,在尝试2中,目前无法为散列提供密钥,我的问题是如何调整此功能以便能够提供密钥并完成我需要做的最后阶段能够模仿原始的php函数:hash_hmac('ripemd160', $string, $key);
使用Bouncy Castle的RIPEMD160很好,但你必须实现HMAC,而不仅仅是哈希数据.HMAC只是H(K XOR opad, H(K XOR ipad, text)),这里H是你的哈希函数,K是秘密,text消息并opad和ipad预定义的常量.为了演示它是如何工作的,我从Python的实现中翻译了以下内容:
public static String signRequest(String uri, String secret) throws Exception {
byte[] r = uri.getBytes("US-ASCII");
// The keys must have the same block size as your hashing algorithm, in this case
// 64 bytes right-padded with zeros.
byte[] k_outer = new byte[64];
System.arraycopy(secret.getBytes("US-ASCII"), 0, k_outer, 0,
secret.getBytes("US-ASCII").length);
byte[] k_inner = new byte[64];
System.arraycopy(secret.getBytes("US-ASCII"), 0, k_inner, 0,
secret.getBytes("US-ASCII").length);
// You'll create two nested hashes. The inner one is initialized with the
// key xor 0x36 (byte-wise), the other one with the key xor 0x5c.
for(int i=0; i<k_outer.length; i++)
k_outer[i] ^= 0x5c;
for(int i=0; i<k_inner.length; i++)
k_inner[i] ^= 0x36;
// Update inner hash with the key and data you want to sign
RIPEMD160Digest d_inner = new RIPEMD160Digest();
d_inner.update(k_inner, 0, k_inner.length);
d_inner.update(r, 0, r.length);
// Update outer hash with the key and the inner hash
RIPEMD160Digest d_outer = new RIPEMD160Digest();
d_outer.update(k_outer, 0, k_outer.length);
byte[] o_inner = new byte[d_inner.getDigestSize()];
d_inner.doFinal(o_inner, 0);
d_outer.update(o_inner, 0, o_inner.length);
// Finally, return the hex-encoded hash
byte[] o_outer = new byte[d_inner.getDigestSize()];
d_outer.doFinal(o_outer, 0);
return new String((new Hex()).encode(o_outer), "US-ASCII");
}
Run Code Online (Sandbox Code Playgroud)
Bouncy Castle在其HMac类中实现了此算法,因此该代码的较短变体是
public static String signRequest(String uri, String secret) throws Exception {
byte[] r = uri.getBytes("US-ASCII");
byte[] k = secret.getBytes("US-ASCII");
HMac hmac = new HMac(new RIPEMD160Digest());
hmac.init(new KeyParameter(k));
hmac.update(r, 0, r.length);
byte[] out = new byte[hmac.getMacSize()];
hmac.doFinal(out, 0);
return new String((new Hex()).encode(out), "US-ASCII");
}
Run Code Online (Sandbox Code Playgroud)