Azure Table Storage Client Side Encryption WITHOUT Using KeyVault - c#

I've got some sensitive information that I want to be encrypted and stored in Azure Table Storage. Honestly, from a naive approach, using the same AES key for all values probably would be sufficient as I would nowhere near approach having enough data encrypted in order for someone to do any meaningful cryptanalysis. But, I know that the best practice is to limit usage of the same symmetric key.
Recently, Microsoft released client side encryption for Azure Table Storage via Azure KeyVault. It allows you to generate an RSA key and store it in KeyVault and the client library will generate a new symmetric key for every row in table storage and it encrypts the symmetric key with your RSA key. This is perfect because there is no way to do differential cryptanalysis on the ciphertext since they all used different keys. It is especially nice because their library does all the plumbing, all you have to do is grab your RSA key from KeyVault, decorate your designated properties with the EncryptPropertyAttribute and it handles everything else.
Therein lies the rub... I personally find KeyVault kind of obtuse to use and manage. You have to use powershell to set up oauth authentication between your app and keyvault and it looks like a tremendous amount of overhead for storing a single RSA key. If we have hundreds of keys to store, I can imagine it would be much more useful.
Is there any way to use all of Microsoft's client side encryption code without storing the RsaKey in the KeyVault?

It took me a while to find it, but yes, you can store your RSA key outside of KeyVault. You just need to use the RsaKey constructor overload that takes in an RSACryptoServiceProvider that you grab from wherever you deem prudent. I grab mine out of my web.config. However, I make sure that my production RsaCsp is not store in source control and I add it directly in the Azure Web App configuration screen.
IKey tableStorageKey = GetTableStorageKey()
_tableRequestOptions = new TableRequestOptions
{
EncryptionPolicy = new TableEncryptionPolicy(tableStorageKey, null)
};
...
private IKey GetTableStorageKey()
{
using (var rsaCsp = new RSACryptoServiceProvider(2048))
{
try
{
//it doesn't really matter where you get your RsaCsp from, I have mine in my webconfig
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
XmlElement node = doc.SelectSingleNode("/configuration/MyTableStorageRsaKey") as XmlElement;
rsaCsp.FromXmlString(node.OuterXml);
return new RsaKey("MyTableStorageRsaKey", rsaCsp);
}
finally
{
rsaCsp.PersistKeyInCsp = false;
}
}
}

In Microsoft.Azure.KeyVault.Cryptography, there is a change in RsaKey constructor.
Now it does not import key from RSACryptoServiceProvider, but uses it directly, and disposes it in Dispose() method. So usage will change to:
public RsaKey GetFromXmlString(string xmlString)
{
try
{
var rsaCsp = new RSACryptoServiceProvider(2048, new CspParameters() { KeyContainerName = "MyTableStorageRsaKey" });
rsaCsp.FromXmlString(xmlString);
rsaCsp.PersistKeyInCsp = false;
return new RsaKey("MyTableStorageRsaKey", rsaCsp);
}
catch (Exception ex)
{
throw new InvalidOperationException("Invalid rsa key xmlString provided", ex);
}
}
Notice that instance of RSACryptoServiceProvider is not disposed.
Also notice that RsaKey is IDisposable.

Related

Can I prevent deletion of a specific RSA key?

I am making a Windows Service which at certain points in its execution requires logging into a server using a username and password so I have been faced with the security issue of where to store these login details.
Initially I was going to use the Windows Credential Manager, but due to the user access of Windows Services (I need to use the Local System account) it was not the best option. I have settled on storing an encryption key by using an RSACryptoServiceProvider, like so:
using rsa = new RSACryptoServiceProvider(new CspParameters
{
Flags = CspProviderFlags.UseMachineKeyStore,
KeyContainerName = "UniqueKeyId"
});
which I can later use to decrypt the usernames and passwords I store in their encrypted, base64 forms, like so:
"MyLoginInfo":
{
"Username" : "LettWH4VMbvEG/OUBXGLluyceEG8Gon1fOytQ0IoKys18KSCfz6lc8fVO6XxBJtnSzjR23OAoE9TxG9lIqSRdpDwGGaTLGa3bpTGxzKlq+3OoLRo4Hf+9VEn/GO/UEZnzAmalPLErQO87krPmJuWCDqTthtPmi2Kh9jbcavz7Ss=",
"Password" : "Pnq3KoPip2WHpnDQivc8b0VOMzFn0W/OtSIVSELSE8SNqJSiRHa/6Yt47ndpyZRe6hTSvz3RZeLxaeQ+X1QIm1VRxESzbgz3ZFFzxy6F2ZJAikygWBhzNnu3jywG6u1C7amN7IO9/dHu2T6Jw8n6U1MTYgN2bV4lmGNHJ2bwAnA="
}
So my question is, what is to stop this from being called by some external process:
using rsa = new RSACryptoServiceProvider(new CspParameters
{
Flags = CspProviderFlags.UseMachineKeyStore,
KeyContainerName = "UniqueKeyId"
});
rsa.PersistKeyInCsp = false;
rsa.Clear();
And my key being eradicated, making decryption impossible at a later time? I'm not sure it would likely ever happen but the possibility is bothersome. Should I make my key container name extremely unique, like a GUID or hash of some kind? No one would be able to "browse" through all the available keys, would they?

RSA Key needs to be regenerated

Need an assist on an issue I am having with an RSA key. I generate a RSA key through C# code on a server with a key container name I define such as "CustKey". I get back the key XML string just fine and I can connect an application to it using that key. I have an API that is generating the key through an endpoint. If I redeploy the API my application that uses the public key stops working on decoding it. I have to run again the same code to create the key and the encrypted string is exactly the same every time I regenerate it and then the consuming app works fine again. It seems to happen when either the app pool recycles but I have it turned off recycling but the publish I stop and start the IIS API site. Any idea how to keep the key working and why I have to regenerate it but it uses the same key encrypted value each time.
Thanks for any info.
Some examples of your code would greatly help to understand what's going on. I can only speculate at this point that you're trying to decode your message with public key instead of private one.
Firstly, you have to have two paired keys:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string publicKey = rsa.ToXmlString(false);
string privateKey = rsa.ToXmlString(true);
Secondly, you have to give your public key to all people who wants to send a secret message to you:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(publicKey);
string secretMessage = Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes(openMessage), false));
And finally, when you recieve a secret message only you can decode it because only you have a private key which is paired to public one:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(privateKey);
string revealedMessage = Encoding.UTF8.GetString(rsa.Decrypt(Convert.FromBase64String(secretMessage), false));

How to handle key rotations in azure key vault

I have to implement key rotations in my application. I have some idea how to do that but I am not sure if everything is OK with that solution.
Ok, lets start. I have couple places in my application where I use the KeyVaultClient(Azure KeyVault client) for decrypting purpose. It's work preety well. In my application are some places where KeyVaultClient is used for encrypting. For now(still development phase) I am using hardcoded params(vaultBaseUrl, keyName, keyVersion). But I want to go further and move this params to app.config file.
And here the question begun what to do with keyVersion variable(rest of them I think I can easy store in app.config file, isn't it?) I have couple ideas:
For encrypting:
I could store current keyVersion in app.config, and use this value each time I will encrypt data.
I could read from KeyVaultClients all Keys(GetKeysAsync), next filter them by active flag and order by expiration date. Finally use the newest one.
For decrypting:
I could store keyVersion used for encryption in encrypted data(the encryption result I am converting to Base64String). I mean I could add to string result the 32 characters(keyVersion) prefix.
No more ideas, maybe using the keyVersion from app.config, but it creates problem with keys rotations.
Maybe there is some tool/library that handle this all work for me? :p
For now, new keys are inserted manually, by administrator. In next phases I am going to implement scheduled task for that.
Maybe there is some tool/library that handle this all work for me?
As you mentioned that you need to encrypt key version is consistent with decrypting key version. It is better that if you could share your scenario. Take Encrypt blob for example. If the blob is encrypted,it will have a Metadata["encryptiondata"] with keyId in it. In your case, maybe you also could add a property with keyId for the object. When you try to decrypt then you could get the keyId from the object.
For now, new keys are inserted manually, by administrator. In next phases I am going to implement scheduled task for that.
If you want to create keys, you could do that with following this code sample in WebJob or Azure function.
static string _clientId= "xxxxxxxxxxxxx";
static string _clientSecret = "xxxxxxxxxxxxxxxx";
static string _tenantId = "xxxxxxxxxxxxxxxx";
public static async Task<string> GetAccessToken(string azureTenantId, string azureAppId, string azureSecretKey)
{
var context = new AuthenticationContext("https://login.windows.net/" + _tenantId);
ClientCredential clientCredential = new ClientCredential(_clientId, _clientSecret);
var tokenResponse = await context.AcquireTokenAsync("https://vault.azure.net", clientCredential);
var accessToken = tokenResponse.AccessToken;
return accessToken;
}
var kv = new KeyVaultClient(GetAccessToken);
var result = kv.CreateKeyAsync(vault,keyName,keyType).Result;

EnvelopedCms decryption does not work with Azure Key Vault

I've been struggeling with this for days now and RFC 2315 is a bit hard to understand.
I'm trying to implement my own version of EnvelopedCms.Decrypt(), so that I can use the certificate operations of Azure Key Vault to UnwrapKey and/or Decrypt a PKCS#7 message (CMS Object) in a correct way. I use EnevelopedCms in .Net to Decode the message, then I try to Decrypt the EnvelopedCms.ContentInfo.Content.
This is what I try to do;
public static async Task<byte[]> DecryptCustom(string certificateId, string encryptedBase64Content)
{
var bytes = Convert.FromBase64String(encryptedBase64Content);
var contentInfo = new ContentInfo(bytes);
var envelopedCms = new EnvelopedCms(contentInfo);
envelopedCms.Decode(bytes);
// envelopedCms.Decrypt() <-- no go. Can't extract certificate from Key Vault
// My (naive) attempt to decrypt CMS content using Azure Key Vault certificates
byte[] decryptedContent;
using (var client = new KeyVaultClient(GetKeyVaultToken))
{
var decryptionresult = await client.DecryptAsync(GetKeyUrl(certificateId), "RSA1_5", envelopedCms.ContentInfo.Content);
decryptedContent = decryptionresult.Result;
}
return decryptedContent;
}
I was hoping it could be that easy, but it gives me the following error;
Unable to decrypt specified value with this key.
I read something about octets in RFC 2315, so maybe the stream (byte-array) needs some reordering before I decrypt. Do I need to unwrap some symmetric key to decrypt the real payload? I'm on thin ice here.
I'm not a cryptography professional so I might have missed something obvious, too. I was hoping someone knew what to do in this case as I really want to keep my certificates inside the Key Vault (HSM)
CMS envelope contents are encrypted using a session key, and this key is encrypted with each recipients (there can be many) public key before transmission.
What you need is to extract your recipient's encrypted session key, and unwrap it with the private key stored in key vault. I'm not near Visual Studio right now, but here is the pseudocode:
// Extract the first (and often only) receiver's encrypted session key
var key = envelopedCms.Receivers[0].EncryptionKey;
// Unwrap the sessionKey using the receiver's private key stored in key vault:
var sessionKey = (await keyVaultClient.Unwrap(uri, "certificatename", key)).Result;
Finally, using the sessionKey, you can decrypt the envelope contents (ContentInfo.Content). The encryption type is specified in the envelope's encryption algorithm-property.

How to store information like passwords encrypted but not in hash

I need to store "password like information" in a database field. I would like it to be encrypted but I need to decrypt it before using it. So I can not use a Hash/Salt solution.
Granted if an attacker made it that far into the database it may be too far gone but I figure this would at least stop the mistaken dump of the data.
How to encrypt a value store it into the database and decrypt the same value for use later?
Hashing is not an option (I use it on other parts actually).
Where to store the private key? Users would not supply anything.
This a C# solution so .NET specific stuff would be great. My question is very similar but I am looking for a .net based solution: Two-way encryption: I need to store passwords that can be retrieved
EDIT:
Hogan pretty much answered my question. I found examples out there and they ranged from very complicated to rather simple. It looks like AES is still good so I will be using that method. thank you for all your help.
One solution that does not involve private keys is using DPAPI.
You can use it from .NET via the ProtectedData class.
Here is an example:
public void Test()
{
var password = "somepassword";
var encrypted_password = EncryptPassword(password);
var decrypted_password = DecryptPassword(encrypted_password);
}
public string EncryptPassword(string password)
{
var data = Encoding.UTF8.GetBytes(password);
var encrypted_data = ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encrypted_data);
}
public string DecryptPassword(string encrypted_password)
{
var encrypted_data = Convert.FromBase64String(encrypted_password);
var data = ProtectedData.Unprotect(encrypted_data, null, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(data);
}
Please note that DPAPI in this case depends on the current logged in user account. If you encrypt the password when your application is running as User1, then you can only decrypt the password running under the same user account. Please note that if you change the windows password for User1 in an incorrect way, then you will lose the ability to decrypt the password. See this question for details.
If you don't want use DPAPI, and prefer to have a private key. Then the best place to store such private key is in the user's key store. However, in order to store a private key in the local user store, you need to have a certificate for it. You can create a self signed certificate and store it with its corresponding private key into the local user certificate store.
You can access the user store in code using the X509Store class. You can use it to find the certificate (which is in C# a X509Certificate2 class) that you want to use and then use it to do encryption/decryption.
See this and this for more details.

Categories

Resources