Microsoft Graph API负载解密疑问:AES与HMAC密钥推导问题
Microsoft Teams Shifts自定义集成中AES+HMAC密钥推导方案修正
我在基于Microsoft Graph API开发Teams Shifts自定义劳动力集成时,对接完成后Graph API会发送AES-256-CBC-HMAC-SHA256加密的负载,使用的是步骤4提供的64字符十六进制密钥。但文档没说明如何从这个密钥推导AES(32字节)和HMAC(32字节)密钥。我尝试将十六进制解码后拆分前32字节做AES密钥、后32字节做HMAC密钥,但解码后只有32字节,导致HMAC密钥为空,请问这个推导方式错在哪里?
原实现代码:
-- Hex character to value lookup table local hexMap = { ["0"] = 0, ["1"] = 1, ["2"] = 2, ["3"] = 3, ["4"] = 4, ["5"] = 5, ["6"] = 6, ["7"] = 7, ["8"] = 8, ["9"] = 9, ["a"] = 10, ["b"] = 11, ["c"] = 12, ["d"] = 13, ["e"] = 14, ["f"] = 15, ["A"] = 10, ["B"] = 11, ["C"] = 12, ["D"] = 13, ["E"] = 14, ["F"] = 15 } -- Function to decode hex string to binary data local function hexDecode(hexStr) if #hexStr % 2 ~= 0 then error("Hex string must have even length") end local result = {} for i = 1, #hexStr, 2 do local high = hexStr:sub(i, i) local low = hexStr:sub(i+1, i+1) if not hexMap[high] or not hexMap[low] then error("Invalid hex characters at position "..i) end local byte = hexMap[high] * 16 + hexMap[low] table.insert(result, string.char(byte)) end return table.concat(result) end local secret = hexDecode(secretHex) local key_enc = secret:sub(1, 32) local key_mac = secret:sub(33, 64)
问题分析与修正方案
你的推导方式核心错误在于对密钥长度的认知:64字符的十六进制字符串解码后是32字节(每2个十六进制字符对应1字节),根本无法拆分成两个32字节的密钥。正确做法是使用**HKDF(基于HMAC的密钥派生函数)**从这32字节的主密钥中分别派生出AES加密密钥和HMAC校验密钥。
以下是修正后的Lua实现代码:
-- 依赖LuaCrypto库提供HMAC-SHA256和SHA256功能,需提前安装:luarocks install luacrypto local crypto = require("crypto") -- 保留原十六进制解码函数 local hexMap = { ["0"] = 0, ["1"] = 1, ["2"] = 2, ["3"] = 3, ["4"] = 4, ["5"] = 5, ["6"] = 6, ["7"] = 7, ["8"] = 8, ["9"] = 9, ["a"] = 10, ["b"] = 11, ["c"] = 12, ["d"] = 13, ["e"] = 14, ["f"] = 15, ["A"] = 10, ["B"] = 11, ["C"] = 12, ["D"] = 13, ["E"] = 14, ["F"] = 15 } local function hexDecode(hexStr) if #hexStr % 2 ~= 0 then error("Hex string must have even length") end local result = {} for i = 1, #hexStr, 2 do local high = hexStr:sub(i, i) local low = hexStr:sub(i+1, i+1) if not hexMap[high] or not hexMap[low] then error("Invalid hex characters at position "..i) end local byte = hexMap[high] * 16 + hexMap[low] table.insert(result, string.char(byte)) end return table.concat(result) end -- HKDF实现:包含提取和扩展两个步骤 local function hkdf(salt, ikm, info, length) -- 提取步骤:生成伪随机密钥(PRK) local prk = crypto.hmac.digest("sha256", ikm, salt, true) -- 扩展步骤:从PRK派生出指定长度的密钥 local t = "" local previous = "" local iterations = math.ceil(length / 32) for i = 1, iterations do previous = crypto.hmac.digest("sha256", previous .. info .. string.char(i), prk, true) t = t .. previous end return t:sub(1, length) end -- 主流程:派生AES和HMAC密钥 local secretHex = "你的64字符十六进制密钥" local masterKey = hexDecode(secretHex) -- 用不同的info字段区分两个密钥,确保派生结果唯一 local aesKey = hkdf("", masterKey, "Teams Shifts AES Encryption Key", 32) local hmacKey = hkdf("", masterKey, "Teams Shifts HMAC Verification Key", 32) -- 验证密钥长度(均应为32字节) print("AES Key Length: " .. #aesKey) print("HMAC Key Length: " .. #hmacKey)
关键说明
- 必须使用密钥派生函数(如HKDF)从单个主密钥生成多个子密钥,直接拆分的方式因主密钥长度不足不可行。
- 两个子密钥的
info参数需唯一,避免派生结果冲突,这里用明确的业务标识字符串区分。 - 代码依赖LuaCrypto库,需通过LuaRocks安装后方可运行。
内容的提问来源于stack exchange,提问作者Man Hen Choy
相关产品推荐
相关产品推荐

