Unable to Add certificate to X509Store - c#

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);

Related

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?

Access X509 Store in Service Fabric application

I've put my certificates in LocalMachine/My and want to access them thusly:
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var x509Certificate2Collection = store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false);
var myCert = x509Certificate2Collection.Count > 0 ? x509Certificate2Collection[0] : null;
store.Close();
This works fine on my local cluster, but not on my standalone one.
The certificate I'm trying to access has NETWORK SERVICE in ACL for private keys.
I'd assume it's a permissions issue. Am I missing something obvious here?
How can I debug my standalone cluster?
The above code works fine. My issue was that my thumbprint was "" because I hadn't set my config overrides correctly!

Azure, App-service, create X509Certificate2 object from string

Having an App-service in Azure, and working on the AzureServiceManagementAPI, I was downloading the file that contains the managememnt certificate for each subscription.
Any how using the certificate string from the file I'm trying to create a X509Certificate2 object.
string cerStr = subscription.Attribute("ManagementCertificate").Value;
X509Certificate2 x509 = new X509Certificate2(Convert.FromBase64String(cerStr), string.Empty, X509KeyStorageFlags.MachineKeySet)
The constructor of X509Certificate2 throw an exception
Access denied.
System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32
hr) at
System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[]
rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet,
SafeCertContextHandle& pCertCtx) at
System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[]
rawData, Object password, X509KeyStorageFlags keyStorageFlags)
Since no one has answered this questions, I will try and have go at it. Please correct me if I am wrong, but the problem I think is the following line of code:
new X509Certificate2(Convert.FromBase64String(cerStr), string.Empty, X509KeyStorageFlags.MachineKeySet)
This line of code will try to add a new certificate to the certificate store of the virtual machine. All certificates used by the runtime, needs to be hosted in a store somewhere. This is not a good idea because the certificate store of the virtual machine hosting the app service is nothing that you should be storing anything in, it's part of the infrastructure which is not of your concern when you are working with app services.
What you need to do is to upload the certificate through the azure portal instead (if they are not already there). I ended up reusing a SSL certificate already in place for this purpose. When this is done, you can retreive that certificate in code. You will need to add a new App Setting under "Application Settings" key in the Azure portal for your app service, named WEBSITE_LOAD_CERTIFICATES. The value should be the thumbprint of the certificate.
To retrieve the cert, you should do something like this:
public async Task<X509Certificate2> GetCertificate(string certificateThumbprint)
{
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.OfType<X509Certificate2>()
.FirstOrDefault(x => x.Thumbprint == certificateThumbprint);
store.Close();
return cert;
}
You might be able to get thumbprint of the cert by navigating your subscription using the azure resource explorer https://resources.azure.com/
As Fredrik mentioned the issue is due to the code
X509Certificate2 x509 = new X509Certificate2(Convert.FromBase64String(cerStr), string.Empty, X509KeyStorageFlags.MachineKeySet)
In the Azure WebApp, if we try to use the certificate, we need to upload the certificate from the Azure portal. Add the WEBSITE_LOAD_CERTIFICATES with thumbprint value in the Azure WebApp application. More detail info please refer to blog.
Web application to access the certificate, snippet code from the blog
static void Main(string[] args)
{
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
// Replace below with your cert's thumbprint
“E661583E8FABEF4C0BEF694CBC41C28FB81CD870”,
false);
// Get the first cert with the thumbprint
if (certCollection.Count > 0)
{
X509Certificate2 cert = certCollection[0];
// Use certificate
Console.WriteLine(cert.FriendlyName);
}
certStore.Close();
}

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..

Add an X509 certificate to a store in code

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

Categories