英文:
hmac.New(h func() hash.Hash, key []byte) hash.Hash equivalent in javascript
问题
我几乎陷入了在Go语言中实现hmac.New的JavaScript代码中的困境,已经几天了,但是没有成功。我使用了crypto
、crypto-js
和stablelib
模块进行实现。问题在于,在Go语言版本中,可以通过hmac
实例创建hmac
实例。例如(代码块是正确的并且经过测试):
hmacf := hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(sha256.New, []byte(SALT))
}, []byte(path[0]))
}, []byte(path[1]))
}, []byte(path[2]))
实际上,我不知道它是如何工作的!因为在所有与JavaScript相关的模块中,你不能从hmac
创建hmac
,它们接受一个字符串值来确定哈希算法。
也许最好询问如何在JavaScript中从hmac
创建hmac
。
有什么解决办法吗?
当Go版本的输出与你的实现输出相同时,你的解决方案是正确的。
英文:
Almost I got stuck in the js implementation of go lang hmac.New for several days. However, no success. I used crypto
,crypto-js
and stablelib
modules for implementation. The problem is that in go lang version, hmac
instance can be created by hmac
instance. For example (the code block is correct and tested):
hmacf := hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(sha256.New, []byte(SALT))
}, []byte(path[0]))
}, []byte(path[1]))
}, []byte(path[2]))
Actually, I don't know how does it work! because in all javascript related modules, You can't create hmac
from hmac
, and they accept an string
value that determines hashing algorithm.
maybe it's better to ask how to create hmac
from hmac
in javascript.
What is the solution?
When the output of go version is the same with the output of your implementation; Your solution is correct.
答案1
得分: 2
根据规范(RFC 2104),HMAC在内部使用摘要函数,例如SHA256。
然而,你的实现应用了(实际上是不符合规范的)在内部使用另一个HMAC而不是摘要的HMAC,只有最低级别的HMAC在内部使用常规摘要。这样就创建了一个嵌套结构。
基于常规HMAC的规范(带有摘要),可以将其扩展为在Go代码中使用的带有HMAC(而不是摘要)的HMAC:
HMAC(K XOR opad, HMAC(K XOR ipad, text))
参见RFC2104,第2节HMAC的定义
由于与规范的差异,可能不太容易找到一个JavaScript库,可以直接支持这样的功能。尽管大多数库当然支持HMAC,但只允许指定摘要(而不是HMAC),例如NodeJS的crypto模块的crypto.createHmac()
,也可以参考其他答案。我认为这种方法不能用于实现Go代码中的逻辑。
如果其他答案的方法不起作用,而且你找不到另一个具有所需功能的JavaScript库,你可以自己在JavaScript中实现这个逻辑,因为HMAC的规范相对简单(如上所述)。
以下代码是使用NodeJS的crypto模块的示例实现:
var crypto = require('crypto')
const digest = 'sha256'
const blockSize = 64 // 摘要的块大小
// 定义输入参数
var salt = Buffer.from('salt')
var path = [ Buffer.from('alfa'), Buffer.from('beta'), Buffer.from('gamma') ]
var data = Buffer.from('data')
// 计算HMAC
var hmac = hmac(data, salt, path)
console.log(hmac.toString('hex'))
function hmac(data, salt, path) {
// 创建keyList
var keyList = []
keyList.push(salt)
keyList = keyList.concat(path)
// 递归确定HMAC
var result = hmac_rec(data, keyList)
return result
}
function hmac_rec(data, keyList) {
// 调整密钥(根据HMAC规范)
var key = keyList.pop()
if (key.length > blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
if (keyList.length > 0) {
hmac_rec(key, [...keyList]).copy(k)
} else {
getHash(key).copy(k)
}
} else if (key.length < blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
key.copy(k)
} else {
k = key
}
// 创建'key xor ipad'和'key xor opad'(根据HMAC规范)
var ik = Buffer.allocUnsafe(blockSize)
var ok = Buffer.allocUnsafe(blockSize)
k.copy(ik)
k.copy(ok)
for (var i = 0; i < ik.length; i++) {
ik[i] = 0x36 ^ ik[i]
ok[i] = 0x5c ^ ok[i]
}
// 计算HMAC
if (keyList.length > 0) {
var innerHMac = hmac_rec(Buffer.concat([ ik, data ]), [...keyList])
var outerHMac = hmac_rec(Buffer.concat([ ok, innerHMac ]), [...keyList])
} else {
var innerHMac = getHash(Buffer.concat([ik, data]))
var outerHMac = getHash(Buffer.concat([ok, innerHMac]))
}
return outerHMac
}
// 计算SHA256哈希
function getHash(data){
var hash = crypto.createHash(digest);
hash.update(data)
return hash.digest()
}
结果为:
2e631dcb4289f8256861a833ed985fa945cd714ebe7c3bd4ed4b4072b107b073
测试:
以下Go代码产生相同的结果:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"
)
func main() {
SALT := "salt"
path := []string{"alfa", "beta", "gamma"}
hmacf := hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(sha256.New, []byte(SALT))
}, []byte(path[0]))
}, []byte(path[1]))
}, []byte(path[2]))
hmacf.Write([]byte("data"))
result := hmacf.Sum(nil)
fmt.Println(hex.EncodeToString(result)) // 2e631dcb4289f8256861a833ed985fa945cd714ebe7c3bd4ed4b4072b107b073
}
编辑:
根据这篇帖子的启发,以下是更紧凑/高效的hmac_rec()
实现,它在最后一个迭代步骤中使用常规HMac(这也使得getHash()
变得不必要):
function hmac_rec(data, keyList) {
var key = keyList.pop()
if (keyList.length > 0) {
// 调整密钥(根据HMAC规范)
if (key.length > blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
hmac_rec(key, [...keyList]).copy(k)
} else if (key.length < blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
key.copy(k)
} else {
k = key
}
// 创建'key xor ipad'和'key xor opad'(根据HMAC规范)
var ik = Buffer.allocUnsafe(blockSize)
var ok = Buffer.allocUnsafe(blockSize)
k.copy(ik)
k.copy(ok)
for (var i = 0; i < ik.length; i++) {
ik[i] = 0x36 ^ ik[i]
ok[i] = 0x5c ^ ok[i]
}
// 计算HMAC
var innerHMac = hmac_rec(Buffer.concat([ ik, data ]), [...keyList])
var outerHMac = hmac_rec(Buffer.concat([ ok, innerHMac ]), [...keyList])
} else {
var outerHMac = crypto.createHmac(digest, key).update(data).digest();
}
return outerHMac
}
英文:
According to the specification (RFC 2104), an HMAC uses a digest function internally, e.g. SHA256.
However, your implementation applies (actually non-compliant) HMACs that internally use another HMAC instead of a digest, where only the lowest level HMAC uses a regular digest internally. In this way, a nested structure is created.
Based on the specification of a regular HMAC (with a digest), this can be extended to the HMAC with an HMAC (instead of a digest) as used in the Go code:
HMAC(K XOR opad, HMAC(K XOR ipad, text))
s. RFC2104, sec 2. Definition of HMAC
Because of the difference from the specification, it will probably not be so easy to find a JavaScript library that supports something like this out of the box.
Although most libraries of course support an HMAC, but only allow the specification of a digest (and not of an HMAC), e.g. crypto.createHmac()
of the crypto module of NodeJS, see also the other answer. I don't think this approach can be used to implement the logic from the Go code.
If the approach of the other answer doesn't work and you can't find another JavaScript library with the needed functionality, you can implement the logic in JavaScript yourself, because the specification of the HMAC is relatively simple (s. above).
The following code is a sample implementation with the crypto module of NodeJS:
var crypto = require('crypto')
const digest = 'sha256'
const blockSize = 64 // block size of the digest
// define input parameter
var salt = Buffer.from('salt')
var path = [ Buffer.from('alfa'), Buffer.from('beta'), Buffer.from('gamma') ]
var data = Buffer.from('data')
// calculate HMAC
var hmac = hmac(data, salt, path)
console.log(hmac.toString('hex'))
function hmac(data, salt, path) {
// create keyList
var keyList = []
keyList.push(salt)
keyList = keyList.concat(path)
// determine HMAC recursively
var result = hmac_rec(data, keyList)
return result
}
function hmac_rec(data, keyList) {
// adjust key (according to HMAC specification)
var key = keyList.pop()
if (key.length > blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
if (keyList.length > 0) {
hmac_rec(key, [...keyList]).copy(k)
} else {
getHash(key).copy(k)
}
} else if (key.length < blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
key.copy(k)
} else {
k = key
}
// create 'key xor ipad' and 'key xor opad' (according to HMAC specification)
var ik = Buffer.allocUnsafe(blockSize)
var ok = Buffer.allocUnsafe(blockSize)
k.copy(ik)
k.copy(ok)
for (var i = 0; i < ik.length; i++) {
ik[i] = 0x36 ^ ik[i]
ok[i] = 0x5c ^ ok[i]
}
// calculate HMAC
if (keyList.length > 0) {
var innerHMac = hmac_rec(Buffer.concat([ ik, data ]), [...keyList])
var outerHMac = hmac_rec(Buffer.concat([ ok, innerHMac ]), [...keyList])
} else {
var innerHMac = getHash(Buffer.concat([ik, data]))
var outerHMac = getHash(Buffer.concat([ok, innerHMac]))
}
return outerHMac
}
// calculate SHA256 hash
function getHash(data){
var hash = crypto.createHash(digest);
hash.update(data)
return hash.digest()
}
with the result:
2e631dcb4289f8256861a833ed985fa945cd714ebe7c3bd4ed4b4072b107b073
Test:
The following Go code produces the same result:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"
)
func main() {
SALT := "salt"
path := []string{"alfa", "beta", "gamma"}
hmacf := hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(func() hash.Hash {
return hmac.New(sha256.New, []byte(SALT))
}, []byte(path[0]))
}, []byte(path[1]))
}, []byte(path[2]))
hmacf.Write([]byte("data"))
result := hmacf.Sum(nil)
fmt.Println(hex.EncodeToString(result)) // 2e631dcb4289f8256861a833ed985fa945cd714ebe7c3bd4ed4b4072b107b073
}
Edit:
Inspired by this post, the following is a more compact/efficient implementation for hmac_rec()
that uses the regular HMac for the last iteration step (which also makes getHash()
obsolete):
function hmac_rec(data, keyList) {
var key = keyList.pop()
if (keyList.length > 0) {
// adjust key (according to HMAC specification)
if (key.length > blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
hmac_rec(key, [...keyList]).copy(k)
} else if (key.length < blockSize) {
k = Buffer.allocUnsafe(blockSize).fill('\x00');
key.copy(k)
} else {
k = key
}
// create 'key xor ipad' and 'key xor opad' (according to HMAC specification)
var ik = Buffer.allocUnsafe(blockSize)
var ok = Buffer.allocUnsafe(blockSize)
k.copy(ik)
k.copy(ok)
for (var i = 0; i < ik.length; i++) {
ik[i] = 0x36 ^ ik[i]
ok[i] = 0x5c ^ ok[i]
}
// calculate HMAC
var innerHMac = hmac_rec(Buffer.concat([ ik, data ]), [...keyList])
var outerHMac = hmac_rec(Buffer.concat([ ok, innerHMac ]), [...keyList])
} else {
var outerHMac = crypto.createHmac(digest, key).update(data).digest();
}
return outerHMac
}
答案2
得分: 0
也许你需要这样的代码?
const crypto = require('crypto');
const encoding = 'hex';
const SALT = 'your_salt_value';
const path = ['path1', 'path2', 'path3'];
const hmacf = crypto.createHmac('sha256', SALT)
.update(path[0])
.digest(encoding);
const hmacf2 = crypto.createHmac('sha256', hmacf)
.update(path[1])
.digest(encoding);
const hmacf3 = crypto.createHmac('sha256', hmacf2)
.update(path[2])
.digest(encoding);
console.log(hmacf3);
英文:
Maybe you need something like this?
const crypto = require('crypto');
const encoding = 'hex';
const SALT = 'your_salt_value';
const path = ['path1', 'path2', 'path3'];
const hmacf = crypto.createHmac('sha256', SALT)
.update(path[0])
.digest(encoding);
const hmacf2 = crypto.createHmac('sha256', hmacf)
.update(path[1])
.digest(encoding);
const hmacf3 = crypto.createHmac('sha256', hmacf2)
.update(path[2])
.digest(encoding);
console.log(hmacf3);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论