How to check if a key already exists in container? - c#

I'm building an application for secure messaging between multiple clients. In order to achieve that, I encrypt the message with AES, then I encrypt the AES key with the recipients public RSA key, then I send these two components (RSA-encrypted AES key and AES-encrypted message) to the recipient. The whole process works well and without any errors.
Now I ran into a problem and I'm wondering what would be the best practice: In order to persist the private and public key of one participent, I need to store the key pair. Saving it somewhere as an XML file is possible, but obviously not an option. So decided to use a key container as described here. Implementing the use of the container is quite easy, but how can I
Check if a specified container already exists?
Check if the key size matches a given value?
As far as I can see this is not possible, because the RSACryptoServiceProvider generates a new key if the container does not exist - without telling so. But I need to know if there is a previously stored key pair available or if a new one is created.
How can I fix that? Or is this a completely wrong approach?

Try this:
public static bool DoesKeyExists(string containerName)
{
var cspParams = new CspParameters
{
Flags = CspProviderFlags.UseExistingKey,
KeyContainerName = containerName
};
try
{
var provider = new RSACryptoServiceProvider(cspParams);
}
catch (Exception e)
{
return false;
}
return true;
}
similiar question: How to check if RSA key container exists in .NET

Related

How to check is X509Certificate2 exportable or not

var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(
X509FindType.FindByThumbprint, thumbprint, false);
X509Certificate2 cert = certificates[0];
Now we have X509Certificate2 instance. How to check exportable private key or not? (preferably without trying to export explicitly)
Another approach I found here: How to determine whether an X509Certificate2 is exportable
X509Certificate2.PrivateKey Gets the AsymmetricAlgorithm object that represents the private key associated with a certificate.
The RSACryptoServiceProvider class is a AsymmetricAlgorithm
Then get the RSACryptoServiceProvider.CspKeyContainerInfo which is a CspKeyContainerInfo object that has a Exportable property that: Gets a value indicating whether a key can be exported from a key container.
Update: works. So, if you use RSA certificates, it is acceptable approach.
Looking at the reference source, the implementation of the Export method makes the following checks:
That the X509ContentType parameter is Cert, SerializedCert or Pfx.
When the content type is Pfx it makes a key container permission demand for both Export and Open permissions.
Beyond this, everything else happens via internal calls to the CLR, so it's much harder to say what demands are made of the caller. I can't observe a check in the source which tests for the exportable flag.
This is a scenario where I would suggest you attempt to perform the export and handle any exceptions as feedback; you cannot reasonably predict the outcome of the call with the information exposed by the certificate.
Use this method:
public static bool CheckCertificateIsExportable(X509Certificate2 certForCheck, X509ContentType certType)
{
try
{
certForCheck.Export(certType);
return true;
}
catch
{
return false;
}
}
How to use:
if (CheckCertificateIsExportable(certForCheck, X509ContentType.Pkcs12))
{
// Do...
}

Implementing Hybrid Encryption?

I already have an asymmetric algorithm implemented in an MVC C# Application, however I would like to modify the encryption method so that I make use of both symmetric and asymmetric encryption (AKA Hybrid encryption). Any idea how I can do this?
Asymmetric encrypt:
public string AsymmEncrypt(int accId, string input, string publickey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
publickey = new UserRepository().PublicKeyByAccountId(accId);
cspParams.KeyContainerName = publickey;
myAlg = new RSACryptoServiceProvider(cspParams);
byte[] cipher = myAlg.Encrypt(UTF8Encoding.UTF8.GetBytes(input), true);
return Convert.ToBase64String(cipher);
}
Asymmetric decrypt:
public string AsymmDecrypt(int accId, string input, string privatekey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
privatekey = new UserRepository().PrivateKeyByAccountId(accId);
byte[] cipher = myAlg.Decrypt(Convert.FromBase64String(input), true);
return UTF8Encoding.UTF8.GetString(cipher);
}
You should probably not try to reinvent the wheel here. The System.Security.Cryptography namespace in .net alrady provides a large array of cryptography functionality that is quite well vetted. Don't try to use your own Asymmetric functions to accomplish this.
If you want to do private key distribution through public key encryption, you should use something like RSAPKCS1KeyExchangeFormatter or maybe even RSAOAEPKeyExchangeFormatter if you have the flexibility to support PKCS#1v2
I would suggest reading how SSL or OpenPGP are implemented.
I'm not sure what part you are struggling with.
In short, the asymmetric algorithm is used for symmetric key exchange.
The symmetric algorithm is used for the bulk data (stream/block) crypto. You won't get it done with simply modifying your 2 functions, you will need to implement a handshake and key exchange.
Since you have an MVC.NET app, you can host it within a web server and gain HTTPS/SSL transport. You can also do the same with WCF. Any reason why aren't using what is provided by the underlying transport? You can even configure your application (web.config) to require client certificates.
PS: I agree about not re-inventing the wheel, even Microsoft's article that Erik linked to provides a warning about it.
Caution We recommend that you do not attempt to create your own key exchange method from the basic functionality provided, because many details of the operation must be performed carefully in order for the key exchange to be successful.

Best practice for encrypting on the client

I am currently using a web API that allows an "Encryption" option.
I can setup my account to have a "shared key", and using this key i should encrypt all data on the client before submitting to the server.
Details from their website:
Encryption Algorithm: DES
Block Mode: ECB
Padding: PKCS7 or PKCS5
(they are interchangeable)
"Shared key" in this meaning i believe is a symmetric algorithm - same key used to decrypt/encrypt, although i may be wrong on this one.
I would like to know what is the best practice of handling this scenario on the client side?
If my application's logic should be using this key to encrypt data, how is it safe from a hacker ?
Note that my app is written in C#, meaning it can be decompiled practically for free.
Unless your key is compromised, then the transmission of your data is safe – anyone eavesdropping on your client–server connection would not be able to decrypt your data unless they have your key.
Your main challenge lies in the secure storage of the key locally on both the client and the server. For this end, I would suggest looking into the Windows Data Protection API (DPAPI) exposed through the ProtectedData class in .NET.
If shared key means public key then you are, most probaly, using one of the algorithms known as asymmetric encryption. This way you are safe to hacker since public key can't be used to decrypt data.
If it's symmetric then all depends on how secure key is. You can store it separately from a program (so user can store it securely on a flash drive). So each user must have it's own key, it's not possible to use one symmetric key for all.
In this manner, client will encrypt the data with different key and server will decrypt with different key. This is called asymmetric encryption/decryption.
The .NET Framework provides the RSACryptoServiceProvider and DSACryptoServiceProvider classes for asymmetric encryption. These classes create a public/private key pair when you use the default constructor to create a new instance. Asymmetric keys can be either stored for use in multiple sessions or generated for one session only. While the public key can be made generally available, the private key should be closely guarded.
For example [VB.NET]:
Dim cspParam as CspParameters = new CspParameters()
cspParam.Flags = CspProviderFlags.UseMachineKeyStore
Dim RSA As System.Security.Cryptography.RSACryptoServiceProvider
= New System.Security.Cryptography.RSACryptoServiceProvider(cspParam)
The key information from the cspParam object above can be saved via:
Dim publicKey as String = RSA.ToXmlString(False) ' gets the public key
Dim privateKey as String = RSA.ToXmlString(True) ' gets the private key
The above methods enable you to convert the public and / or private keys to Xml Strings.
And of course, as you would guess, there is a corresponding FromXmlString method to get them back.
So to encrypt some data with the Public key. The no-parameter constructor is used as we are loading our keys from XML and
do not need to create a new cspParams object:
Dim str as String = "HelloThere"
Dim RSA2 As RSACryptoServiceProvider = New RSACryptoServiceProvider()
' ---Load the private key---
RSA2.FromXmlString(privateKey)
Dim EncryptedStrAsByt() As Byte =RSA2.Encrypt(System.Text.Encoding.Unicode.GetBytes(str),False)
Dim EncryptedStrAsString = System.Text.Encoding.Unicode.GetString(EncryptedStrAsByt)
and as a "proof of concept", to DECRYPT the same data, but now using the Public key:
Dim RSA3 As RSACryptoServiceProvider = New RSACryptoServiceProvider(cspParam)
'---Load the Public key---
RSA3.FromXmlString(publicKey)
Dim DecryptedStrAsByt() As Byte =RSA3.Decrypt(System.Text.Encoding.Unicode.GetBytes(EncryptedStrAsString), False)
Dim DecryptedStrAsString = System.Text.Encoding.Unicode.GetString(DecryptedStrAsByt)

Get Apple Keychain to recognize Bouncy Castle .NET created PKCS12 (.p12) store

Our organization manages a stable of iOS applications for multiple clients, which means dealing with a lot of different developer identity certificates and push notification certificates.
I have had success with the Bouncy Castle C# Crypto API in simplifying management of the certificates and private keys for push notifications, essentially eliminating the need for the Keychain for all our push notification certificates.
I would like to extend this to the developer identity certificates. The goal would be to store all the private key and certificate information in the database for each developer identity. Then when a new developer or build machine needs to be provisioned, server side code could wrap all of the certificates and private keys into one p12 archive with one password that could be imported into the target Mac's Keychain.
Unfortunately, the Mac Keychain doesn't like the p12 files I'm generating. This is annoying since I can successfully import these files into the Windows certificate manager just fine.
The code I'm using (the important parts) looks like this:
private byte[] GetP12Bytes(List<DevIdentity> identities, string password)
{
Pkcs12Store store = new Pkcs12Store();
foreach(DevIdentity ident in identities)
{
// Easiest to create a Bouncy Castle cert by converting from .NET
var dotNetCert = new X509Certificate2(ident.CertificateBytes);
// This method (not shown) parses the CN= attribute out of the cert's distinguished name
string friendlyName = GetFriendlyName(dotNetCert.Subject);
// Now reconstitute the private key from saved value strings
BigInteger modulus = new BigInteger(ident.PrivateKey.Modulus);
BigInteger publicExponent = new BigInteger(ident.PrivateKey.PublicExponent);
BigInteger privateExponent = new BigInteger(ident.PrivateKey.Exponent);
BigInteger p = new BigInteger(ident.PrivateKey.P);
BigInteger q = new BigInteger(ident.PrivateKey.Q);
BigInteger dP = new BigInteger(ident.PrivateKey.DP);
BigInteger dQ = new BigInteger(ident.PrivateKey.DQ);
BigInteger qInv = new BigInteger(ident.PrivateKey.QInv);
RsaKeyParameters kp = new RsaPrivateCrtKeyParameters(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv);
AsymmetricKeyEntry privateKey = new AsymmetricKeyEntry(kp);
// Now let's convert to a Bouncy Castle cert and wrap it for packaging
Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(dotNetCert);
X509CertificateEntry certEntry = new X509CertificateEntry(cert);
// Set the private key and certificate into the store
store.SetCertificateEntry(friendlyName, certEntry);
store.SetKeyEntry(ident.PrivateKeyName, privateKey, new X509CertificateEntry[] { certEntry });
}
using (MemoryStream ms = new MemoryStream())
{
store.Save(ms, password.ToCharArray(), new SecureRandom());
ms.Flush();
byte[] p12Bytes = ms.ToArray();
return p12Bytes;
}
}
Like I said, this works great for import on Windows, but fails with a very generic error when importing into the Mac Keychain.
There is one major difference I can see when loading a Keychain-generated p12 and my own generated p12 file, but I do not know if this is the cause.
If I load the Mac Keychain generated p12 into a Bouncy Castle PKCS12Store, and then examine the keys, on the Keychain p12, both the certificate and the private key have an attribute with the key "1.2.840.113549.1.9.21" with equivalent values (a DerOctetString with value #af8a1d6891efeb32756c12b7bdd96b5ec673e11e).
If I do the same to my generated p12 file, the private key contains the "1.2.840.113549.1.9.21" attribute, but the Certificate does not.
If I Google "1.2.840.113549.1.9.21", I find out that this OID means PKCS_12_LOCAL_KEY_ID . My only theory is that the Keychain relies on this to match up the certificate and private key, and that my generated file does not have this, so it fails.
However, I've tried adding these values to a Hashtable and then using the CertificateEntry constructor that takes the attribute hashtable. If I do that, and then save the bytes, and then reload the bytes, that attribute is again missing.
So I'm flummoxed. Maybe this attribute is a glitch in the Bouncy Castle API? Maybe there's something I'm doing wrong. Maybe the Keychain has ridiculous non-standard requirements for incoming p12 files. In any case, any help that could be provided would be greatly appreciated.
BouncyCastle's Pkcs12Store takes care of setting both the Friendly Name and Local Key ID attributes for you (or at least it does so in the 1.7 release, circa April 2011). My guess is that you must have used an older version where this didn't work.
Here's how I'm saving an iPhone Developer identity to a Pkcs12Store instance (extra stuff and security omitted):
var store = new Pkcs12Store();
// pairs is IEnumerable<Tuple<X509Certificate, AsymmetricKeyParameter>>
foreach (var pair in pairs)
{
var cn = pair.Item1.SubjectDN
.GetValueList(X509Name.CN).OfType<string>().Single();
var certEntry = new X509CertificateEntry(pair.Item1);
store.SetCertificateEntry(cn, certEntry);
var keyEntry = new AsymmetricKeyEntry(pair.Item2);
store.SetKeyEntry("Developer Name", keyEntry, new[] { certEntry });
}
store.Save(stream, string.Empty.ToArray(), new SecureRandom());
Importing the store in Keychain Access.app on OS X 10.7 correctly places the certificate and private key in the keychain and places the certificate within the private key in the UI, as with a certificate and key generated by Keychain Access itself.
On a side note, it seems that Pkcs12Store uses the public key of the certificate to generate the value of the LocalKeyId attribute shared by the certificate and key entries.
You can see the relevant section of the Pkcs12Store source here.

How do I decrypt RSA data in C#.NET appropriately?

My server creates a RSACryptoServiceProvider and exports its parameters to a variable (RSAKeyInfo).
Then, the public key is sent to the client, and the client encrypts something with that public key.
Now, I need to be able to decrypt this very data when sent back to the server - hence why RSA is useful in my case.
However, I get a "Bad Data" exception when trying to recreate a RSACryptoServiceProvider with imported parameters from the first RSACryptoServiceProvider created previously.
... Code might be clearer.
Creating the crypto:
class Cryptograph
{
public Cryptograph()
{
this.RSAKeyInfo = new RSACryptoServiceProvider(2048, new CspParameters(1)).ExportParameters(true);
}
}
Accessing it later for decryption:
byte[] encrypted = ...;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(this.Cryptograph.RSAKeyInfo);
byte[] decrypted = rsa.Decrypt(encrypted, false);
Console.WriteLine(Utilities.ByteArrayToHexString(decrypted));
I get the "Bad Data" exception at this line:
byte[] decrypted = rsa.Decrypt(encrypted, false);
What am I doing wrong? How can I do it properly?
Thank you :)
P.S.: Please don't send MSDN or obvious Google results links, I've read all these pages and still can't get it to work.
When something is encrypted with a public key, you need to use the private key for the decryption. I don't see where you are using the private key for decryption.
I realize you have already read this, but you may want to read the Encrypt page and this Decrypt page, and make certain that you are following the steps:
http://msdn.microsoft.com/en-us/library/te15te69.aspx
Unless you are encrypting very short messages, such as a password, RSA encryption should generally be used for encrypting a symmetric key, which is faster to encrypt/decrypt longer messages.
The size of what you can encrypt with a public key is tied to the length of the key.
I needed an encryption/decryption that used no padding, and C#.NET doesn't provide it by default. OpenSSL.NET will do the job, however, I'm stuck while trying to use it. (See this question if you want to help me make it work). :(

Categories