Hmac-SHA256 未返回预期的哈希值

Hmac-SHA256 not returning expected hash

我知道这里有很多关于此的问题,但在浏览了大部分问题后,我还没有真正看到任何解决我的问题的方法。

在以下输入上使用 SHA256 我得到了正确的输出:

var canonString = 'GET\n'+
                    '/\n'+
                    'Action=ListUsers&Version=2010-05-08\n'+
                    'content-type:application/x-www-form-urlencoded; charset=utf-8\n'+
                    'host:iam.amazonaws.com\n'+
                    'x-amz-date:20150830T123600Z\n'+
                    '\n'+
                    'content-type;host;x-amz-date\n'+
                    'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';

console.log(CryptoJS.SHA256(canonString).toString()); //returns the expected value of f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59

所以 SHA256 在这方面工作正常。同样,在以下输入上使用 Hmac-SHA256,我得到了正确的响应:

var kDate = CryptoJS.HmacSHA256("20150830", "AWS4wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY");
var kRegion = CryptoJS.HmacSHA256('us-east-1', kDate);
var kService = CryptoJS.HmacSHA256('iam', kRegion);
var kSigning = CryptoJS.HmacSHA256("aws4_request", kService);

console.log(kSigning.toString()); //returns the expected value of c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9

所以这个 Hmac-SHA256 函数在这个输入上可以正常工作。但是,在以下输入中,Hmac-SHA256 没有 return 预期的输出。

var stringToSign = 'AWS4-HMAC-SHA256\n'+
                '20150830T123600Z\n'+
                '20150830/us-east-1/iam/aws4_request\n'+
                CryptoJS.SHA256(canonString).toString();

CryptoJS.HmacSHA256(kSigning.toString(), stringToSign); //Returns 8a96b6691875490d30d05731cc9aa26be1fd64cf611ed929753b6498075aa886
//Expected value is 5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
//Trying in opposite order just in case
CryptoJS.HmacSHA256(stringToSign, kSigning.toString()); //Returns fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
//Still not expected value which is 5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7

所以,我的 stringToSign 显然出了点问题,我不知道是什么问题。我在想换行符被解释为两个不同的字符,而不仅仅是一个字符。但是,像 '\\n' 一样转义它也没有解决它!我在这里不知所措。这是我一直关注的两个文档 (doc1 doc2)。有谁知道为什么我得不到预期的输出?

记住 sha256 摘要是一个字节序列:它不是 "normal string"。为了方便起见,CryptoJS 似乎正在将真正的 sha256 摘要转换为其他内容,所以不要那样做,你就可以开始了。

使用 Node 的 crypto 库(它是一个内置的 API)而不是 CryptoJS(它的文档非常糟糕,所以使用它有点值得怀疑):

const crypto = require("crypto");

function HMAC(key, text) {
    return crypto.createHmac("sha256", key).update(text).digest();
}

然后我们形成规范哈希:

const canonString = [
    'GET',
    '/',
    'Action=ListUsers&Version=2010-05-08',
    'content-type:application/x-www-form-urlencoded; charset=utf-8',
    'host:iam.amazonaws.com',
    'x-amz-date:20150830T123600Z',
    '',
    'content-type;host;x-amz-date',
    'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
].join('\n');

// note: plain hash, not a secret-key-seeded hash
const canonHash = crypto.createHash("sha256").update(canonString).digest();
console.log("Canonical hash is   :", canonHash.toString('hex'));

这会产生 f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59,但这只是因为我们使用 .toString('hex') 将其记录为十六进制字符串:真实 值仍然是一个字节序列。

然后我们继续:

const kSecret = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
const kDate = HMAC("AWS4" + kSecret,"20150830");
const kRegion = HMAC(kDate,"us-east-1");
const kService = HMAC(kRegion,"iam");
const kSigning = HMAC(kService,"aws4_request");
console.log("kSigning hash is    :", kSigning.toString('hex'));

产生 c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9:再次注意,这仅在 toString('hex') 之后用于控制台日志记录。 sha256 字节摘要 kSigning 本身 不是 十六进制字符串。

然后最后:

const stringToSign = [
    'AWS4-HMAC-SHA256',
    '20150830T123600Z',
    '20150830/us-east-1/iam/aws4_request',
    canonHash.toString('hex')
].join('\n');

const signed = HMAC(kSigning, stringToSign);
console.log("Final signed hash is:", signed.toString('hex'));

这会产生 5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7,请注意,我们 必须 将规范哈希转换为十六进制字符串以用于签名目的,而不仅仅是日志记录,按照中的说明您 link 访问的页面。但是,我们仍然 触及 kSigning 摘要,它仍然是一个真正的 sha256 字节序列。