Using a certificate for Azure subscription logs - 403 forbidden - c#

Background:
I have a Windows Service which polls Azure subscription logs (API: http://msdn.microsoft.com/en-us/library/windowsazure/gg715318.aspx)
On my local development machine the service is set to log on as my account. The X509 certificate was imported under CurrentUser\Personal and in the source code where I check the cert store I have:
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
Issue:
The service works fine on my dev machine, it can retrieve data from the API.
On the testing machine I get this error:
The remote server returned an error: (403) Forbidden.
The service is set to log on as a specific user, dmz\aaseclg1 and the current user\personal cert store has the required certificate.
Any ideas?
Thanks in advance.
Edit: image of password prompt:

I have seen this error when I export the certificate from the machine on which it is created and while exporting, I choose to import it in .cer format (i.e. without exporting private keys). Can you try by exporting the certificate from your dev box in pfx format and then copy the file on your test box and import it again in your test box by selecting the file and installing the certificate?
UPDATE
I was able to reproduce this. When you import the certificate, please make sure that you have unchecked the checkbox which reads "Enable strong private key protection" as shown in the screenshot below.
When I check this checkbox, every time I use this certificate it prompts me to enter a password. Now I was using a GUI application so I could see that box. In your case since you are consuming the certificate through a Windows Service (a non UI thingie), this box never shows and you think the service is hanging.

Related

IIS will not bind certificate

Using the BouncyCastle API I create certificates and add them to the certificate store, marking the Private Key persisted and exportable.
My key shows up in the local machine's personal store and is a selectable option inside IIS manager, however, when I click OK to bind my certificate, I get an error dialog with the following message:-
A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)
In Windows Event Viewer, the following is logged:-
A fatal error occurred when attempting to access the TLS server credential private key. The error code returned from the cryptographic module is 0x8009030D. The internal error state is 10001.
I have verified that my private key is exportable by going to Action -> All Tasks -> Export... in Certificates MMC and can see that the radio button to export private key is not grayed out.
Suspecting that it could be a file permissions issue, I also from the MMC went to All Tasks -> Manage Private Keys and tried setting Full Control + Read on the application pool the site uses in IIS. Same error, in desperation, I have even tried setting it to Everyone with no success.
I am unsure what else could be causing this issue, or what other steps I should try.
Here is a link to a pastebin of the code I am using to generate the certificate, in case it provides any useful information.

SSL certification on C# service over TCP

I have 2 server side (dummy) programs which creates a TCP server (TCPListener) and then tries to authenticate the server with X509 certificates (BeginAuthenticateAsServer).
The client program is ready.
The difference between the two server side program is that one of them is a simple console application while the other is a Windows service.
For some reason client can connect to the console application but not to the service. Design is the same in both program.
I'm using the following line to describe the certificate I will use:
serverCertificate = new X509Certificate( "C:\\Users\\Tom\\workspace\\ServerSSL.cer", "12345678" );
I think something is fishy about privilages with the service program but I could not figured out in the last couple of days. Of course, I have the The server mode SSL must use a certificate with the associated private key. error. When I tried to search for answer as help, I got results in topics of IIS / webservices but I'm using a simple Windows 7 Pro.
May I ask your help?
The file ServerSSL.cer most likely contains only certificate, not the private key. That's what the error message tells you.
Try to find pfx or p12 file. Or if you have separate file that contains private key (i.e. .key) you need to make a pfx (p12) file from both of them (.key and .cer). You could use openssl or xca to do that.

.Net Prevents API Web Service Connection

I have created an app in C# (.exe output) which connects to an API WebService that requires a Self-Signed certificate.
For debugging, I have add the certificate to Trusted Root Certification Authorities certificate store, and It has been working perfectly.
Running it from command line console, I do not get any error neither, (I guess it is because of command line also checks if the certificate is there.)
The problems comes when the applications is runn from another process, then is when I get the following error.
"The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."
My questions are:
Why can I run it without any problem from comand line and from an automated process dont??
What should I do in order to that my application would use the self-signed certificate?
I have found the solution.
System.Net.WebException thrown when consuming a web service over HTTPS
You don't mention it, but is the process running as another user?
Unless you specifically select the computer account certificate store, you'll add certificates to your personal certificate store (your user account). These are not available when running as another user.
To make a certificate available for all users, add it to the computer certificate store:
Start mmc.exe
Press CTRL+M (Add/remove snap-in)
Select Certificates, click Add
Select Computer account, click Finish
Import your certificate
You can also select Service account and select the service which needs access to the certificate if you prefer.
This article on MSDN covers the same steps and is a bit more verbose.
Edit:
Adding a certificate to the machine store requires elevated permissions. If you're not bundling this in a MSI installer, I suggest you write a separate InstallCertificate.exe (console or windows app) which performs the installation.
In Visual Studio:
Create a new console/windows application and add an Application Manifest File (Add Item / CTRL+SHIFT+A). In the app.manifest file just added, change this line
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
into this
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Place this code in Program.cs (add some logging and error handling before pushing this to production!):
using System;
using System.Security.Cryptography.X509Certificates;
namespace InstallCertificate
{
class Program
{
static void Main(string[] args)
{
string cerFileName = args[0];
X509Certificate2 certificate = new X509Certificate2(cerFileName);
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
}
}
}
Run the application. You should get the UAC prompt and the certificate should be installed in your machine's root cert store.

HttpClient request with x509 Certificate works on Debug but fails in Production (403 forbidden)

I have a black box service I have to call into with simple rest commands that returns xml.
They issued us a certificate that had to be run in IE and installs in to IE's Certificate section. As per their instructions I exported it with the entire chain as a pfx with password.
On the machine that the cert was issued directly to, everything works fine in code
var certHandler = new WebRequestHandler();
certHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
certHandler.UseDefaultCredentials = false;
var certificate = new X509Certificate2(Properties.Resources.SigningCert, "password", X509KeyStorageFlags.DefaultKeySet | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); //Must be renewed and replaced every year.
certHandler.ClientCertificates.Add(certificate);
//Execute the command
var client = new HttpClient(certHandler);
string result;
try
{
result = await client.GetStringAsync(url);
System.Diagnostics.Debug.WriteLine(result);
}
catch (Exception ex)
{
throw ex;
}
(I've stored the cert in the resources, but it loads fine and loading it from a file works fine too in developer machine.) I also imported it into IE on the server just in case. Obviously this is likely under the wrong cert store, but I couldn't figure out how to load this in globally. I can tell you that the same REST GETs work in IE on the server just like they do on the developer machine. It's only in code that it fails.)
In production, this same code throws a 403 forbidden.
Production (really a beta server) is actually behind the same nat as the as the development machine so they're seeing the same IP come through etc.
Any ideas why it would fail on the server and not on the developer box?
Thanks!
You should use X509KeyStorageFlags according to account under which your app is running. If it is
1) An app that runs under regular Windows User Account you should use
X509Certificate2(Properties.Resources.SigningCert, "password", X509KeyStorageFlags.UserKeySet);
2) Windows Service under LocalSystem, IIS under NetworkService or other services under built in Windows Account, you should use
X509Certificate2(Properties.Resources.SigningCert, "password", X509KeyStorageFlags.MachineKeySet);
Basically, you shouldn't use X509KeyStorageFlags.PersistKeySet in your case - you import certificate from pfx format every time.
Certificate's private key is storing in the container according to flags. So you may have no access to it if you use wrong flags.
DefaultKeySet is not just alias for UserKeySet (msdn) - so choose appropriate flags in every case.
These articles also may be helpfull:
Key Storage and Retrieval
Eight tips for working with X.509 certificates in .NET
How Certificates Work
What I've found is that for whatever reason it won't allow you to grant permissions properly to the key. To work around it, I went to:
X:\Users\All Users\Microsoft\Crypto\RSA\MachineKeys
In explorer and found the key in question (I used deduction by Date but the thumbprint is there so you should be able to match it up.) and then right clicked, took over the file permissions and then set the permissions manually.
Then my code just started working.
Interestingly however, on another machine that I needed to deploy this to, the file doesn't exist in the machinekeys directory so I don't know what I'm going to do there but...

"The credentials supplied to the package were not recognized" error when authenticating as server with certificate generated using BouncyCastle

I'm trying to create a certificate using the BouncyCastle.Crypto dll, which is then used to authenticate a SslStream as the server in a Windows Service process, which runs under the Local System account.
However when I get to the SslStream.AuthenticateAsServer(certificate) call, it throws a Win32 exception with the error message "The credentials supplied to the package were not recognized".
There are several questions on here about this error message, but none of them seem to describe, or solve, my particular problem.
In the hope that someone may be able to offer some help, I include the code I am using to create and install the certificate:
// First create a certificate using the BouncyCastle classes
BigInteger serialNumber = BigInteger.ProbablePrime(120, new Random());
AsymmetricCipherKeyPair keyPair = GenerateKeyPair();
X509V1CertificateGenerator generator = new X509V1CertificateGenerator();
generator.SetSerialNumber(serialNumber);
generator.SetIssuerDN(new X509Name("CN=My Issuer"));
generator.SetNotBefore(DateTime.Today);
generator.SetNotAfter(DateTime.Today.AddYears(100));
generator.SetSubjectDN(new X509Name("CN=My Issuer"));
generator.SetPublicKey(keyPair.Public);
generator.SetSignatureAlgorithm("SHA1WITHRSA");
Org.BouncyCastle.X509.X509Certificate cert = generator.Generate(
keyPair.Private, SecureRandom.GetInstance("SHA1PRNG"));
// Ok, now we have a BouncyCastle certificate, we need to convert it to the
// System.Security.Cryptography class, by writing it out to disk and reloading
X509Certificate2 dotNetCert;
string tempStorePassword = "Password01"; // In real life I'd use a random password
FileInfo tempStoreFile = new FileInfo(Path.GetTempFileName());
try
{
Pkcs12Store newStore = new Pkcs12Store();
X509CertificateEntry entry = new X509CertificateEntry(cert);
newStore.SetCertificateEntry(Environment.MachineName, entry);
newStore.SetKeyEntry(
Environment.MachineName,
new AsymmetricKeyEntry(keyPair.Private),
new [] { entry });
using (FileStream s = tempStoreFile.Create())
{
newStore.Save(s,
tempStorePassword.ToCharArray(),
new SecureRandom(new CryptoApiRandomGenerator()));
}
// Reload the certificate from disk
dotNetCert = new X509Certificate2(tempStoreFile.FullName, tempStorePassword);
}
finally
{
tempStoreFile.Delete();
}
// Now install it into the required certificate stores
X509Store targetStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
targetStore.Open(OpenFlags.ReadWrite);
targetStore.Add(dotNetCert);
targetStore.Close();
Ok, now I have created and installed the certificate. I then configure my Windows Service to use this certificate by supplying it with the generated certificate's thumbprint. I then use the certificate like this:
// First load the certificate
X509Certificate2 certificate = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certInStore in store.Certificates)
{
if (certInStore.Thumbprint == "...value not shown...")
{
certificate = certInStore;
break;
}
}
SslStream sslStream = new SslStream(new NetworkStream(socket, false), false);
// Now this line throws a Win32Exception
// "The credentials supplied to the package were not recognized"
sslStream.AuthenticateAsServer(certificate);
Does anyone have any idea what the problem could be here?
I don't get the problem if I install a certificate created with 'makecert', but that isn't suitable for production certificates.
I've also tried creating a separate x509v1 CA certificate and then x509v3 certificate for server authentication, but I get the same error, so I removed this in the example code for simplicity.
That particular error message rings a bell. I'll guess that either you did not store the private key with the certificate, or, the Windows service does not have access to the private key. To check this, open the Certificates MMC snap-in:
Run mmc (e.g. from the Start menu)
File menu > Add/Remove Snap-in
Select "Certificates" in left pane and then click Add
Select "Computer Account" (for LocalMachine) then click Next,
and then Finish
Navigate to the certificate and double-click in the right pane. On the General tab that comes up, you should see a little key icon at the bottom, along with the text, "You have a private key that corresponds to this certificate." If not, that's the problem. The private key was not saved.
If the private key is present, click Ok to dismiss this dialog, and then right-click on the certificate in the right pane and select on the pop-up menu: All Tasks > Manage Private Keys. In that dialog, make sure that the Windows account that the service runs under has read access to the private key. If it doesn't, that's the problem.
Edit: Oops, you wrote that the service runs as Local System, so it must be a missing private key, if it is one of these two problems. I'll leave the key access check in my answer anyway, for anybody else that hits this and is not running as Local System.
Sometime the problem happens when the application try to reach the certificate doesn't have enough privilege to access the certificate, the issue may resolve by running the application as administrator.
I've the same issue, tried everything from many posts, and google researching.
But looks like I found fix.
When I changed Identify from ApplicationPoolIdentity to LocalSystem everything start working perfectly.
May be will be helpful for someone.
For me works on Windows Server 2012 R2 (.net 4.6.1) - "All Tasks > Manage Private Keys" and set access to Everyone (setting to IS_IUSRS was not enough)
Found this solution online but I can't find the source to give the credit.
Since I ran into the "The credentials supplied to the package were not recognized" problem with AuthenticateAsClient() (for client verification), I'd like to document how I solved it. It's a different method with the same end goal. Since it might be useful for AuthenticateAsServer(), figured why not.
Here I convert a BC Certificate to a .NET certificate. Add an extra step in converting it to a .NET X509Certificate2 to store it's PrivateKey property.
Org.BouncyCastle.X509.X509Certificate bcCert;
X509Certificate dotNetCert = DotNetUtilities.ToX509Certificate(bcCert);
X509Certificate2 dotNetCert2 = new X509Certificate2(dotNetCert);
Problem showed up when adding a BouncyCastle private key to a .NET private key. The X509 certificates converted fine but not the private keys. I converted the BC private key to RSACryptoServiceProvider using the provided DotNetUtilities. Unfortunately it looks like the conversion isn't complete. So I created another RSACryptoServiceProvider which I then initialized. Then I imported the private key into the one I created.
// Apparently, using DotNetUtilities to convert the private key is a little iffy. Have to do some init up front.
RSACryptoServiceProvider tempRcsp = (RSACryptoServiceProvider)DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)ackp.Private);
RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(new CspParameters(1, "Microsoft Strong Cryptographic Provider",
new Guid().ToString(),
new CryptoKeySecurity(), null));
rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
dotNetCert2.PrivateKey = rcsp;
After that, I was able to save the X509Certificate2 object directly to the key store. I didn't need the actual file so I skipped that step.
Previously, every time I have run into this issue, I have had to delete the cert out of my local machine cert store and re-import it. Then it all seems happy. I can't see how it could be a global permissions issue or invalid cert if simply re-importing it fixes the issue.
How I finally fixed it was using the winhttpcertcfg tool from the Windows Resource Kit to grant permission to the specific user that was using the cert.
The syntax would be:
"C:\Program Files (x86)\Windows Resource Kits\Tools\winhttpcertcfg" -i cert.p12 -c LOCAL_MACHINE\My -a UserWhoUsesTheCert -p passwordforp12
I had the similar issue when calling a WCF REST service from .NET application where I need to attach the client certificate; All I had to do was provide access to the certificate in cert store[mmc console] to the "NETWORKSERVICE] off course my IIS Pool was default pool which indicates its using NETWORKService user account.
the mistake that I did was, I copied the cert from another store to Local
Machine -> Personnel store where the certificate was protected with password. should import the certificate explicitly in required store.
If you running from IIS, ensure that the Application Pool has 'Load User Profile' set to true.
This was the only solution for me.
I don't recall this error but the certificate you're creating is not a valid to be used for SSL/TLS, including:
v1 (not v3) certificate;
missing extensions;
invalid CN;
...
There are several RFC that talks about this, including RFC5246 on TLS (1.2).
Finally making your own certificates is not more suitable than using the ones made by makecert (but the last one can generate the minimum set to be usable for an SSL/TLS server certificate).
I strongly suggest you to buy, from a good known Certificate Authority (CA), a SSL/TLS certificate for production. That will get you a working certificate recognized by the most browsers and tools.
Another reason for this error is that you ran the application / server under an account which password has changed, breaking its capability of accessing the certificate it wants to use in the certificate store.
This especially may not be as obvious if you use a NuGet package like LettuceEncrypt which automatically stores the LetsEncrypt in your store.
Delete the certificate from your store and reimport it.

Categories