在 JavaScript 中使用 SubtleCrypto 进行 HMAC 验证的错误结果
Wrong result with HMAC verification using SubtleCrypto in JavaScript
我正在尝试使用 SubtleCrypto API 验证 HMAC 签名。整个事情应该在 Cloudflare Workers 中 运行,我正在使用他们的 wrangler
工具在本地测试它。
到目前为止,这是我的代码,但它生成了错误的签名。
const message = "(query params from an url)";
const given_signature = "(extracted from the query params)";
const SECRET = "...";
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(SECRET),
algorithm,
false,
['sign', 'verify']
);
const signature = await crypto.subtle.sign(
algorithm.name,
key,
encoder.encode(message)
);
const digest = btoa(String.fromCharCode(...new Uint8Array(signature)));
// The digest does not match the signature extracted from the query params
// If I, for example, want to verify the signature directly, the result is still false.
const verify = await crypto.subtle.verify(
algorithm.name,
key,
encoder.encode(given_signature),
encoder.encode(message)
);
如果我在在线 HMAC 测试工具中使用相同的秘密和消息,我得到的结果是正确的,所以我确定我的代码中一定有错误。
我发现有趣的是,我的代码生成的签名比给定的签名短得多(例如 3fn0mhrebHTJMhtOyvRP5nZIhogX/M1OKQ5GojniZTM=
vs ddf9f49a1ade6c74c9321b4ecaf44fe67648868817fccd4e290e46a239e26533
)。
有人知道我哪里出错了吗?
感谢有用的评论!简而言之,问题是提供的签名被编码为 HEX 字符串,而生成的签名是 base64 编码的字符串。
为了保持整洁,这是一个使用 crypto.subtle.verify
函数的工作版本:
const message = "(query params from an url w/o the hmac signature)";
const given_signature = "(the given hmac signature extracted from the query params)";
const SECRET = "(my secret key)";
const hexToBuffer = (hex: string) => {
const matches = hex.match(/[\da-f]{2}/gi) ?? [];
const typedArray = new Uint8Array(
matches.map(function (h) {
return parseInt(h, 16);
})
);
return typedArray.buffer;
};
const algorithm = { name: "HMAC", hash: "SHA-256" };
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(SECRET),
algorithm,
false,
["sign", "verify"]
);
const result: boolean = await crypto.subtle.verify(
algorithm.name,
key,
hexToBuffer(given_signature),
encoder.encode(message)
);
我正在尝试使用 SubtleCrypto API 验证 HMAC 签名。整个事情应该在 Cloudflare Workers 中 运行,我正在使用他们的 wrangler
工具在本地测试它。
到目前为止,这是我的代码,但它生成了错误的签名。
const message = "(query params from an url)";
const given_signature = "(extracted from the query params)";
const SECRET = "...";
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(SECRET),
algorithm,
false,
['sign', 'verify']
);
const signature = await crypto.subtle.sign(
algorithm.name,
key,
encoder.encode(message)
);
const digest = btoa(String.fromCharCode(...new Uint8Array(signature)));
// The digest does not match the signature extracted from the query params
// If I, for example, want to verify the signature directly, the result is still false.
const verify = await crypto.subtle.verify(
algorithm.name,
key,
encoder.encode(given_signature),
encoder.encode(message)
);
如果我在在线 HMAC 测试工具中使用相同的秘密和消息,我得到的结果是正确的,所以我确定我的代码中一定有错误。
我发现有趣的是,我的代码生成的签名比给定的签名短得多(例如 3fn0mhrebHTJMhtOyvRP5nZIhogX/M1OKQ5GojniZTM=
vs ddf9f49a1ade6c74c9321b4ecaf44fe67648868817fccd4e290e46a239e26533
)。
有人知道我哪里出错了吗?
感谢有用的评论!简而言之,问题是提供的签名被编码为 HEX 字符串,而生成的签名是 base64 编码的字符串。
为了保持整洁,这是一个使用 crypto.subtle.verify
函数的工作版本:
const message = "(query params from an url w/o the hmac signature)";
const given_signature = "(the given hmac signature extracted from the query params)";
const SECRET = "(my secret key)";
const hexToBuffer = (hex: string) => {
const matches = hex.match(/[\da-f]{2}/gi) ?? [];
const typedArray = new Uint8Array(
matches.map(function (h) {
return parseInt(h, 16);
})
);
return typedArray.buffer;
};
const algorithm = { name: "HMAC", hash: "SHA-256" };
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(SECRET),
algorithm,
false,
["sign", "verify"]
);
const result: boolean = await crypto.subtle.verify(
algorithm.name,
key,
hexToBuffer(given_signature),
encoder.encode(message)
);