2

我有一个共享密钥,我需要从中派生一个 iv 以便我可以破译。

苹果商务聊天文档状态:

生成派生密钥和初始向量 通过具有 SHA256 哈希函数的 X9.63 密钥派生函数运行共享密钥。这会产生一个 48 字节的有效负载。你的结果应该是rV3qrszd0PMPgeRhNnlOYA==

这是我尝试过的。我将 scryptSync 和 pbkdf2Sync 加密函数与许多“盐”配置一起使用。我不确定这些是否是这项工作的正确功能。

const crypto = require('crypto');

const keyLength = 48;
// sharedKey is a base64 string
const sharedKey = "2lvSJsBO2keUHRfvPG6C1RMUmGpuDbdgNrZ9YD7RYnvAcfgq/fjeYr1p0hWABeif";
// publicKey is a base64 string
const publicKey = "BDiRKNnPiPUb5oala31nkmCaXMB0iyWy3Q93p6fN7vPxEQSUlFVsInkJzPBBqmW1FUIY1KBA3BQb3W3Qv4akZ8kblqbmvupE/EJzPKbROZFBNvxpvVOHHgO2qadmHAjHSg=="

const key1 = crypto.scryptSync(sharedKey, 'salt', keyLength);
console.log(key2.toString('base64'));

const key2 = crypto.pbkdf2Sync(sharedKey, 'salt', 10000, keyLength, 'sha256');
console.log(key2.toString('base64'));

// results should be:
// mAzkYatDlz4SzrCyM23NhgL/+mE3eGgfUz9h1CFPhZM=
// iv: rV3qrszd0PMPgeRhNnlOYA==

下面是使用 X9.63 密钥推导函数推导密钥和初始向量的 Apple 示例代码。

    def ITOSP(self, longint, length):
        """ITOSP, short for Integer-to-Octet-String Primitive, converts a non-negative integer
        to an octet string of a specified length. This particular function is defined in the
        PKCS #1 v2.1: RSA Cryptography Standard (June 14, 2002)
        https://www.cryptrec.go.jp/cryptrec_03_spec_cypherlist_files/PDF/pkcs-1v2-12.pdf"""

        hex_string = "%X" % longint
        assert len(hex_string) <= 2 * length, "ITOSP function: Insufficient length for encoding"
        return binascii.a2b_hex(hex_string.zfill(2 * length))

    def KDFX963(self, inbyte_x, shared_data, key_length, hashfunct=sha256, hash_len=32):
        """KDFX963 is a key derivation function (KDF) that takes as input byte sequence inbyte_x
        and additional shared data shared_data and outputs a byte sequence key of length
        key_length. This function is defined in ANSI-X9.63-KDF, and this particular flavor of
        KDF is known as X9.63. You can read more about it from:
        http://www.secg.org/sec1-v2.pdf"""

        assert key_length >= 0, "KDFX963 function: key_length should be positive integer"
        k = key_length / float(hash_len)
        k = int(ceil(k))

        acc_str = ""
        for i in range(1, k+1):
            h = hashfunct()
            h.update(inbyte_x)
            h.update(self.ITOSP(i, 4))
            h.update(shared_data)
            acc_str = acc_str + h.hexdigest()

        return acc_str[:key_length * 2]
4

1 回答 1

3

X9.63 KDF 是一个密钥派生函数,例如在此处此处进行了描述。scrypt 和 PBKDF2 也是 KDF,但是不同的,所以当然不能用它们来重现预期的结果。

所以你需要一个支持 X.963 KDF 的 NodeJS 库。如果找不到,也可以自己实现。

X9.63 KDF 需要一个共享密钥和一个共享信息,并确定一个keysize大密钥,如下所示:

  • 创建一个从 0x0000001 开始递增的 4 字节计数器 c i 。
  • 连接数据 conc i = shared secret | 我| _ 共享信息
  • 散列结果 hash i = hash(conc i )
  • 连接哈希 hash 1 | 哈希2 | ...直到生成长度为keysize的输出。

更正式地说,包括各种检查,算法在上面的链接中描述。问题后面发布的 Python 代码也实现了这个逻辑。

一种可能的 NodeJS 实现(省略规范中的检查)是:

var crypto = require('crypto');

var digest = 'sha256';
var digestLen = 32;

function X963KDF(sharedSecret, sharedInfo, keySize){
  var maxCount = Math.ceil(keySize/digestLen);
  var result = Buffer.allocUnsafe(0);
  for (var count = 1; count < maxCount + 1; count++){
      var counter = Buffer.allocUnsafe(4);
      counter.writeUInt32BE(count, 0);
      var current = Buffer.concat([sharedSecret, counter, sharedInfo]);
      var hash = crypto.createHash(digest).update(current).digest();
      result = Buffer.concat([result, hash]);
  }
  
  return result.slice(0, keySize);
}

测试:

在问题中发布了共享秘密,但未发布共享信息。互联网搜索显示发布的问题在此处进行了描述,因此也可以确定共享信息(但是,如果您将此信息添加到您的问题中会更好):

var sharedSecret = Buffer.from('2lvSJsBO2keUHRfvPG6C1RMUmGpuDbdgNrZ9YD7RYnvAcfgq/fjeYr1p0hWABeif', 'base64')
var sharedInfo = Buffer.from('04389128d9cf88f51be686a56b7d6792609a5cc0748b25b2dd0f77a7a7cdeef3f111049494556c227909ccf041aa65b5154218d4a040dc141bdd6dd0bf86a467c91b96a6e6beea44fc42733ca6d139914136fc69bd53871e03b6a9a7661c08c74a', 'hex');

var keyiv = X963KDF(sharedSecret, sharedInfo, 48);
var key = keyiv.slice(0,32).toString('base64');
var iv = keyiv.slice(32, 48).toString('base64');
console.log("Key: ", key); // Key:  mAzkYatDlz4SzrCyM23NhgL/+mE3eGgfUz9h1CFPhZM=
console.log("IV: ", iv); // IV:  rV3qrszd0PMPgeRhNnlOYA==

生成的密钥和 IV 等于预期值。

于 2022-01-04T11:34:56.887 回答