使用RSACryptoServiceProvider从XML导入RSA公钥会将属性PublicOnly设置为false

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用RSACryptoServiceProvider从XML导入RSA公钥会将属性PublicOnly设置为false相关的知识,希望对你有一定的参考价值。

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

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

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

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

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

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

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

但是,因为我需要为密钥容器提供一个名称,以便以后可以用于加密操作,所以在构造CspParameters时我被迫使用RSACryptoServiceProvider对象,因为没有其他方法来命名密钥容器。

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 Parameter对象上指定此标志时,将在行csp.FromXmlString(xmlText)处抛出类型为“指定的无效标志”的异常;

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

我对此做了很多研究,但却无法找到这些问题的答案。

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

答案

导入关键参数时(从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 Cryptography API不允许存储公钥(除非它们与私钥一起存储)。

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

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

一个假设他们的意思是BCryptImportKey,但“公钥不持久”感觉具有权威性。

对于CAPI,我找不到任何简单明了的东西。

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

钥匙容器

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

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

以上是关于使用RSACryptoServiceProvider从XML导入RSA公钥会将属性PublicOnly设置为false的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)