如何以编程方式创建用于加密的 X509 存储密钥?

How can I create an X509 Store key programmatically for use in encryption?

我有一项服务需要加密和解密连接文本。我不想硬编码密码,所以我决定使用证书,但我希望能够在第一次设置时将它添加到商店 UI(一个单独的配置应用程序)已打开。

我有检查现有证书的代码,如果找不到证书则添加一个新证书。

    private static void GenerateNewSecurityCertificate()
    {
        var ecdsa = ECDsa.Create(); // generate asymmetric key pair
        var request = new CertificateRequest($"cn={CertificateName}", ecdsa, HashAlgorithmName.SHA512);
        var cert = request.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(50));
        File.WriteAllBytes("c:\temp\EncryptionCert.pfx", cert.Export(X509ContentType.Pfx, _certificatePassword));
    }

    private static void AddCertificateToStore()
    {
        using (var store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine))
        {
            store.Open(OpenFlags.ReadWrite);
            using (var cert = new X509Certificate2("c:\temp\EncryptionCert.pfx", _certificatePassword,
                X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet))
            {
                store.Add(cert);
            }
        }
    }

当我尝试检索 public 密钥时,我 运行 遇到了问题

using (var store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine))
{
    store.Open(OpenFlags.ReadOnly);
    using (var cert = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateName, false)[0])
    {
        var publicKey = cert.GetRSAPublicKey();
        encryptedKey = publicKey.Encrypt(aesKey.Key, RSAEncryptionPadding.OaepSHA512);
    }
}

我在调用 cert.GetRSAPublicKey() 时返回了一个空值。有人看到我可能做错了什么吗?

更新:

我更新了GenerateNewSecurityCertificate阅读

var rsa = RSA.Create(); 
var request = new CertificateRequest($"cn={CertificateName}", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); 

我现在可以获取 public 密钥,但读取异常

"The parameter is incorrect" 
at System.Security.Cryptography.NCryptNative.EncryptData[T](SafeNCryptKeyHandle key, Byte[] data, T& paddingInfo, AsymmetricPaddingMode paddingMode, NCryptEncryptor`1 encryptor) 

我打电话给 publicKey.Encrypt

由于您显然加密了 AES 密钥,因此可以假定数据的(最大)长度为 32 字节 (AES-256)。可以重现错误消息,例如如果消息对于使用的密钥来说太大。

对于RSA,消息的长度不能超过密钥长度。事实上,它甚至更小,因为允许长度的一部分保留用于填充。对于 OAEP,这部分取决于所使用的摘要。对于 SHA512,保留部分(130 字节)太大以至于 1024 位(128 字节)的密钥是不够的,如果使用 2048 位(256 字节)的密钥,消息可能仍然有 126 字节大,请参阅 here

您使用

创建了一个 RSA 密钥
var rsa = RSA.Create(); 

它使用密钥长度的默认值 s。 RSA.Create()。此默认值还取决于 .NET 版本。在 .NET Framework 4.8 下,我的机器上创建了一个 1024 位密钥(现在太短了),在 .NET Core 3.1 下创建了一个 2048 位密钥。您可以使用 rsa.KeySize.

检查密钥长度

您的环境中生成的密钥可能对于您的用途来说太短了。最好不要依赖默认值,而是明确指定值。 Create 方法有一个重载使这成为可能,请参阅 RSA.Create(Int32)。您应该使用此重载并创建一个(至少 2048 位)密钥。

或者,理论上可以使用另一个摘要(例如 SHA256)消除该错误。除此之外,出于安全原因,如今 (12/2020) 不应使用 1024 位的密钥,长度应至少为 2048 位(参见 here)。