当前位置: 首页 > 知识库问答 >
问题:

为什么这两个算法最后输出的值不一样,一个node.js写的,一个python写的?

雍骏俊
2023-05-19

python 代码:地址:

import logging
import io
from hashlib import sha1
from struct import pack, unpack

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
.............................
..........
@staticmethod
def makekey_from_password(password, algId, algIdHash, providerType, keySize, saltSize, salt):
    r"""
    Generate intermediate key from given password.

        >>> password = 'Password1234_'
        >>> algId = 0x660e
        >>> algIdHash = 0x8004
        >>> providerType = 0x18
        >>> keySize = 128
        >>> saltSize = 16
        >>> salt = b'\xe8\x82fI\x0c[\xd1\xee\xbd+C\x94\xe3\xf80\xef'
        >>> expected = b'@\xb1:q\xf9\x0b\x96n7T\x08\xf2\xd1\x81\xa1\xaa'
        >>> ECMA376Standard.makekey_from_password(password, algId, algIdHash, providerType, keySize, saltSize, salt) == expected
        True
    """
    logger.debug([password, hex(algId), hex(algIdHash), hex(providerType), keySize, saltSize, salt])
    xor_bytes = lambda a, b: bytearray([p ^ q for p, q in zip(bytearray(a), bytearray(b))])  # bytearray() for Python 2 compat.

    # https://msdn.microsoft.com/en-us/library/dd925430(v=office.12).aspx
    ITER_COUNT = 50000

    password = password.encode("UTF-16LE")
    h = sha1(salt + password).digest()
    for i in range(ITER_COUNT):
        ibytes = pack("<I", i)
        h = sha1(ibytes + h).digest()
    block = 0
    blockbytes = pack("<I", block)
    hfinal = sha1(h + blockbytes).digest()
    cbRequiredKeyLength = keySize // 8
    cbHash = sha1().digest_size
    buf1 = b"\x36" * 64
    buf1 = xor_bytes(hfinal, buf1[:cbHash]) + buf1[cbHash:]
    x1 = sha1(buf1).digest()
    buf2 = b"\x5c" * 64
    buf2 = xor_bytes(hfinal, buf2[:cbHash]) + buf2[cbHash:]
    x2 = sha1(buf2).digest()  # In spec but unused
    x3 = x1 + x2
    keyDerived = x3[:cbRequiredKeyLength]
    logger.debug(keyDerived)
    return keyDerived

node.js 代码:

const crypto = require('crypto');

function makekey_from_password (password, algId, algIdHash, providerType, keySize, saltSize, salt) {
  const ITER_COUNT = 50000;
  const cbRequiredKeyLength = keySize / 8;

  const passwordBuf = Buffer.from(password, 'utf16le');
  //   let saltedPasswordHash = crypto.createHash('sha1').update(salt).update(passwordBuf).digest();
  let saltedPasswordHash = crypto.createHash('sha1').update(Buffer.concat([salt, passwordBuf])).digest();

  for (let i = 0; i < ITER_COUNT; i++) {
    const ibytes = Buffer.alloc(4);
    ibytes.writeUInt32LE(i, 0);
    saltedPasswordHash = crypto.createHash('sha1').update(Buffer.concat([ibytes, saltedPasswordHash])).digest();
    // saltedPasswordHash = crypto.createHash('sha1').update(ibytes).update(saltedPasswordHash).digest();
  }

  const block = Buffer.alloc(4);
  // const hfinal = crypto.createHash('sha1').update(saltedPasswordHash).update(block).digest();
  const hfinal = crypto.createHash('sha1').update(Buffer.concat([saltedPasswordHash, block])).digest();

  const cbHash = 20;

  let buf1 = Buffer.alloc(64, 0x36);
  buf1 = Buffer.concat([xor_bytes(hfinal, buf1.slice(0, cbHash)), buf1.slice(cbHash)]);
  const x1 = crypto.createHash('sha1').update(buf1).digest();

  let buf2 = Buffer.alloc(64, 0x5C);
  buf2 = Buffer.concat([xor_bytes(hfinal, buf2.slice(0, cbHash)), buf2.slice(cbHash)]);
  const x2 = crypto.createHash('sha1').update(buf2).digest();
  const x3 = Buffer.concat([x1, x2]);
  const keyDerived = x3.slice(0, cbRequiredKeyLength);

  return keyDerived;
}

function xor_bytes (a, b) {
  const result = [];
  for (let i = 0; i < a.length; i++) {
    result.push(a[i] ^ b[i]);
  }
  return Buffer.from(result);
}

// 测试
const password = 'Password1234_';
const algId = 0x660e;
const algIdHash = 0x8004;
const providerType = 0x18;
const keySize = 128;
const saltSize = 16;
const salt = Buffer.from('e88266490c5bd1eebd2b43c94e3f3830ef', 'hex');
const expected = Buffer.from('40b13a71f90b966e375408f2d181a1aa', 'hex');
const key = makekey_from_password(password, algId, algIdHash, providerType, keySize, saltSize, salt);
console.log('key', key, key.toString('hex'));
console.log(makekey_from_password(password, algId, algIdHash, providerType, keySize, saltSize, salt).equals(expected));

python 输入输出:

>>> salt = b'\xe8\x82fI\x0c[\xd1\xee\xbd+C\x94\xe3\xf80\xef'
>>> expected = b'@\xb1:q\xf9\x0b\x96n7T\x08\xf2\xd1\x81\xa1\xaa'
(40b13a71f90b966e375408f2d181a1aa)

node.js 输入输出:

Buffer.from('e88266490c5bd1eebd2b43c94e3f3830ef', 'hex');
实际输出是:6c1cc43d0fbe4e7d987f7ac68968111d

请大佬指教为什么我参考 python 代码写的 node.js 代码最后输出的内容是不一样的?

共有2个答案

丁雅惠
2023-05-19

nodejs地方改一下:

const salt = Buffer.from([0xe8, 0x82, 0x66, 0x49, 0x0c, 0x5b, 0xd1, 0xee, 0xbd, 0x2b, 0x43, 0xc9, 0x4e, 0x3f, 0x38, 0x30, 0xef]);
扶文光
2023-05-19

代码我没看,不过你这个测试用例两边儿也不一致啊?

你 python 里:

salt = b'\xe8\x82fI\x0c[\xd1\xee\xbd+C\x94\xe3\xf80\xef'

对应十六进制是:

e88266490c5bd1eebd2b4394e3f830ef

跟你 JS 里的也不一样啊?

e88266490c5bd1eebd2b4394e3f830ef    -- python
e88266490c5bd1eebd2b43c94e3f3830ef  -- node.js
 类似资料:
  • py代码如下: 参考py写的node.js代码如下 node.js 的解密参数全是和py的一样,打印出来的key也是一样的,说明makeKey方法是一样的, 就是接下来的rc4解密,不知道是我node.js哪里写的不对,最后解密出来的verifierHash和 hash 不一样,求大神解答下,是不是node.js这里的rc4解密有问题导致的,还是哪里有问题?

  • python代码:代码地址: node.js代码: go 代码:代码地址: 请大佬指教为什么我参考python代码写的node.js代码最后输出的内容是不一样的,py版和go版的可以解密成功,node.js的代码可以运行,但是最后解密出来的是不对的?是因为aes-128-ecb算法需要分块解密吗?

  • 小白菜疑问,能不能详细地跟我说一下两种写法的区别? 题目:使用函数求素数和: 定义prime(p), 其中函数prime当用户传入参数p为素数时返回True,否则返回False 定义PrimeSum(m,n),函数PrimeSum返回区间[m, n]内所有素数的和。题目保证用户传入的参数1<=m<n 假设输入m=1,n=10 疑问:下面是两种不同的写法,第一个是我写的,第二个是正确答案TT,我不太

  • 下面的代码基于@a.bertucci提供的一个示例,这里使用Android上的RxJava以固定的间隔在UI中绘制对象,其中我使用计时器压缩了一个可观察对象。当我通过调用processDelayedItems()触发订阅时,压缩的Observable中的代码[A]只执行一次,一个项目被发送到[B]。我原本希望代码[A]在触发后继续运行,并保持每1500毫秒发出一次项,但显然它在这里只运行一次。 >

  • 问题内容: 我发布此消息是因为该主题刚刚在另一个问题/答案中提出,并且该行为没有得到很好的记录。 考虑数据框 我想获取由column定义的每个组的第一行和最后一行。 我试过了 但是,这并没有给我我所期望的。 如何获得每个组中的实际第一个和最后一个值? 问题答案: 一种选择是使用该方法: 但是,我还没有找到一种将它们整齐地聚合的方法。当然,总是可以使用构造函数: 注意:我明确使用了该属性,否则您必须

  • 本文向大家介绍写一个方法将一个数组中的元素,从前到后依次两两组合,最后一个与第一个组合相关面试题,主要包含被问及写一个方法将一个数组中的元素,从前到后依次两两组合,最后一个与第一个组合时的应答技巧和注意事项,需要的朋友参考一下