Get certificate from LocalComputer store "Store_1" on IIS - asp.net

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

Related

Error ID1039: The certificate's private key could not be accessed even when app pool account has access to private key

After installing a .pfx cert into the 'Local Machine' store on my laptop, I configured an ASP.NET website in IIS Manager to use that cert in its https bindings.
The application pool, which the website uses, is assigned a user account that has access to the cert. When I navigate to the homepage of the site, I get a 500 error page. In Windows Event Viewer, the following is logged...
Error: 'ID1039: The certificate's private key could not be accessed.
Ensure the access control list (ACL) on the certificate's private key
grants access to the application pool user
This is driving me nuts because, as I mentioned previously, the app pool account already has Full Control and Read permissions granted against the cert in the Local Machine store.
I have already tried the suggestion mention on this webpage...
https://elybob.wordpress.com/2012/07/04/wif-error-id1039/
whereby you grant the IIS_IUSRS account Read permissions against the MachineKeys folder, but that made no different to my situation.
I'm stuck on how to make progress here, so any suggestions would be appreciated.
In IIS, to be able to use Certificates from file (Certificate .pfx file), need to set "Load User Profile" to True in the application pool's Advanced Settings to be able to load a cert by filename & password.
Select Application Pool of your ASP .NET application -> Right Click -> Advanced Settings -> Set Load User Profile to True
For cryptographic operations, Microsoft algorithms may not be FIPS compliant and you may override it by setting Security Policy to support Digital Signature verification in
Administrative Tools -> Tools -> Local Security Policy -> Local Policies -> Security Options -> System Cryptography: Use FIPS Compliant algorithms -> Disabled
Update: Using certificate private key may also require permission to IIS application pool:
Run certlm.msc -> Select certificate -> All task -> Manage private key
-> Give permission to application pool

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.

How to give permission to Network service to install certificate in certificate store

We have developed a ASP.Net application. My apppool is running under Network Service Identify.Throug code we need to install a certificate. As my app pool is running with Network Service Privilages, it is throwing cryptographic exception "Access Denied".
I've tried the following option:
Given Full permissions to Network Service for the following path:
"C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys"
installed and the below tool to give permission to network service.
WinHttpCertCfg.exe
Not able to give permissions to Certificate Root. This tool is only helpful to give permission to access a specific certificate.
Please help me on resolving this issue.
According to article found at msdn
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384088(v=vs.85).aspx
It says:
Note The user must have sufficient privileges to use this tool, which requires the user to be an administrator and the same user who installed the client certificate, if installed.
So check if network service is member of local administrators group.
whole story
ASP.net permissions to root certificate store
download WCF sample projects from the link below and compile FindPrivatekey project. ( tips: Add reference system.security otherwise you will catch a undefine X509Certificate2UI function error )
http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=21459

Content Manager configuration snap-in "Could not read configuration item"

I am on a Tridion 2011 SP1 CM server and I’m trying to start the SDL Tridion Content Manager configuration MMC snap-in.
I get the following error:
Could not read configuration item. Modification of this item is not
available on this machine. Account has no permission to access the
protected configuration section 'tridion.security'. Contact your
system administrator.
My user is of course part of the local admins.
What is going on? how to fix it?
The Content Manager uses a .NET encryption key to ensure the encryption of sensitive configuration data such as passwords. By default nothing is encrypted. The following user accounts automatically have access to this encryption key:
Any Content Manager system account (including the Content Manager user account and impersonation user accounts created during installation)
The user account of the user who originally ran the installer
The use of the configuration encryption functionality is completely transparent, so long as the following is true:
The user account that runs the SDL Tridion MMC Snap-in configuration tool is the same user account that originally ran the installer.
The user executing the various SDL Tridion Windows services is not changed from its default value.
If you want to run the Snap-in and/or Windows services as another user than specified, you must grant that new user access to the encryption key. To grant this access, log on as the user account of the user who originally ran the installer, or as another, similarly authorized user with access to the encryption key, and do the following:
Open a Windows command prompt.
Go to a directory on your machine on which a version of the .NET Framework is installed (a subdirectory of C:\Windows\Microsoft.NET\Framework\ or C:\Windows\Microsoft.NET\Framework64\).
Enter the following command:
aspnet_regiis -pa "TridionRsaKeyContainer" "<domain>\<account>" where <domain> is the domain of this user and <account> is the username of the user.

WCF, Certificate Authentication - Common Errors and Confusing Arguments

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

Resources