【问题标题】:Importing RSA public key from XML using RSACryptoServiceProvider sets property PublicOnly to false使用 RSACryptoServiceProvider 从 XML 导入 RSA 公钥将属性 PublicOnly 设置为 false
【发布时间】:2015-02-28 21:02:29
【问题描述】:

我正在创建一个工具来管理 Windows 上的 RSA 密钥对,因为没有内置功能,我们在 .NET 中广泛使用了 RSA 加密功能。

该工具提供各种功能,例如列出现有的 RSA 密钥对、创建新密钥、删除密钥、导出密钥以及从以前创建的 XML 导出(使用 ASPNet_RegIIS.exe 导出)导入密钥。

除了仅导入用于加密操作的公钥之外,我还拥有所有功能。当最初在服务器上创建密钥对时,公共和公共/私有导出都使用 aspnet_regiis.exe 导出到 xml。

我热衷于提供加密其他机器上的配置文件的选项,以防止私钥被分发,除非绝对必要。

每次我从以前导出的 XML 块导入公钥时,RSACryptoServiceProviderPublicOnly 属性设置为 false,表示密钥对具有公钥和私钥。我已经确认 xml 不包含私钥信息,因此问题不在 xml 文件中。

当使用CspParameters 对象并将标志指定为CspProviderFlags.UseMachineKeyStore 时,似乎会出现问题。如果构造RSACryptoServiceProvider而不指定任何Csp参数,然后从xml导入密钥,PublicOnly属性正确设置为false。

String xmlText = File.ReadAllText(filePath);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
csp.FromXmlString(xmlText);
//csp.PublicOnly equals true;

但是,由于我需要为密钥容器命名,以便以后可以将其用于加密操作,因此在构造 RSACryptoServiceProvider 时,我不得不使用 CspParameters 对象,因为没有其他方法可以命名密钥容器。

String xmlText = File.ReadAllText(filePath);
CspParameters cspParams = new CspParameters();
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.KeyContainerName = keyContainerName;
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cspParams);
csp.PersistKeyInCsp = true;
csp.FromXmlString(xmlText);
//csp.PublicOnly equals false;

我已经尝试了此代码的各种版本,但问题仍然存在。我注意到有类似的问题,例如RSA Encryption public key not returned from container?,但是我相信这个问题是不同的,并且没有给出令人满意的答案。

因此,问题是如何从 XML 导入 RSA 公钥并为密钥容器命名,同时确保容器中只存在一个公钥对?

编辑

围绕该领域的进一步研究也表明,从 xml 导入完整密钥并设置标志以允许导出密钥时存在问题。

CspProviderFlags.UseArchivableKey;

当在 CSP 参数对象上指定此标志时,会在 csp.FromXmlString(xmlText) 行抛出“指定的无效标志”类型的异常;

我真的无法解释这一点。密钥已创建并先前导出为 XML,如果密钥先前已导出,则应该可以将其导入并允许再次导出?

我对此进行了大量研究,但无法找到这些问题的答案。

我已尝试将 csp 提供程序类型从 PROV_RSA_FULL 更改为 PROV_RSA_AES,因为我相信这是现在的默认设置,并且我认为密钥可能最初是使用它而不是 PROV_RSA_FULL 创建的,但这没有任何区别。

【问题讨论】:

    标签: c# encryption cryptography rsa rsacryptoserviceprovider


    【解决方案1】:

    在导入密钥参数(从 XML 或从 RSAParameters)时,如果提供了公共参数,RSACryptoServiceProvider 将与当前密钥分离;如果导入私有参数,它只会替换密钥容器中密钥的内容。

    http://referencesource.microsoft.com/#mscorlib/system/security/cryptography/rsacryptoserviceprovider.cs,b027f6909aa0a6d1

    ImportCspBlob 采用了不同的路径,但最终得出了相同的结论:如果正在导入一个仅限公共的 blob,它会分离到一个临时密钥。 http://referencesource.microsoft.com/#mscorlib/system/security/cryptography/utils.cs,754f1f4055bba611

    似乎底层的 Windows 加密 API 不允许存储公钥(除了与私钥一起存储时)。

    https://msdn.microsoft.com/en-us/library/windows/desktop/bb427412(v=vs.85).aspx 的 CNG 文档说

    要让 BCryptExportKey 创建持久密钥对,输入密钥 BLOB 必须包含私钥。公钥不持久。

    人们假设他们的意思是 BCryptImportKey,但“公钥不持久”感觉很权威。

    对于 CAPI,我找不到这么简单的东西。

    我能找到的最好的是“密钥容器”的 CAPI 定义 (https://msdn.microsoft.com/en-us/library/windows/desktop/ms721590(v=vs.85).aspx#_security_key_container_gly):

    密钥容器

    密钥数据库的一部分,包含属于特定用户的所有密钥对(交换和签名密钥对)。每个容器都有一个唯一的名称,在调用 CryptAcquireContext 函数以获取容器的句柄时使用该名称。

    此定义仅引用“密钥对”,表明“公钥”可能会被存储层拒绝。鉴于 RSACryptoServiceProvider 的行为,这似乎很有可能。

    【讨论】:

      猜你喜欢
      • 2013-07-31
      • 2018-08-01
      • 2011-05-11
      • 2015-01-21
      • 1970-01-01
      • 2016-08-16
      • 1970-01-01
      • 2014-07-07
      相关资源
      最近更新 更多