Can I prevent deletion of a specific RSA key? - c#

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?

Related

(NET6+) Grant user read permission on certificate's private key

Windows stores certificate's private keys as files, and you can use mmc.exe to give users read permissions on these keys. I need a way to do that programatically in NET6.
Microsoft have marked the PrivateKey property on the X509Certificate class as obsolete (since .NET 4.6) and the correct way is to use the extension methods provided.
However, the returned RSA key class does not contain a UniqueName property which I can then use to determine the filename of the private key, and thus grant a user read permission on it.
This question Grant user permission to the private key shows how it can be achieved using the obsolete property name.
Does anyone know how this can be achieved without using the PrivateKey property?
I had some luck with this:
// input: "X509Certificate2 cert"
RSACng rsa = cert.GetRSAPrivateKey() as RSACng;
string rsaKeyName = rsa.Key.UniqueName;
if (rsaKeyName == null)
{
RSACryptoServiceProvider rsaCSP = cert.GetRSAPrivateKey() as RSACryptoServiceProvider;
rsaKeyName = rsaCSP.CspKeyContainerInfo.KeyContainerName;
}

C# | jstedfast/MimeKit | Office 365 connector with DKIM setup

DKIM is set up for a domain in Office365. A .Net application (currently MVC 4) sends Email through an O365 connector to external parties.
We'd like to sign these using DKIM as well.
I'm not quite clear about the entire process.
MimeKit's Documentation is reasonably clear. I suppose I can use any pub/priv key generator such as Putty to generate a keypair? I would then store the private key in a way that the C# application can read it into
var signer = new DkimSigner ("privatekey.pem") {
SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1,
AgentOrUserIdentifier = "#eng.example.com",
QueryMethod = "dns/txt",
};
The public key will be published as a DNS record for my domain. Unfortunately, the Office 365 documentation isn't all too clear on the exact how.
Summary Questions
What exactly goes into AgentOrUserIdentifier, if my system sends with the address application#example.org?
How exactly would I publish my generated public key to Office 365?
Any enlightening summary would be greatly appreciated, thanks.
I'll accept #jstedfast's answer (although without really understanding it).
Just in case anyone else is struggling with this, here's the complete walk-through:
Get a public/private key pair. You can use Puttygen or openssl directly, but it's easier to use (oh had I only known beforehand) sth like https://port25.com/dkim-wizard/
Specify your domain name (example.org here) and a "selector" - this could be your application name ("greatapp"). This selector will be the TXT record for the public key in DNS.
Create an additional DNS (TXT) record; leave the Office365 ones intact. Since they rotate keys regularly you want an additional record that you can control.
greatapp._domainkey.example.org IN TXT
"k=rsa\; p=here goes the stuff between -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----", so e.g.
"k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhvIwVBomj+dx2CEBbY/ZpSdnQK2Omx6ZNyHsuvC3MMJYNLQ069ajuJo5FP......."
Copy the private key to a file, or use it in your code directly. MimeKit either expects a file or a stream, so for the quick & dirty example here I'm using a string:
var mail = new MimeMessage();
mail.From.Add(new MailboxAddress("Justin Case", "justin#example.org"));
mail.To.Add(new MailboxAddress("Candy Barr", "candy#example.org"));
... subject etc
var privateKey = #"-----BEGIN RSA PRIVATE KEY-----......";
var privateKeyStream = new MemoryStream(Encoding.Default.GetBytes(privateKey));
mail.Sign(new DkimSigner(privateKeyStream, "example.org", "greatapp", DkimSignatureAlgorithm.RsaSha256), new HeaderId[] { HeaderId.From, HeaderId.Subject }, DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm.Simple);
... Connect client and send.
Thanks to jstedfast something as awesome as MailKit/MimeKit exists, don't forget to donate.
From rfc6376, section 2.6:
2.6. Agent or User Identifier (AUID)
A single identifier that refers to the agent or user on behalf of
whom the Signing Domain Identifier (SDID) has taken responsibility.
The AUID comprises a domain name and an optional <local-part>. The
domain name is the same as that used for the SDID or is a subdomain
of it. For DKIM processing, the domain name portion of the AUID has
only basic domain name semantics; any possible owner-specific
semantics are outside the scope of DKIM. It is specified in
Section 3.5.
Note that acceptable values for the AUID may be constrained via a
flag in the public-key record. (See Section 3.6.1.)

X509Certificate2 request PIN

I am able to successfully identify client certificates in a .NET thick client app, and the user is able to successfully select one.
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var fcollection = store.Certificates.Find(X509FindType.FindByApplicationPolicy, "1.3.6.1.5.5.7.3.2", true);
// other stuff where user selects one of them
Now how do I ask the user to answer the challenge (e.g. PIN in this case)?
I see there's a SignedXML.ComputeSignature() class, but it takes a byte stream, and I'm not sure where that comes from (perhaps in certificate.RawData[]?).
I'm not really as interested in getting the actual pin as I am that the card/pin match.
EDIT:
I tried using the private key from the smart card (and even encrypted from it), but I don't get asked for my PIN.
RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)certificate.PrivateKey;
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] dataToEncrypt = ByteConverter.GetBytes("Data to Encrypt");
var encryptedData = RSAEncrypt(dataToEncrypt, rsacsp.ExportParameters(false), false);
Looks like the PIN request happens when I call RSACryptoServiceProvidersa.Decrypt.
Here's example code that worked perfectly for me in a Console app:
http://blog.aggregatedintelligence.com/2010/02/encryptingdecrypting-using.html
Much simpler in ASP.NET (aside from all the IIS config hassles/mysteries ...).
If this is a smartcard, the pin prompt will happen when you try to use the private key of the certificate.
You need to use the certificate somehow, and validate the result. For example, you might use the certificate to sign something. Once that signature operation happens, the pin prompt will appear.
If you don't really need to "use" the certificate, just want to validate that it's there and the user knows the pin, then you need some sort of proof of work. The certificate could be used to sign a challenge, and a remote server could validate the signature uses a key that belongs to a trusted root. Keep in mind this is difficult to get right, such as making sure you aren't open to a replay attack, etc.

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.

Azure Table Storage Client Side Encryption WITHOUT Using KeyVault

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.

Categories

Resources