WCF, Certificate Authentication - Common Errors and Confusing Arguments - x509certificate

I am trying to setup a WCF service to use a Certificate for Authenticating the client. I have read tons of posts on how to create the certificate, and I have been able to do so (finally).
I am installing the Cert Authority and the Cert on a server that runs Windows 2008 R2. When I open the MMC Certificates Snap-in, I choose Computer Account. Is this correct? I am doing this because my WCF service will run in a Windows Service, and will be running even when no user's are logged in. But admittedly, I don't know what the difference is between the three options:
My user account
Service account
Computer account
Once the snap-in loads, I import the Authority Cert into Trusted Root Certification Authorities. Then, I import the cert into Trusted Publishers. I don't encounter any errors when doing this. When I do the import, of both the Authority Cert and the Cert signed by that authority, I don't make any reference to the .pvk file. It is my understanding that the private key is embedded in either the cert or the authority cert. Here are the commands I use to create each cert:
MakeCert.exe
-n “CN=InternalCA”
-r
-sv InternalCA.pvk InternalCA.cer
-cy authority
MakeCert.exe
-sk InternalWebService
-iv InternalCA.pvk
-n “CN=InternalWebService”
-ic InternalCA.cer InternalWebService.cer
-sr localmachine
-ss root
-sky exchange
-pe
Notice I used -ss root. I have seen many posts using -ss My. I don't really understand what the difference is or when it is appropriate to use each value.
My WCF service runs on this machine inside a Managed Service (Windows Service). When I start my windows service, which hosts the WCF service, it crashes immediately and a seemingly common error is reported in the event viewer:
System.ArgumentException: It is likely
that certificate 'CN=TempCertName' may
not have a private key that is capable
of key exchange or the process may not
have access rights for the private key
I have found posts that say I need to grant permissions to the user running the service to the key.
This one seems to be a popular answer here on stackoverflow: Grant access with All Tasks/Manage Private Keys
But I don't have the option of All Tasks/Manage Private Keys
But I'm not clear on how to do that. But also, the service is running under my domain account, which is an administrator and is also the same user that installed the cert.
Please Help :)

Here's the best link that should help you get your self-hosted SSL WCF service to work with your own custom CA/certificates: SSL with Self-hosted WCF Service.
After you get it working from the guide above, you may want setup your service programatically to use the right certificate during installation-time.
I find that verifying my HTTP.SYS configuration with the HTTPCfgUI tool to be easier than via the command line httpcfg/netsh commands.
Next, if you still encounter errors, you can debug further using WCF Tracing. Additionally, you should turn on WCF Message Tracing as well. You can trace the .NET network stack too, if the WCF tracing doesn't provide enough information.
You can test if your certificate/CA pair on the service is working by hitting your service URL in a browser on another machine. It should first state that the certificate is invalid. Then, import the CA on the machine into the trusted root, and hit the service URL again. This time it should display the service description page as usual, with no warnings.

I think you are close. Here are some suggestions:
Make sure you are getting a private key attached to your certificate in the second step. You have to run the command in an elevated process -- even if you have Admin privileges, you have to, for example, right-click and "Run as Administrator" to start the command shell you use for this command. Otherwise, you won't get the private key into the localMachine store.
I would use -ss my and put the certificate (with private key) into the Personal store. Here I see this:
cc.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.CurrentUser,
StoreName.My,
X509FindType.FindBySubjectName,
"contoso.com");
and so wherever you are pointing to in your equivalent, put the certificate there.
You don't need to import the private key of the CA cert (the first one you created). That's only kept around to sign more certificates with MakeCert. You will need a copy of that CA certificate (without the private key!) on the client that is connecting in, otherwise the client won't be able to validate the InternalWebService certificate.
You don't really need the CA certificate locally on the server machine, because it will only be needed by the client. But it doesn't hurt, and would be needed if anything on the server connects to the service locally. Also, it makes the InternalWebService certificate look good on the MMC snap-in. You can try with and without the CA in the Trusted Root store and you'll see what I mean. But in any case, I would not put the private key of the CA into the local computer store.
Check the private key permissions for InternalWebService from the MMC snap-in (right-click on the certificate, then Tasks, Manage Private Keys...) If you import under a different user account than the service runs under, then certainly it won't have access yet, and you have to go give it access. Otherwise the service will get the certificate, but it will appear that the certificate does not have a private key.
To summarize:
Run with Admin privileges to make sure the private key of InternalWebService is really getting into the certificate store. (You'll see a little key on the certificate in the MMC snap-in, and right-clicking on the certificate will have an option "Manage Private Keys..." which is not present if there is no private key attached.)
Put the InternalWebService in the place where you Web service is looking for it. I would guess "Personal" (a.k.a. "my") but you know where that is in your service config. Whether current user or local machine, look in your config.
Give access to the InternalWebService certificate private key.
Put the CA certificate -- without its private key -- under Trusted Root, and you'll need to put that on the client as well (or otherwise have the client accept an "untrusted certificate" at its end.)

Related

Where does one place the Always Encrypted Certificate on an IIS 7.5 web server?

We have a SQL Server 2016 database that employs Always Encrypted. Our recently published ASP.net web site attempts to pull data from this database, and when it does we get this error:
Error: Failed to decrypt column 'EnSSd'. Failed to decrypt a column encryption key using key store provider: 'MSSQL_CERTIFICATE_STORE'. The last 10 bytes of the encrypted column encryption key are: 'B8-48-B3-62-90-0B-1D-A6-7D-80'. Certificate with thumbprint '97B0D3A64CADBE86FE23559AEE2783317655FD0F' not found in certificate store 'My' in certificate location 'CurrentUser'. Verify the certificate path in the column master key definition in the database is correct, and the certificate has been imported correctly into the certificate location/store. Parameter name: masterKeyPath
Now we know that this means that the certificate has not been placed in the proper location on the server. During development we simply placed the certificate in the Certificates snap-in under the Personal Certificate Store, and that worked, however now that the site has been published we tried doing the same on the web server but it's not working (we kind of figured it wouldn't).
Anonymous Authentication is enabled on the site and the anonymous user identity is IUSR. ASP.NET impersonation is disabled.
Where is the proper place to put the certificate?
UPDATE - we got it to work by changing the Application Pool Identity account to the one that created the Certificate. It was also the account used when adding the certificate to the Current User-Personal list on the web server. We would rather not use this account, so again, where is the proper place to put the certificate?
Always Encrypted requires that the user that is accessing the database to have both the public and private key, which is what it appears to require you to use the account to generate the certificate as they will have this key.
What I usually do is generate the certificate and export the cert with a private key and secure passphrase. Then import the cert with key into the personal store of the account you use to run the app pool. This cannot be a generic integrated account and must be a service account you specify.
run a powershell script as the user:
whoami
COMPUTER\myIISPoolUser
Set-Location -Path cert:\localMachine\my
Import-PfxCertificate –FilePath c:\AlwaysEncrypt.pfx
or use mmc.
whoami
COMPUTER\myIISPoolUser
certmgr.msc
You must also allow the APP Pool user load user profile
IIS can't recognize the certificate from local user, while creating the certificate in SQL server, by default it's putting in to the local user store, do the following things and make sure the certificate generated under local machine -> current user certificate store
Generate the encrypted columns with default certificates
Undo all encrypted columns in to plain text
Go to the certificate and key from the table security "Your DB - > Tables - > Security -> Always Encrypted Keys" and right click the "CEK_Auto 1" -> Script Column Encryption Key as -> Create to new window, keep this generated script
Delete the CEK_Auto 1
Do the step 3 for "CMK_Auto 1" certificate and delete this as well
in the "CMK_Auto 1" script change the certificate path "CurrentUser" in to "LocalMachine"
you example path will be like this "N'LocalMachine/my/G4452V8ERH035D2557N235B29MWR0SV834263G26'"
execute the CMK_Auto 1 and CEK_Auto 1 script
make sure the certificate generated local machine personal directory
it will work, if not test with IIS express that means still your certificate held in local user personal directory
rest all same make sure the "Column Encryption Setting = Enabled" added in the connection string.
Thanks
John Rajesh J
Running on IIS Express is different from running on IIS.
when we create Column Master Key (CMK) from SSMS at that time it generates Always Encrypted Certificate based on which location we have set while creating CEK.
For IIS, you have to generate certificate under MyLocalMachine, and then install certificate on hosting server with administrator rights. this will work for you.
You also need to give access of that certificate to IIS User. This can be done by right click on certificate and then click on manage primary key and add IUSR.
I had the same issue and here's what solved it for me.
Run SSMS as administrator
Right-click the first column you want to encrypt and select Encrypt column...
In the wizard to encrypt the column, under Select a master key source, select Local Machine and not Current User. (In my case, the Local Machine option only appears when I am running SSMS as administrator, hence #1).
Then launch mmc.exe. Under File > Add/Remove Snap in..., add the Certificates snap-in. When prompted, select Computer Account then Local Computer.
Now that Certificates has been added, expand Certificates (Local Computer) > Personal > Certificates, select the Always Encrypted Certificate you just created, right-click it and go to All Tasks > Manage Private Keys..., then make sure the user who is going to access the database has read permissions.
If you want to move the database to another machine, in this same MMC window, right-click the certificate, then All Tasks > Export. When prompted, choose the option to export the private key with the certificate (won't work if you don't). You'll need to enter a password before exporting. Once exported, import it to the other machine using that same MMC window. You'll need to enter the password you created when exporting and again make sure your user has read permission to it.

Verifying Client-Signed X509 Request in Web API without installing to Store

We have Web API 2 application exposed to outside vendors for various integrations. We're adding a new one with DocuSign through their Connect service and they will be signing their requests with their X509 certificate. I would rather not install the certificate on the server itself because we add new servers and deployments often based on load.
Here is my plan, and I'd like to know what the security risks are with it (assuming it will work at all).
DocuSign provides their X509 certificate for download. I want to place that *.cer file in my Web API application's ~/App_Data folder, along with any other certs from any other vendors. I will use a DelegatingHandler to grab the client certificate from the Request. I would then use the X509Chain class as described here to load all certificates from the ~/App_Data folder and to verify the request certificate.
From there I would map the certificate subject to a role and add that to the current thread to provide authentication for specific routes.
I've gathered from my research that this method would be less secure than installing DocuSign's certificate to the server's root store - is that correct? And how much less secure?
At the end of the day I'd like to (1) verify that the request is coming from who it says it's coming from, and (2) add roles based on the verified requester for authentication.

Get certificate from LocalComputer store "Store_1" on IIS

I'm working on multi tenant ASP.NET web application for digital signing.
For every tenant certificate store named "Tenant_{TenantId}" is created in store location "Local Computer" (StoreLocation.LocalMachine).
Everything works fine from windows application running under account with local administrator permissions.
When the same library is used from ASP.NET application hosted on IIS, only "Local System" identity can work with certificates.
I tried with local administrator user account and get exception Access is denied:
System.Security.Cryptography.CryptographicException: Access is denied.
Are there any restrictions on code under IIS? How to grant user/code read certificates permissions (from specific certificate locations/stores)?
Application is hosted on Windows Server 2012R2 / IIS 8.5 / ASP.NET 4.5
There are 3 options you can try.
1) Use MMC (Certificates snap-in for Local Computer).
If your store (Store1) is listed under "Certificates (Local Computer)" node, find your certificate. If the store is not listed, try to search certificate with command from context menu of "Certificates (Local Computer)" node: Find Certificates... (Find in: All certificate stores).
After you have found the certificate, you should drag&drop it to Personal store, then select All Tasks -> Manage Private Keys from its context menu, grant access to accout and finally drag&drop it to your store back.
Based on this answer
2) Use WinHttpCertCfg.exe to grant access to certificate private key
winhttpcertcfg -g -c LOCAL_MACHINE\<your-store-name> -s <cert-subject> -a <IIS-app-pool-account>
3) If you need to grant access in code take a look at solution in powershell (you can easily implement it in C#)

Jar signing common name

I'm looking to sign a jar that will be launched via jnlp from different servers behind a firewall on an internal network. The jar file needs to be signed with a trusted certificate so as to avoid the security warning.
I've set-up keystores and SSL certificates in the past, but only to be used for web applications. Typically the Common Name used when setting up the key-pair should be the domain name pointing to the web application (e.g. mysite.example.com).
How does this change when signing a jar that will be served via jnlp from different servers that typically do not have domain names assigned to them. Is the Common Name as important here? Can we set-up and sign the jar using a single trusted certificate with one Common Name, to be used for all servers?
Thanks!
Normally we sign something with private key and we verify by public key. Then it is a little bit different than trust a web (SSL) server.
The procedure:
create (get) key pair certificate
import on keystore
sign the jar
http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jarsigner.html

How to provide private key access to the application pool identity?

I have come across this excellent blog explaining how to provide access to private keys and why its needed:
On www.microsoft.com we have a number of applications that use certs
to access other web services, the way we do is by installing the
certificate with the private key into the local machine store and
provide access to the application pool identity to the private key and
use the serial number or the thumbprint of the certificate in the
web.config of the application. One of the key challenge was to script
out the private key access to the application pool identity across the
server farm. (source: http://tinyurl.com/cbsmpqk)
At the time being my silverlight application can't access the database as it seems that it has no access to the installed certification (on Azure Cloud), as this is required to read the encrypted DB connectionString.
It says Failed to decrypt using provider 'CustomProvider' -> Keyset does not exist.
Following the blog above I was able to solve it by changing permissions for the installed certificate to additional read access for 'Authenticated Users' or even to 'Users'.
In both cases it works fine, but I don't understand the security implications. Does it mean now that anybody from Internet is able to read my certificate's thumbprint? Have I mistakenly opened a security hole?
Which username/Role should I rather have given the read access to the private keys of the certificate?
Many Thanks
Grant permissions to IIS AppPool\AppPoolName user. For example, IIS AppPool\DefaultAppPool.
From this answer: https://stackoverflow.com/a/2647003/991267
I don't think it means anyone can access your private key. If you set the 'authenticated user' or 'users' that means only the users in these groups would have the access permission. But if you set 'everyone' that means, everyone.

Resources