【问题标题】:How to get raw output from SHA1 using JS as PHP does?如何像 PHP 一样使用 JS 从 SHA1 获取原始输出?
【发布时间】:2021-04-26 05:51:23
【问题描述】:

我正在尝试在 Postman 预请求脚本 中动态生成安全标头。为此,我需要将以下代码 sn-p 从 PHP 转换为 JS。

$password = "SECRETPASSWORD";
$nonce = random_bytes(32);
date_default_timezone_set("UTC");
$created = date(DATE_ATOM);
$encodedNonce = base64_encode($nonce);
$passwordHash = base64_encode(sha1($nonce . $created . sha1($password, true), true));

(注意php's sha1() function 处的true 标志,强制原始输出)。

到目前为止,我已经编写了这个代码 sn-p:

var uuid = require('uuid');
var CryptoJS = require('crypto-js');
var moment = require('moment');

// Generate messageId
var messageId = uuid.v4();
pm.environment.set('messageId', messageId);


// Generate nonce
var nonce = uuid.v4();
var encodedNonce = CryptoJS.enc.Base64.stringify(
    CryptoJS.enc.Utf8.parse(nonce)
);
pm.environment.set('nonce', encodedNonce);

// Generate created
var created = moment().utc().format();
pm.environment.set('created', created);

// Generate password hash
var password = 'SECRETPASSWORD';
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
var passwordHash =  CryptoJS.SHA1(nonce + created + rawSha1Password).toString(CryptoJS.enc.Base64);
pm.environment.set('passwordHash', passwordHash);

我的 JS 脚本几乎可以工作了,唯一的问题似乎是 sha1 生成。取以下示例值:

密码:SECRETPASSWORD
nonce:55d61876-f882-42f0-b390-dc662a7e7279
创建:2021-01- 21T18:19:32Z

PHP 的输出是:

encodedNonce:NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash:olI18mUowhmeCwjb1FJNHtTHYDA=

但是,JS的输出是:

encodedNonce:NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash:tk/uYkL/3Uq0oIkYO0nlBGnV/0E=

如您所见,encodedNonce 是正确构建的;但是密码哈希值不同。当我使用 Postman 时,可用的 JS 库有限。 考虑到这一点,我怎样才能得到与 PHP 相同的结果?

【问题讨论】:

  • rawSha1Password 在散列时似乎是 base 64 编码。当你重新散列它时,它需要是原始二进制文件(这就是为什么true 在调用中)。 IE。如果我没记错的话,只是CryptoJS.SHA1(password) 的输出。
  • 感谢您的贡献。 CryptoJS.SHA1(password) 返回一个 WordsArray 对象;这就是我在创建缓冲区之前编码为 Base64 的原因。如果此时我尝试编码为 Utf8,库会抛出错误:格式错误的 Utf8 字符串。

标签: javascript php cryptography sha1 cryptojs


【解决方案1】:

排队

var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");

密码哈希被读入缓冲区,然后进行 UTF-8 解码。 UTF-8 解码通常会破坏数据,请参阅here。一种可能的解决方案是连接 WordArrays 而不是字符串:

function getPasswordHash(test){

    // Generate nonce
    var nonceWA = !test ? CryptoJS.lib.WordArray.random(32) : CryptoJS.enc.Utf8.parse('55d61876-f882-42f0-b390-dc662a7e7279'); 
    console.log('nonce (Base64):  ' + nonceWA.toString(CryptoJS.enc.Base64)); 

    // Generate created
    var created = !test ? moment().utc().format('YYYY-MM-DDTHH:mm:ss[Z]') : '2021-01-21T18:19:32Z'; 
    var createdWA = CryptoJS.enc.Utf8.parse(created);
    console.log('created:         ' + created); 

    // Hash password
    var pwd = 'SECRETPASSWORD';
    var pwdHashWA =  CryptoJS.SHA1(pwd);

    // Hash nonce + created + pwd
    var passwordHash =  CryptoJS.SHA1(nonceWA.concat(createdWA).concat(pwdHashWA)).toString(CryptoJS.enc.Base64);

    console.log('passwordHash:    ' + passwordHash);
}

getPasswordHash(true);    // with testdata
getPasswordHash(false);   // without testdata
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

执行代码时,getPasswordHash() 的第一次调用使用随机数和日期的测试数据 (test=true),第二次调用应用随机随机数和当前日期 (test=false)。带有测试数据的调用返回与 PHP 代码相同的结果:

nonce (Base64):  NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
created:         2021-01-21T18:19:32Z
passwordHash:    olI18mUowhmeCwjb1FJNHtTHYDA=

CryptoJS 将 CryptoJS.lib.WordArray.random() 实现为 CSPRNG,它是 PHP 方法 random_bytes() 的对应物。因此实际上没有必要使用 uuid 库。我在示例中选择了CryptoJS.lib.WordArray.random(),因为它最接近 PHP 代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 2013-03-18
    • 1970-01-01
    • 2013-10-17
    相关资源
    最近更新 更多