首页 > 解决方案 > 将 RSA 密钥容器从服务器 A 移动到服务器 B

问题描述

我的项目需要密钥恢复功能,系统管理员可以上传 RSA 密钥容器以在灾难恢复时恢复它

当前的实现是当管理员上传备份的 RSA 密钥容器文件时,应用程序会将其移动到位于C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys的 RSA 密钥容器文件夹

我试图将密钥容器从服务器恢复到我的本地机器

来自服务器的密钥容器

a953858192ce652ca077837fd55e8ea2_ 06454689-ae14-440b-aa53-c2eaac321be6

粗体部分是服务器机器 ID

当 RSACryptoServiceProvider 尝试访问容器时,它会创建一个新容器,因为密钥名称不包含我的本地机器 ID,使得从服务器解密加密数据不起作用。

我试图将机器 ID 重命名为我的本地机器 ID

a953858192ce652ca077837fd55e8ea2_fbf0b515 -e8c9-450d-bc0c-4bcb55cbd342

和 RSACryptoService 抛出错误:

"Key not valid for use in specified state."

C# 中的代码实现:

try{
    // Create the CspParameters object and set the key container name used to store the RSA key pair.
    var parameters = new CspParameters {KeyContainerName = containerName, Flags = UseMachineKeyStore};

    // Create a new instance of RSACryptoServiceProvider that accesses
    // the key container Key Container Name.
    using var rsaCryptoServiceProvider = new RSACryptoServiceProvider(parameters); // error thrown "Key not valid for use in specified state."
    try
    {
        var keyContainerBlob = rsaCryptoServiceProvider.ExportCspBlob(true);
        using (var rsa = System.Security.Cryptography.RSA.Create())
        {
            rsa.KeySize = CryptoCommonHeap.RSAEncryptionKeySize;

            rsaCryptoServiceProvider.ImportCspBlob(keyContainerBlob);
            var privateKeyParameters = rsaCryptoServiceProvider.ExportParameters(true).ToPrivateKeyParameters();

            var privateKeyParametersJson = JsonConvert.SerializeObject(privateKeyParameters);

            PrivateKeyParametersJson = privateKeyParametersJson;
        }
     }
     finally
     {
        // Setting This If Do Not Want To Store The File Persistently
        //rsaCryptoServiceProvider.PersistKeyInCsp = false;
     }
}
catch (Exception exception)
{
    LogErrorToDatabase(ModuleName, "GenerateKeyAndSaveInKeyStore", exception);
}

我希望有人能告诉我从另一台机器恢复 RSA 密钥容器的正确方法..

标签: c#asp.netrsacryptoserviceprovider

解决方案


我已经弄清楚了如何正确恢复密钥容器。我没有将密钥容器直接从服务器 A 复制到服务器 B,而是以 PEM 格式将私钥从服务器 A 导出到服务器 B,并以编程方式进行恢复。

依赖:Org.Bouncycastle

首先,您需要在密钥容器中导出现有密钥的私钥,您必须确保 RSACryptoServiceProvider 已加载您想要的密钥容器信息

//Create the CspParameters object and set the key container name used to store the RSA key pair.
var parameters = new CspParameters {KeyContainerName = containerName, Flags = UseMachineKeyStore};

// Create a new instance of RSACryptoServiceProvider that accesses
// the key container Key Container Name.
using var rsaCryptoServiceProvider = new RSACryptoServiceProvider(parameters);

然后使用以下方法导出密钥:

private string ExportKey(RSACryptoServiceProvider rsa) {
   var writer = new StringWriter();
   var pemWriter = new PemWriter(writer);
   
   var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
   pemWriter.WriteObject(rsaKeyPair.Private);
   return writer.ToString();
}

现在您将拥有 PEM 内容并将其保存到适当的位置:

-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQDCmI8XFUlZUwoGXl42GNzY2o6exeA51/7U0UF4u5+AAbS+h3xD
Pk1BQ5rlzwOu+a2SrbNnGlH5j/6n+kQqLcBdwVdHAF6CFaPmKf7xUEqKwo2RCoG9
zNYB5gc4youdppr4K7uLDQoVvM9xUVi09n2zg3KSigLpX3WM5k4lJWOiOQIDAQAB
AoGBAL6X58ZHDhFT+MSmFwZLMbufzQKLcoOVH73XupWCxsT8ZsgaMUY3NjmO+p7N
NKFjYHMCeG2qZNHXDCgAQlVBfF9fvA3SulymyYoHEAGY1ghAnky7PjuESYmCFDes
6BlyMBfjNtAPkvSA/VZi00VOuCl7Vg4FJLOesmZzHdoaflIlAkEA+W5NU3l+z5+d
rZkw86v+ZvmuDNv77Bh5DZJ1SjQ6uiWCV7LmMOc6eOETmV0d25UkBdAU4KpfRFXg
zcEh548t9wJBAMe4jVQFMjv2L35Kr0jsSEi+O3OPNn7UTNYWlCwv0FnzB3YBMFvA
ULDYmtduaBqchzqqleWrOGK8dOeiUyj+ZU8CQQCsMlX31tyRAaSdgDCnSIntFVnv
Tr9wksSfdgi7Haudbt+5I6x+/mMDqH8bVYmTWjbwPGLtZzE1wAPeiAKcFeCpAkEA
hW+OLRaTq3Ad5xjq56PF36QJgHmshSw+ccMAGE2RvKcc0wCUWJiy0JTHTyvarfzq
dI3IPHwa3gzfZmsTeI4PDQJBAI+LvqZzxzwf01DWgDqiJzKwt+bejtdfnPqQhjRD
rcZcM550Iwy0PCdrRTswDbloNhCfcyi1HXIvZTydMXzOueU=
-----END RSA PRIVATE KEY-----

然后根据 PEM 文件中的密钥信息在 Server B 中重新生成密钥容器

public bool PrivateKeyRecovery(string pem)
{
    try
    {
        //read key info
        PemReader pr = new PemReader(new StringReader(pem));
        var paramObject = pr.ReadObject();
        AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)paramObject;
        RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);

        //prepare key name and csp parameters
        var keyContainerName = "RestoredContainer" + DateTime.Now.ToString("yyyyMddhmm");
        var parameters = new CspParameters { KeyContainerName = keyContainerName, Flags = UseMachineKeyStore };

        //import csp parameters into new instance of RSACryptoServiceProvider
        RSACryptoServiceProvider csp = new RSACryptoServiceProvider(parameters);
        csp.ImportParameters(rsaParams);

        //exporting new key container
        var keyContainerBlob = csp.ExportCspBlob(true);
        csp.ImportCspBlob(keyContainerBlob);

        
    }
    catch (Exception exception)
    {
        //your error handling
    }

    return false;
}

现在您可以通过将新生成的密钥容器加载到 RSACryptoServiceProvider 中来解密来自服务器 B 中的服务器 A 的数据。


推荐阅读