Reputation: 33
My project required key recovery features where the system admin can upload RSA Key container to restore it in the event for disaster recovery
Current implementation is when admin upload the backup RSA Key container file, the app will move it to RSA Key container folder located at C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
I tried to recover the key container from server into my local machine
Key container from server :
a953858192ce652ca077837fd55e8ea2_06454689-ae14-440b-aa53-c2eaac321be6
the bold part is Server Machine ID
When the RSACryptoServiceProvider tried to access the container, it will create a new container because the key name is not contain my local machine ID, made the decryption of encrypted data from server doesn't work.
I tried to rename the machine ID to my local machine ID
a953858192ce652ca077837fd55e8ea2_fbf0b515-e8c9-450d-bc0c-4bcb55cbd342
and the RSACryptoService throw error :
"Key not valid for use in specified state."
Code implementation in 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);
}
I hope someone can enlighten me a correct way to restore RSA key container from another machine..
Upvotes: 1
Views: 720
Reputation: 33
I've figured out myself how to properly restore the key container. Instead of copy the key container directly from Server A to Server B, I export the private key from server A to server B in PEM format and do restoration programmatically.
Dependency : Org.Bouncycastle
First you need to export the private key of your existing key in the key container, you must ensure the RSACryptoServiceProvider is loaded with your intended key container info
//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);
Then export the key by using this :
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();
}
now you'll have the PEM content and save it to appropriate location :
-----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-----
Then regenerate key container in Server B based on key information in PEM file
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;
}
now you can decrypt data from Server A in Server B by loading the newly generated key container into RSACryptoServiceProvider.
Upvotes: 1