Add an X509 certificate to a store in code - c#

This code will add a x509 cer cert file into the certificate store (using System.Security.Cryptography.X509Certificates):
var filename = "Cert.cer";
var cert = new X509Certificate2(filename);
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
Where the certificate has been generated with:
makecert -r -pe -sky exchange -n "CN=Blah" Cert.cer -sv Cert.pvk
But - this will add the certificate into the "Personal" certificates of the currentuser - how can I add the certificate to a different collection of certificates - in my case I want to add to the "Trusted People" certificates for currentuser.
Thanks

var store = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser);
The First Parameter contains the enumeration for which store to use see MSDN
The Second Parameter contains the enumeration for which location to use (eg Computer, Current user) see MSDN

Related

Unable to Add certificate to X509Store

Im trying to add a certificate to a certificate store programatically
I'm using the following code to fetch a pfx file from a directory and add the certificate to the CurrentUser under My store.
The code runs without any exception but I'm not able to see the certificate added in the store.
I've tried changing CurrentUser to LocalMachine and have also tried adding under TrustedPeople but with no success.
X509Certificate2 cer = new X509Certificate2(Server.MapPath("<filepath>"), "<pswd>", X509KeyStorageFlags.MachineKeySet );
StorePermission sp =
new StorePermission(PermissionState.Unrestricted);
sp.Flags = StorePermissionFlags.AllFlags;
sp.Assert();
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed);
store.Certificates.Add(cer);
store.Close();
Try
store.Add(cer);
instead of
store.Certificates.Add(cer);

Is it possible to store RSACryptoServiceProvider in X509Certificate2 or X509Store?

I have keypair provided to me by hardware device in form of RSAParameters
which I can convert to a CryptoServiceProvider using following code and encrypt/decrypt/sign.
RSACryptoServiceProvider RSAPrivKey = new RSACryptoServiceProvider(2048) { PersistKeyInCsp = false };
// RSAParameters rsaparam = RSAPrivKey.ExportParameters(true); // for testing
RSAPrivKey.ImportParameters(rsaparam);
I'm not allowed to use file system to store the certificate and as hardware device is not available sometimes, I like to store this certificate in Machine Certificate store (Certificates -> Local Computer -> personal)
As the X509Certificate2.PrivateKey document suggests, I should be able to set private key of the certificate and save it to store like following
X509Certificate2 Cert2 = new X509Certificate2();
Cert2.PrivateKey = RSAPrivKey;
X509Store cstore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
cstore.Open(OpenFlags.MaxAllowed);
cstore.Add(Cert2);
cstore.Close();
However I get error m_safeCertContext is an invalid handle which according to this answer is happening because some properties of X509Certificate2 are not set, which is correct. I cannot assign validity date, issuer, ... to the certificate.
Question
Is it even possible to store RSACryptoServiceProvider in X509Certificate2? If yes, am I missing some steps?

Install a cert with a private key

I'm converting part of this batch command to managed code:
"%~dp0\makecert" -r -pe -n "CN=CompanyName" -ss my -sr localmachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 "%~dp0\server.cer"
This not only creates the cert, but it also installs it.
When the cert is installed it looks like this :
Now I'm trying to get the private cert installed too when using managed code.
This is what I have so far:
/// <summary>
/// Install the root authority certificate
/// </summary>
/// <param name="fileName"></param>
private static void InstallRootAuthority(string fileName)
{
var cert = new X509Certificate2(X509Certificate.CreateFromCertFile(fileName));
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
}
How can I also add the private key?
So to pull this off you will need to use a .pfx file (which I believe contains not only the cert but also the private key).
So here is what I did.
Generate the cert using MakeCert.exe like this:
"%~dp0\makecert" -r -pe -n "CN=CompanyName" -ss my -sr localmachine
-sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 "%~dp0\server.cer" -sv Server.pvk
Notice at the end, that -sv Server.pvk (That's the private key).
pvk2pfx.exe -pvk Server.pvk -spc Server.cer -pfx Server.pfx
This prompted me for a password and I selected None. Trying to include a password resulted in bad things happening.
Now in your code, adjust like this :
private static void InstallRootAuthority(string fileName)
{
var cert = new X509Certificate2(fileName,string.Empty, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
}
Excuse formatting, but stackoverflow formatting was broken at time of answering.

how to load password protected certificates from the X509Store?

I am building an ACS protected Azure WCF service that will require clients to authenticate via a certificate.
I would like the client (and the server) to load their respective password certs from the X509Store instead of from the file system.
I am using this code:
private static X509Certificate2 GetCertificate(string thumbprint)
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
thumbprint, false);
certStore.Close();
if (certCollection.Count == 0)
{
throw new System.Security.SecurityException(string.Format(CultureInfo.InvariantCulture, "No certificate was found for thumbprint {0}", thumbprint));
}
return certCollection[0];
}
Problem is, it's not loading the private key which it needs for authentication. I have tried to modify the return statement to this:
return new X509Certificate2(certCollection[0].Export(X509ContentType.Pfx, "password"));
However, this fails with a CryptographicException "The spcecified network password is incorrect".
Edit:
The .Export() method works properly if you don't pass the password argument in.
Any help on this?
When you export, the password you provide is the password you want to use for the exported file, it's not the password for the source certificate.
I'm not sure what you can do with X509Store and password-protected certs because the password should be supplied to the X509Certificate constructor and you get already-instantiated objects out of the store.
I think you can just get the raw data from the cert you want and construct a new one with the password you want. For example:
X509Certificate2 cert = new X509Certificate2(certCollection[0].GetRawCertData, password);
I would also suggest you try to use SecureString when dealing with passwords (but that's a different bag of worms...)
I used the Export without the 'password' parameter and it worked without issue.
When the cert was imported into the certificate store, I think the key has to be marked as "exportable" otherwise I don't think you can export the private key..

Decrypt with PrivateKey X.509 Certificate

I have a problem to decrypt a message usgin X.509 Certificate.
I generate my certificate with makecert with this options:
makecert -r -pe -n "CN=MyCertificate" -ss CA -sr CurrentUser -a sha1 -sky signature -cy authority -sv CA.pvk CA.cer
And the PrivateKey was "mypassword".
My problem is when I want to decrypt a message encrypt with previous certificate in c#.
I found this class http://blog.shutupandcode.net/?p=660, but in the X509Decrypt method allways the PrivateKey is null.
public static byte[] X509Decrypt(byte[] data, string certificateFile, string password)
{
// load the certificate and decrypt the specified data
using (var ss = new System.Security.SecureString())
{
foreach (var keyChar in password.ToCharArray())
ss.AppendChar(keyChar);
// load the password protected certificate file
X509Certificate2 cert = new X509Certificate2(certificateFile, ss);
using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey)
{
return rsa.Decrypt(data, true);
}
}
}
I tried passing the certificate file (.cer)
X509DecryptString(token, #"c:\CA.cer", "mypassword");
And passing the pvk file (.pvk)
X509DecryptString(token, #"c:\CA.pvk", "mypassword");
But allways have that the PrivateKey property is null.
Can anyone guide me to decrypt the message using the pvk file?
Thanks,
Jose
The certificate itself only contains the public key (+ some data), but not the private key. (It's very unlikely that the RSA private key is "mypassword". The password that protects your private key may be "mypassword", but the private key itself (more specifically the private exponent, in RSA) will be a rather long number.)
As a result (because CA.cer only contains the certificate), X509DecryptString(token, #"c:\CA.cer", "mypassword") will almost certainly not work.
X509DecryptString(token, #"c:\CA.pvk", "mypassword"); could work in principle, but you're creating a X509Certificate2 object from it, and it still needs the certificate and the private key. You should be able to load that from a PKCS#12 container (.p12/.pfx).
To create this container, you can use pvk2pfx:
pvk2pfx -spc CA.cer -pvk CA.pvk -pfx CA.pfx
(If you don't specify -pfx CA.pfx, it will launch the interactive interface, in which case you need to tick the box to export the private key.)
Then, try to decrypt using that pfx/p12 file instead.
I think you should be using "-sky exchange" to generate a public/private key pair.

Categories