Client/Server SSL certificate ignored/forbidden with Require SSL set in IIS 7.5 - iis

I have created a website that I want to be able to access using a smart card so I have enabled SSL on the site. When I am logged in remotely to the server and access the site on the https port using the smart card everything works fine. I receive my prompt and the site loads. However, if I try to access the same site, using the same card from a client pc then I receive the 403 Forbidden error.
Settings on the server(IIS 7.5) I have implemented or verified:
1) Site is bound to specific port (not 443) with a type of https and a certificate that has a friendly name.
2) SSL settings: Require SSL is checked, Require client certificates is selected (note: if I set it to accept, the site will load for the client but it never prompts for credentials)
3) I have verified that when Require client certificates is set to accept, that the certificate it recognizes in the browser is the correct one (e.g. myserver.com) but as I said then I don't receive the prompt for credentials
4) Anonymous Authentication is enabled, all others disabled
5) applicationhost.config file set the following in location -> system.webserver -> security:
a) sslFlags = "Ssl, SslNegotiateCert, SslRequireCert"
b) under authentication set iisClientCertificateMappingAuthentication to true
6) I have confirmed the necessary certificates exist in the Trusted Root and Personal folders for the Local Computer account.
Settings for the client:
1) I have confirmed the necessary certificates exist and match what the server has in the Trusted Root and Personal folders for the Local Computer account.
2) I have checked that the certificate chain in the browser does not show any errors going down the chain in both IE and Chrome
I created a self-signed certificate and added it to all the certificate stores on the server and the client and received the same behavior.
Using OpenSSL I have achieved the following results:
openssl s_client -connect myserver.com -CAfile c:\certs\godaddy.pem -servername myserver.com
Loading 'screen' into random state - done
CONNECTED(00000160)
depth=2 C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", CN = Go D
addy Root Certificate Authority - G2
verify return:1
depth=1 C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", OU = http
://certs.godaddy.com/repository/, CN = Go Daddy Secure Certificate Authority - G
2
verify return:1
depth=0 OU = Domain Control Validated, CN = *.myserver.com
verify return:1
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=*.myserver.com
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.c
om/repository//CN=Go Daddy Secure Certificate Authority - G2
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.c
om/repository//CN=Go Daddy Secure Certificate Authority - G2
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certific
ate Authority - G2
---
Server certificate
-----BEGIN CERTIFICATE-----
...29u1FZGXZnMUZCW5rlmZrkCAiBwDAB0/2t6BhyGh0JxydLWrRzJAZhW33KIem7IC...
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/CN=*.myserver.com
issuer=/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy
.com/repository//CN=Go Daddy Secure Certificate Authority - G2
---
No client certificate CA names sent
---
SSL handshake has read 2713 bytes and written 673 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1
Cipher : AES128-SHA
Session-ID: *** edited ***
Session-ID-ctx:
Master-Key: *** edited ***
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1439825381
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
read:errno=10054
The OpenSSL results has one section that says 'No client certificate CA names sent' and before I added the -CAfile option I was getting the Verify 20 error unable to get local issuer certificate.
UPDATE to OpenSSL results:
Figured out how to use the debug feature and from what I can see here, it seems to negotiate SSL correctly and with the correct certificate as long as I pass in the CAfile path.
openssl s_client -connect myserver.com:10168 -state -debug -CAfile c:\certs\godaddy.pem -servername myserver
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
depth=2 C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", CN = Go Daddy Root Certificate Authority - G2
verify return:1
depth=1 C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", OU = http://certs.godaddy.com/repository/, CN = Go Daddy Secure Certificate
Authority - G2
verify return:1
depth=0 OU = Domain Control Validated, CN = *.myserver.com
verify return:1
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read finished A
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=*.myserver.com
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.c
om/repository//CN=Go Daddy Secure Certificate Authority - G2
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.c
om/repository//CN=Go Daddy Secure Certificate Authority - G2
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certific
ate Authority - G2
---
Server certificate
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/CN=*.myserver.com
issuer=/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy
.com/repository//CN=Go Daddy Secure Certificate Authority - G2
---
No client certificate CA names sent
---
SSL handshake has read 2713 bytes and written 643 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1
Cipher : AES128-SHA
Session-ID: ***
Session-ID-ctx:
Master-Key: ***
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1439919398
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
read from 0x2dd5b0 [0x2f06d3] (5 bytes => -1 (0xFFFFFFFF))
read:errno=10054
write to 0x2dd5b0 [0x2f4c23] (37 bytes => -1 (0xFFFFFFFF))
Additional openssl command:
openssl s_client -connect yourip:443 -prexit
...found on this SO site here did prove that in the list of acceptable certificates names was the Go Daddy Class 2 Certification Authority CA; however, I did not see *.myserver.com. Since *.myserver.com is chained to the Go Daddy Class 2 Certification Authority CA it seems that might be a normal process as indicated by this site here. On my local pc and the server the Go Daddy certificate exists in both the Trusted Root and Intermediate CA folders.
UPDATE - netsh:
I also tried the netsh http add sslcert certhash= appid= clientcertnegotiation=enable which executed sucessfully but did not result in any success.
UPDATE TO WIRESHARK INFO:
If I use Wireshark on my local pc to look at the traffic specifically to the server (display filter is set to search for ip.addr or ssl) I don't see any traffic at all generated by SSL. There's no Client or Server Hello or Certificate entry. I did verify in the first three SYN, SYN/ACK, and ACK packets that the IP addresses and ports were the correct values.
When I run Wireshark on the server I don't see any SSL traffic either specifically coming from my IP address. However, if I run Wireshark and my SSL website directly on the server, the site does resolve, works and prompts for credentials like it should but I discovered that the Certificate id-at-commonName equals a certificate different than I assigned to the site (*.myserver.com) which is *.vo.msecnd.net and belongs according to my research to the Baltimore CyberTrust Root certificate from Microsoft.
UPDATE - Repaired Certificate
Followed the instructions on this site to repair the certificate and rule out it being faulty. I don't have an Entrust cert but the instructions at the bottom work the same.
Why am I not seeing a Client Hello? How can I troubleshoot lack of SSL handshake?
Some SO sites that I have used to try and resolve the issue include:
OpenSSL errno 10054,connection refused
OpenSSL: unable to get local issuer certificate
IIS 7.5 site with SSL fails, site without SSL works
RESOLUTION:
It appears the combination of certificate repair and setting the clientcertnegotiation did the trick but only after I rebooted the server.

The correct steps are:
Set IIS SSL bindings up correctly
netsh http show sslcert and copy the values
Remove server SSL certificate binding with netsh http delete sslcert
Added server SSL certificate binding with netsh http add sslcert ipport=0.0.0.0:443 certhash=.... appid=.... sslctlstorename=ClientAuthIssuer clientcertnegotiation=enable
Verified that settings were applied with netsh http show sslcert
(Windows 2012 R2 only) Set HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList to 1

Related

How to disable all built-in SSL certificates in wget and just use self-signed one?

I would like to use wget (or curl) to connect to my website using only my self signed SSL certificate. The website also has some root CA signed wildcard certificates.
wget -O- --ca-certificate=my.pem --ca-directory=/dev/null --certificate=my.pem https://example.com
This works on my server with the self signed certificate, but it also establishes a connection to any regular SSL-enabled public website (when changing example.com). So it seems to not disable build-in root CAs.
How can I disable all build-in root CAs in wget so only my private certificate can establish a secure connection and it fails without (to test if the self signed cert is installed correctly)?
Got some help on stackexchange: real openssl s_client (check with openssl version) supports the parameter -verify_return_error which will catch certificate verification errors.

SSL: Servers certificate chain is incomplete

I bought a PositiveSSL Wildcard from https://www.ssls.com/
I have received 3 files a .ca-bundle a .crt and a .p7b.
I configured the certificates with NGINX but I'm getting an error:
"Servers certificate chain is incomplete"
https://www.ssllabs.com/ssltest/analyze.html?d=api.billgun.com
How can I fix this?
Servers certificate chain is incomplete
means you don't have intermediate certificates, certificates have expired or are in wrong order.
It looks like you don't have any intermediate certificates: https://www.sslshopper.com/ssl-checker.html#hostname=https://api.billgun.com/.
When you open your site in a browser you will get green padlock because browsers can download missing intermediate certificates but other tools won't be able to connect ie. curl:
curl -I 'https://api.billgun.com/'
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html
or openssl:
openssl s_client -connect api.billgun.com:443
CONNECTED(00000003)
depth=0 OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.billgun.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.billgun.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.billgun.com
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
The fastest way to generate correct chain is to:
open your site in a browser
click on green padlock and display certificate properties
export every certificate in the chain (in your case, you should get 3 files: -billguncom.crt, COMODORSADomainValidationSecureServerCA.crt, COMODORSACertificationAuthority.crt)
combine the files in order from leaf to root cert:
cat -- -billguncom.crt COMODORSADomainValidationSecureServerCA.crt COMODORSACertificationAuthority.crt > billgun_com.crt
install new cert on server
test nginx cofiguration nginx -t
restart server service nginx restart
There is a tool to automate the procedure of producing a bundle of correctly chained certificates. https://github.com/zakjan/cert-chain-resolver (I'm the author.)
Usage:
cert-chain-resolver -o domain.bundle.pem domain.pem
domain.pem is your input certificate
domain.bundle.pem is the certificate bundle, that you can use in your web server configuration

Error "empty certificate chain" in a basic haskell app

I generated a certificate in the directory of my Haskell app:
openssl genrsa -out key.pem 2048
openssl req -new -key key.pem -out certificate.csr
openssl x509 -req -in certificate.csr -signkey key.pem -out certificate.pem
and then I ran my app:
import Network.Wai
import Network.Wai.Handler.Warp
import Servant
import Network.Wai.Handler.WarpTLS
startApp :: IO ()
startApp = do
let port = 3345
let tls = tlsSettings "certificate.csr" "key.pem"
runTLS tls (setPort port defaultSettings) app
And then went to https://localhost:3345 and I got an error "empty certificate chain"
What's wrong with it? Maybe I've put my certificate somewhere such as "/opt/...."?
For now all 3 files are in the root directory of my application: key.pem, certificate.csr and certificate.pem.
UPDATE:
It's arch linux, whereas on hosting I have Ubuntu, therefore I'll need a solution for both.
This certificate is self-signed, whereas on hosting it's issued by let's encrypt.
I've changed a code a bit: "csr" to pem:
let tls = tlsSettings "certificate.pem" "key.pem"
runTLS tls (setPort port defaultSettings) app
Here's another error:
$ curl -v https://localhost:3345
* Rebuilt URL to: https://localhost:3345/
* Trying ::1...
* connect to ::1 port 3345 failed: Connection refused
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3345 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, Client hello (1):
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
TL;DR: Handling certificates is hard. Use the self-signed .pem file (and the key) and add security exceptions to your browser or your operating system. The .csr file isn't something you can use. The server is fine (except for the .csr file), but you will end up with security warnings till you get your key signed by a CA (which isn't possible for local domain names).
What's wrong with it?
The server is (almost) fine. However, you want to send the .pem file. The .csr is a request for signing your certificate by a certificate authority (CA). A CA needs to get trusted by the user or another CA (that's trusted by the user). Let's have a look at your original commands:
openssl genrsa -out key.pem 2048
This will generate the private key, which will get used for the TLS handshake and other operations.
openssl req -new -key key.pem -out certificate.csr
This will generate the mentioned certificate sigining request. You would send this request to a CA, which checks your identity. For example, they would check whether the FQDN actually is yours. Otherwise, you could ask for a certificate for stackoverflow.com and use a MITM attack. This also concludes that you cannot ask a (non-local) CA for a certificate on a local name, like localhost or hostname.local.domain.name.
openssl x509 -req -in certificate.csr -signkey key.pem -out certificate.pem
This will take the original certificate request, use your own key to sign it, and generate a certificate (certificate.pem). Usually, this certificate contains a CA chain, e.g. who signed the CA's certificate, who signed the certificate of the one that signed the CA's certificate and so on, till we end up at a root certificate.
So, all you have to do is to use that certificate together with your private key. This will lead to security warnings though, since you cannot prove your own identity. That's why you need --insecure in curl and a security exception in most browsers. That aside, it will work. Note that the server doesn't know that its certificate is self-signed. It's just using two files to initiate the communication with the client.

IIS Client certificate not working. Returns 403 error

I'm trying to setup IIS 8 (Windows Server 2012) to accept client certificates for a secured WebAPI endpoint. Following this post I created a self signed certificate and a client certificate:
makecert.exe -r -n "CN=MyCompany" -pe -sv MyCompany.pvk -a sha1 -len 2048 -cy authority MyCompany.cer
makecert.exe -iv MyCompany.pvk -ic MyCompany.cer -n "CN=MY Client" -pe -sv MyClient.pvk -a sha1 -len 2048 -sky exchange MyClient.cer -eku 1.3.6.1.5.5.7.3.2
pvk2pfx.exe -pvk MyClient.pvk -spc MyClient.cer -pfx MyClient.pfx -po THE_PASSWORD
I installed the root certificate MyCompany.cer on the IIS server, then on IIS Manager/SSL Settings I selected the "Accept" radio button to allow the website accept client certificates.
On the client side a have a C# test console app that loads the client cert MyClient.pfx file and calls the WebAPI endpoint:
var certHandler = new WebRequestHandler();
certHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
certHandler.UseProxy = false;
var certificate = new X509Certificate2(File.ReadAllBytes(#"C:\MyClient.pfx"), "THE_PASSWORD");
certHandler.ClientCertificates.Add(certificate);
var client = new HttpClient(certHandler);
var result = client.GetAsync("https://MyServer/api/MyEndpoint").Result;
string resultStr = result.Content.ReadAsStringAsync().Result;
Console.WriteLine(resultStr);
I'm getting back a 403 error:
403 - Forbidden: Access is denied.
You do not have permission to view this directory or page using the credentials that you supplied.
I tried the same setup on my local IIS (Windows 7): Imported the MyCompany.cer file, setup SSL in IIS. This time everything works fine and the WebAPI endpoint can see the client certificate with no problem.
Any ideas?
-- Update 1
I enabled Failed REquest Tracing on IIS and I get this:
<failedRequest url="https://myserver:443/"
siteId="35"
appPoolId="CertTest"
processId="7248"
verb="GET"
authenticationType="NOT_AVAILABLE" activityId="{00000000-0000-0000-B0AA-0280000000E0}"
failureReason="STATUS_CODE"
statusCode="403.16"
triggerStatusCode="403.16"
timeTaken="0"
xmlns:freb="http://schemas.microsoft.com/win/2006/06/iis/freb"
>
If I understand right the error is 403.16. I understand that happens when the certificate on the server is not imported into the Trusted Root Certification Authorities under Local Computer. I double checked and that's not my case.
Check that
On your IIS machine you have installed Server's cert issuer certificate to Trusted Root Certification Authorities under Local Computer
On your IIS machine you have installed Client's cert issuer certificate to Trusted Root Certification Authorities under Local Computer
On your client machine you have installed Server's cert issuer certificate to Trusted Root Certification Authorities under Windows User that runs console app
On your client machine you have installed Client's cert issuer certificate to Trusted Root Certification Authorities under Windows User that runs console app. Or you can make sure to include all necessary certificate's chain to pfx file
In code use X509Certificate2's ctor version with X509KeyStorageFlags.UserKeySet explicitly.
If it won't help,
try to open url in IE (under the same Windows User Account that runs console app).
open *.cer files with double click on a client machine under Windows User Account that runs console app and see what Windows says about they validity.
change IIS SSL settings to ignore client certificate to see if it's all good with server certificate. Try both browser and console app.
Have a look at the following blog post: https://configmgrblog.com/2014/02/23/configmgr-2012-r2-internet-facing-mp-windows-server-2012-r2-note/
In short: There seems to be an issue with client certificate authentication and IIS 8.x in Windows Server 2012 (R2), that might cause status code 403.16 to be returned when using client certificates. Solution:
Set registry DWORD entries under HKey_Local_Machine\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL on your server:
SendTrustedIssuerList = 0
ClientAuthTrustMode = 2
The post further mentions that if your get a 403.13 (client certificate revoked) after fixing the in initial problem (403.16), you should disable client certificate revocation check on the server. I would not recommend that. Please make sure that the CDP is correctly set in the client certificate. The CDP has to be reachable from the server and the revocation list should be valid and not outdated.

Using self signed client certificate in development

I'm trying to set up a dev env for a web application that require ssl + client certificate.
So the server & client is the same, it's my laptop.
What's I've done so far, using several different tutorial found on the net:
- Created a self signed Root CA
- Created a "Server" certificate using this root CA, and configure IIS ssl with this certificate.
- Created a "Client" certificate (still using the same root certificate), with all roles
The command used to create certificates looks like this:
makecert -n "CN=Dev Client" -iv DevCA.pvk -ic DevCA.cer -pe -ss my -sr localmachine -sv DevClient.pvk DevClient.cer
pvk2pfx -pvk DevClient.pvk -spc DevClient.cer -pfx DevClient.pfx
So both Client & Server certificate has the same root certificate.
Then I:
- Added the Root certificate in the "Trusted authority store" of both current user and local machine
- Added the Client certificate in the "Personal store" of "Current user"
- Added the Server certificate in the "Personal store" of "Local computer"
When I check Client certificate properties, it correctly found the root certificate.
But now, the problem is that it doesn't works in IIS.
If I set client certificate as "required", I've an HTTP 403.4 Forbidden
If I set client certificate as "accepted", the site indeed works but doesn't receive the certificate
Does someone know how to make this works?
You should use
makecert ... -sky exchange
for server certificate and
makecert ... -sky signature
for client certificate in order to server has the ability to secure connection and client has the ability to authenticate himself.

Resources