如何通过TPM从BCryptSecretAgreement共享秘密派生密钥且不泄露密钥?
ECDH密钥协商派生密钥留存TPM实现问题
从高层需求来看,我需要实现ECDH密钥协商方案,最终生成的临时密钥要存储在TPM中。目前bcrypt/ncrypt接口几乎具备我所需的全部能力:我可以完成ECDH秘密协商,再从共享秘密派生密钥。据我所知,使用MS_PLATFORM_CRYPTO_PROVIDER密钥存储提供商时,秘密协商是在TPM内部完成的,但我找到的所有密钥派生函数都只会将新派生的密钥输出到内存的明文缓冲区中,这似乎违背了在TPM上执行ECDH的初衷(ECDH本身使用的是临时密钥)。
下面是可运行的交互代码片段,只是NCryptDeriveKey的输出会存储在内存中。我本质上需要找到一种方法,将NCryptDeriveKey的输出存储在TPM中,这样后续我可以直接用它做加密,不会出现密钥泄露。我原本以为这是常规使用场景,但一直找不到实现方法。本示例使用的是NCryptDeriveKey,基于CNG接口相关特性,我最终希望全流程使用BCryptxxx系列函数,因为我不需要密钥持久化存储。
//NOTE: 为了代码简洁已移除部分错误检查逻辑 class EcdhPartner { public: EcdhPartner(NCRYPT_PROV_HANDLE hProvider, string name) : m_hProvider(hProvider), m_keyname("ECDHTestKey1" + name), m_name(name) { cout << "New EcdhPartner '" + m_name + "' keyname=" + m_keyname + "\n"; } void GenerateKeypair() { SECURITY_STATUS secStatus; secStatus = NCryptCreatePersistedKey(m_hProvider, &m_hKey, BCRYPT_ECDH_P256_ALGORITHM, s2lpcwstr(m_keyname), 0, NCRYPT_OVERWRITE_KEY_FLAG); secStatus = NCryptFinalizeKey(m_hKey, 0); // 导出公钥 DWORD publicBlobLength; // 先获取公钥长度 secStatus = NCryptExportKey(m_hKey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, NULL, NULL, &publicBlobLength, 0); // 导出公钥内容 secStatus = NCryptExportKey(m_hKey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, m_publicKeyBlob, 72, &publicBlobLength, 0); } void DoKeyAgreement(EcdhPartner& partner) { SECURITY_STATUS secStatus; NCRYPT_KEY_HANDLE hPartnerPublicKey; // 导入对端公钥 secStatus = NCryptImportKey(m_hProvider, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, &hPartnerPublicKey, partner.m_publicKeyBlob, 72, 0); CheckSecStatus(secStatus, m_name, "NCryptImportKey"); secStatus = NCryptSecretAgreement(m_hKey, hPartnerPublicKey, &m_hSharedSecret, 0); // 构造KDF参数列表 const DWORD BufferLength = 3; BCryptBuffer BufferArray[BufferLength] = { 0 }; // 指定哈希算法 BufferArray[0].BufferType = KDF_HASH_ALGORITHM; BufferArray[0].cbBuffer = (DWORD)((wcslen(BCRYPT_SHA256_ALGORITHM) + 1) * sizeof(WCHAR)); BufferArray[0].pvBuffer = (PVOID)BCRYPT_SHA256_ALGORITHM; NCryptBufferDesc parameterList = { 0 }; parameterList.cBuffers = 1; parameterList.pBuffers = BufferArray; parameterList.ulVersion = BCRYPTBUFFER_VERSION; DWORD derivedKeyLength; // 我需要该接口的输出结果保留在TPM内部!!! secStatus = NCryptDeriveKey(m_hSharedSecret, BCRYPT_KDF_HMAC, ¶meterList, m_derivedKey, sizeof(m_derivedKey), &derivedKeyLength, KDF_USE_SECRET_AS_HMAC_KEY_FLAG); } const string m_keyname; const string m_name; NCRYPT_PROV_HANDLE m_hProvider; NCRYPT_KEY_HANDLE m_hKey; BYTE m_publicKeyBlob[72]; //64位密钥内容 + 8字节头 NCRYPT_SECRET_HANDLE m_hSharedSecret; BYTE m_derivedKey[32]; }; int main() { LPCWSTR providerName = MS_PLATFORM_CRYPTO_PROVIDER; NCRYPT_PROV_HANDLE hProvider; SECURITY_STATUS secStatus; secStatus = NCryptOpenStorageProvider(&hProvider, providerName, 0); EcdhPartner alice(hProvider, "Alice"); EcdhPartner bob(hProvider, "Bob"); alice.GenerateKeypair(); bob.GenerateKeypair(); alice.DoKeyAgreement(bob); bob.DoKeyAgreement(alice); }
内容的提问来源于stack exchange,提问作者rkor
相关产品推荐
相关产品推荐

