Windows CNG导入RSA密钥时NCryptSetProperty报NTE_NOT_SUPPORTED
问题根因
首次调用NCryptSetProperty返回NTE_NOT_SUPPORTED和TPM是否支持RSA密钥设置NCRYPT_EXPORT_POLICY_PROPERTY无关,是两个实现错误叠加导致的:
- 传入的属性值字节序错误:CNG接口接收的DWORD类型属性值要求使用本机小端序,代码中用
to_be_bytes()转成了大端序,TPM密钥存储提供程序对参数格式校验比软件提供程序严格,无法识别大端序传入的策略值。 - 传入了TPM不支持的策略flag:
NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG(允许明文导出私钥)是TPM提供程序明确不支持的选项。TPM的硬件安全设计要求私钥不能以明文形式离开芯片边界,该flag仅对软件存储提供程序生效。
修复方法
按以下两点修改代码即可解决该错误:
- 将属性值序列化方法从
to_be_bytes()替换为to_le_bytes(),使用Windows系统要求的小端序传参。 - 移除
NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG选项,仅保留NCRYPT_ALLOW_EXPORT_FLAG——该选项在TPM场景下表示允许导出经TPM硬件加密包裹的私钥blob,符合TPM安全规则。
额外注意:导入BCRYPT_RSAFULLPRIVATE_BLOB格式密钥到TPM时,要确保RSA密钥长度在当前TPM支持范围内(消费级TPM通常支持1024/2048/3072/4096位,部分老旧TPM最高仅支持2048位),否则会在后续设置blob或最终化密钥阶段返回参数错误。
修正后的核心代码片段:
unsafe { let mut h = NCRYPT_KEY_HANDLE(0); NCryptCreatePersistedKey( self.handle, &mut h, to_pcwstr(BCRYPT_RSA_ALGORITHM), to_pcwstr(key_name), CERT_KEY_SPEC(0), if overwrite { NCRYPT_OVERWRITE_KEY_FLAG } else { NCRYPT_FLAGS(0) }, )?; let prop_handle = NCRYPT_HANDLE(h.0); println!("set export policy"); // 修正字节序,移除TPM不支持的明文导出标记 let value = NCRYPT_ALLOW_EXPORT_FLAG.to_le_bytes(); let value: *const u8 = addr_of!(value[0]); NCryptSetProperty( prop_handle, to_pcwstr(NCRYPT_EXPORT_POLICY_PROPERTY), value, std::mem::size_of::<u32>() as u32, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG, )?; println!("set blob prop"); NCryptSetProperty( prop_handle, to_pcwstr(BCRYPT_RSAFULLPRIVATE_BLOB), to_import.as_ptr(), to_import.len() as u32, NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG, )?; println!("finalize key"); NCryptFinalizeKey(h, NCRYPT_SILENT_FLAG)?; h }
补充说明
TPM密钥存储提供程序和软件密钥存储提供程序的行为存在本质差异:软件提供程序运行在系统内存中,支持明文导出私钥;TPM提供程序的私钥操作全部在硬件安全边界内完成,永远不会向内存暴露明文私钥材料,所有导出的私钥都是经TPM内部密钥加密后的受保护格式。
内容的提问来源于stack exchange,提问作者Geob-o-matic
相关产品推荐
相关产品推荐

