Keyset does not exist (attempting to sign executable from web service) - c#

See this: X509Certificate - Keyset does not exist
I am attempting to sign a certificate using a web service on IIS 8. I thought it would be as simple as installing a PFX on a server, and then the web service could simply use that certificate (from the store) and sign an executable (these executables are created on demand, and signed by our service, at least that's the idea behind it).
I've exhaused all of the options linked to the above question, I've navigated to C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys, and added EVERYONE to the files to see if I can narrow down that it is (in fact) a permissions issue. I've attempted running MMC and added the snap-in for service\computer accounts, I've ensure that upon installing the certificate it was marked as exportable, I've even attempted to add, in the web service itself, code which would basically add the certificate to the store itself: i.e.
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
Any ideas? Is there another way that I could determine (for sure) if it is actually a permissions issue? Is there no way to sign a certificate without having the certificate in the machine's store (i.e. signtool.exe allows it).
Also, for reference, I used the class found here: Has anyone got any code to call SignerSignEx from C#? for signing the certificate. I've scoured the internet and most of the solutions are very much the same: permissions on the GUID cert files themselves. Any help on this would be greatly appreciated.

Related

Adding a <publisher> to an Excel add-in project

I recently had to take over an Excel plugin project and one of the issue I'm getting is that a customer can't use the plugin because their Trust center only allows COM add-ins signed by Trusted publishers. (And they are not able to disable it)
I'm quite new and don't really understand this signing issue so I self-signed the code.
But the add-in has its publisher set to None. Here's how it's showing.
I believe that's not what I need to do, right? As a self signing will not be trusted by the customer Trust Center.
I saw this post but I was quite unsure about what was going on.
Does that mean there is no possible workaround or am I getting this wrong?
One thing I was wondering but I needed some help over is that if the add-in needs to be signed by a Trusted publisher.. Would that do the trick if the client is generating .pfx and I use it to sign the add-in?
AFAIK, there are no "tricks" you can do from the DEV side. This is a security feature. You need to sign your add-in with a valid certificate to get rid of "unknown publisher". You can do that by selecting that certificate on "Sign" tab in Project settings in Visual Studio.
If you are a public company/developer and don't really have any assumptions about your users, then you can obtain the code signing certificate from one of the public code signing certificate providers. The main ones are listed here: https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/get-a-code-signing-certificate
It is not free, and you'll need to prove your identity to the certificate issuing authority (like, provide them your passport/company registration details/etc).
If you are developing an add-in for a specific organization and you can negotiate with it's admins, or your users have admin rights on their computers and are allowed to mess with security, then you can create a self-signed certificate, sign your code with it (select it on "Signature" tab), and then ask your users (or user's admin) to add this self-signed certificate to "trusted root" for their organization/on their PC.
Please note that self-signed certificate usually work only on the PC where the certificate was created, and used for testing or development purposes. In your case, this means that your self-signed certificate is considered valid on your PC, but not on the customer's PC. If you want a certificate created by you on your PC to also work on customer's PC, then you'll have to ask the customer to add your certificate as "trusted root", i.e. to treat your signature the same way as one of those "worldwide trusted certification authorities" from the list above.
The normal practice is to buy a certificate from the certification authorities though.

How to programmatically add x509 certificate to local machine store using c#

I understand the question title may be a duplicate but I have not found an answer for my situation yet so here goes;
I have this simple peice of code
// Convert the Filename to an X509 Certificate
X509Certificate2 cert = new X509Certificate2(certificateFilePath);
// Get the server certificate store
X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
store.Add(cert); // x509 certificate created from a user supplied filename
But keep being presented with an "Access Denied" exception.
I have read some information that suggests using StorePermissions would solve my issue but I don't think this is relevant in my code.
Having said that, I did test it to to be sure and I couldn't get it to work.
I also found suggestions that changing folder permissions within Windows was the way to go and while this may work(not tested), it doesn't seem practical for what will become distributed code.
I also have to add that as the code will be running as a service on a server, adding the certificates to the current user store also seems wrong.
Is there anyway to programmatically add a certificate into the local machine store?
Thank you to Oscar and Bob for asking the questions and leading me in the right direction +10 to you both :)
My issue, as I think we all knew (even me) was the user running the application had insufficient privilages to add a certificate to the local machine store.
But various attempts to elevate the user permissions were failing for me, let me explain why.
I had 3 seperate projects in my solution, the wcf service which requires the X509certificates, the windows form client and the cryptography class library which, amongst other things, installs the certificates provided via the windows form client.
As most of the code within all 3 projects could run without elevated permissions, I really wanted to only elevate them at the certificate install stage within the class library but I tried to use Process and Verb= "runas" in code and this didn't work.
Then I tried to add a custom manifest but if you try to alter the properties of a class library to use a custom manifest, you'll find the option is disabled.
So I changed things.
My cryptography class is now within my windows form client and I've added the custom manifest to the client. This now means the whole client opens with elevated privilages but I'd rather that than the alternative.
Thank you again

Why does sslStream.AuthenticateAsServer require no UAC and alternatives

While tweaking a web proxy code, I noticed that if I run the code in an app with UAC (User Access Control) I get an exception
Here is the location where the exception is thrown:
sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false);
Here is the error I get (with UAC)
The server mode SSL must use a certificate with the associated private key.
First question: Why?
2nd question: Is there an alternative? I really would like to run this in UAC (note that http works fine)
It sounds like the private key to your certificate is probably located somewhere with restricted file access. This is so that not just any user can read your private key. What is the purpose of running your service with limited privileges? If the key is only for this service, you may consider removing the security limitations on the folder it is in, but if this key is for your whole domain, I would be hesitant to do that...
Have you installed the certificate in the appropriate certificate store?
From what I remember when working with certificates, the file that you load is only used as a reference to identify the server certificate in one of the Windows certificate stores. If the proxy is running under one account but not the other, maybe you didn't install the certificate in that other account's personal certificate store.

Cannot find the certificate in either the LocalMachine store or the CurrentUser store

I have already installed the ssl cert in user PC. And, I also imported the cert under Local computer of Trusted Root Certification Authorities. I am trying to consume the web servcie. In my code I attach the cert as below.
webservice.ClientCertificates.Add(X509Certificate.CreateFromCertFile(certPath));
But I got the following error.
Cannot find the certificate in either the LocalMachine store or the CurrentUser store
What could be the issue? I'm using .Net Framework 2.0. So, not able to use WCF. Thanks.
That means,
probably you have the wrong certificates installed.
you installed the certificates to the wrong place.
An easy way to tell if you have a client certificate installed properly is
launch IE.
Open Tools | Internet options.
In Content tab, click Certificates button.
Then you should see the cert in Personal (read cert path and see whether it contains errors), and will be able to use it in your WinForms application.
I experienced similar issue. In my case it was solved by using MMC and giving Full control rights of the certificate for user IIS_IUSRS. My Website is running under ApplicationPoolIdentity. This answer guided me to the right path: How to give ASP.NET access to a private key in a certificate in the certificate store?
I had the same error when trying to connect to a service that hadn't been set to use SSL. Once I change the configuration of the service to use SSL, the connection worked fine.
Also you may get that error if the account doesn't have access to the private key, which WinHttpCertCfg.exe from the Windows Resource Kit can solve.

Programmatically import cert into IIS?

I have a .pem certificate for SSL, I want to distribute it with my web application in an MSI (has to run on clients' computers). I then need to import it (into some credentials store?) and tell my site bindings to use it. But how can I do this in code? I've discovered Microsoft.Web.Administration, but not sure where to go from there …
This is in IIS7 btw.
EDIT: The goal here is to have a web application that customers can run on their intranets. It mainly acts as an API for an iPhone app. (Maybe this isn't the best design but we're locked in now.) So the customer installs the MSI, and voila, they have a web service. Now there needs to be password authentication between the iPhone and the web service; the simplest way seemed to be to do it in https. So I made a self-signed cert.
I'm aware that redistributing a single cert is generally a bad idea, but we're just trying to defeat casual hackers here … this is going to be intranet only and for businesses only, it seems unlikely that anyone is going to be doing anything too crazy, and the API severely restricts the amount of Bad Things you are able to do to the database anyways.
So there we go, the goal is to have password authentication on an intranet web app, with one-click(ish) installation. :-D
The answer, dear readers, is this:
// Assume 'site' is already set to your site via something like
// Site site = mgr.Sites.Add(siteName, directory, 443);
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
// Here, directory is my install dir, and (directory)\bin\certificate.pfx is where the cert file is.
// 1234 is the password to the certfile (exported from IIS)
X509Certificate2 certificate = new X509Certificate2(directory + #"\bin\certificate.pfx", "1234");
store.Add(certificate);
var binding = site.Bindings.Add("*:443:", certificate.GetCertHash(), store.Name);
binding.Protocol = "https";
store.Close();
Thanks to this random thread: http://forums.iis.net/t/1163325.aspx
You need to narrow down your question. What is the certificate used for (exactly)?
If your certificate is used for client-side authentication (to authenticate the client on the server), then distributing it with your application makes such authentication plain useless, as you would be disclosing the secret key.
If you need to validate the server's certificate (and you've been given server's certificate or certificate chain in your PEM file), then this could work, but why would you need to install the certificate to the certificate store?
You should note, that PEM format is not natively supported by Windows or .NET libraries so you need to convert it to PFX before deployment, and then import a PFX or just create a store in memory based on PFX (you will find plenty of information by looking for PFX or PKCS#12 on StackOVerflow).
Upd: it would be a better approach to generate the certificate each time you install the application and let the user have their own certificate (eg. it's possible that they already have a valid certificate for their web site).

Categories