Securing a private IP address (https certificate) - ssl

I have an unusual use case :
a web server on the Internet is serving pages through HTTPS,
inside those web pages, there are calls to XMLHttpRequests to a locally connected device (IP over USB)
the device supports both HTTP and HTTPS,
the device is accessible on http(s)://192.168.0.1
the http calls fail because of insecure content in a https page,
the https calls fail because the certificate is not trusted (self-signed),
Side question: Since the device is locally connected to the PC, the encryption is pretty useless: Does a http header exists that allows insecure connections to a specific URL ? (like CORS for cross domain)
Main question: Is it possible to obtain a certificate for a private IP address ?
Edit: it seems that Plex had a similar problem and solved it the way described on this blog. This is a way too big for me.

Is it possible to obtain a certificate for a private IP address ?
A certificate can be bound to an IP address (see this). You can issue a self-signed certificate to a private address, but a trusted CA will not issue a certificate to a private address because it can not verify its identity.
For example a certificate issued to 192.168.0.1 would be theoretically valid in any context, and this should not be allowed by a trusted CA
Plex solves the problem with a Dynamic DNS and a wildcard certificate. The connection are done using the name (not the IP) of the device which is resolved to the private IP
Does a http header exists that allows insecure connections to a specific URL ? (like CORS for cross domain)
No, it does not exist. The browser blocks your XHR connections because they are HTTP connections initiated from a HTTPS page (mixed-content warning). Non-secure content can theoretically be read or modified by attackers, even though the parent page is served over HTTPs, so is normal and recommended that the browser warns the user.
To fix the mixed-content and https errors, you could serve the content through HTTPS and a self-signed certificate, and request users to import your root CA at browser.

An SSL certificate cannot be issued for Reserved IP addresses (RFC 1918 and RFC 4193 range)/ private IP addresses (IPv4, IPv6), Intranet for Internal Server Name, local server name with a non-public domain name suffix.
You could however use a 'self-signed' certificate. Here's how to create one:
Creating a Self-signed Certificate for a private IP
(example https://192.168.0.1) :
You need OpenSSL installed.
For example, on Ubuntu, you could install it by: sudo apt-get install openssl
(It may already be installed. Type "openssl version" to find out)
For Windows, you could try this: https://slproweb.com/products/Win32OpenSSL.html
Once OpenSSL is installed, go to OpenSSL prompt by entering 'openssl' on the console (LINUX), or the cmd prompt (WINDOWS).
$ openssl
OpenSSL>
Now do the following steps to create: Private key, Certificate Request, Self-signing the certificate, and putting it all together, by using the below commands:
i) Create KEY called mydomain.key:
OpenSSL> genrsa -out mydomain.key 2048
ii) Use the key to create a Certificate request called mydomain.csr
You could accept the default options, or specify your own information:
OpenSSL> req -new -key mydomain.key -out mydomain.csr
iii) use the above to create a certificate:
OpenSSL> x509 -req -days 1825 -in mydomain.csr -signkey mydomain.key -out mydomain.crt
iv) Put all the above to create a PEM certificate:
exit OpenSSL (OpenSSL> q) and go to certificate location and do:
$ sudo cat mydomain.key mydomain.crt >> mylabs.com.pem
mylabs.com.pem is your self-signed certificate. You can use this in requests like https://192.168.0.1 if your server supports https. Remember to check the port number for https(443).

Related

How do you get Mozilla FireFox to accept your root Certificate Authority ssl cert so it doesn't complain about self-signed ssl certs on https?

First I want to point out that this works fine with Internet Exporer 11. But for some reason I just can't get FireFox to play nice!
So I already added my own rootCA security certificate and under Internet explorer it works fine and my website with self-signed cert is trusted.
But on Firefox v61.0.1 and also v65.0 even though my root certificate is already added, and in addition security.enterprise_roots.enabled is set to true (in about:config), I still get the "your connection is not secure" message and I have to add a security exception just to view my site.
In the area with the message and button to add exception, Firefox says:
"xx.xxx.xx.x uses an invalid security certificate. The certificate is only valid for xx.xxx.xx.x. Error code: SSL_ERROR_BAD_CERT_DOMAIN"
Now the xx.xxx.xx.x is an internet reachable IP address and both instances in the above line have exactly the SAME IP address, I've used IP for the CN field.
clicking on SSL_ERROR_BAD_CERT_DOMAIN shows:
"Unable to communicate securely with peer: requested domain name does not match the server’s certificate. HTTP Strict Transport Security: false HTTP Public Key Pinning: false Certificate chain: -----BEGIN CERTIFICATE----- ......."
Anyway, I am guessing that in its current configuration, Mozilla FireFox is CONFUSED that the site has an invalid certificate when in fact it is valid and the reason for its confusion is perhaps it expects a domain-NAME not IP address.
If so, is there a way I can tell Firefox to honor IP Address based ssl certs?
Again, current config is working perfectly fine with Internet Explorer for me.
As you know many tools like built-in IDE svn and other source control clients really do not like it when there's something to complain about in an ssl-cert.
That's the reason I went through trouble of creating and loading own Root CA Authority in windows. And no please do not recommend letsencrypt, needs renew often and I don't have incoming OPEN ports which it seems to require to renew.
thx!
Hooray! Success :) I went by my what I thought was confusion over IP vs name and discovered that my extfile under the [alt_names] section I had used DNS.1 = xx.xxx.xx.x
So I simply edited my extfile and changed DNS.1 = ... to now be IP.1 = xx.xxx.xx.x
Then I simply recreated my webserver's cert. For anyone wondering, here's actual command:
openssl x509 -req -in mywebserver.csr -CA myrootCA.pem -CAkey myrootCA.key -CAcreateserial -out ./certs/mynginxwebserverIP.crt -days 2555 -sha256 -extfile myextfile.cnf
That overwrote my current .crt file and all I needed to do was nginx -s reload and bingo!
Well hope that helps someone cuz it was driving me crazy!
Now it works just fine in Firefox and I get that comforting nice green lock :)
And internet explorer didn't know/care about the difference, worked both ways :D

Openssl Client Certificates [duplicate]

I am developing a client/server application with TLS. My idea is to use a certificate on the client so it is authenticated by the server. Also another certificate on the server so the client is also able to authenticate that it is connecting to the right server.
I want first to test and use openssl s_server and openssl s_client to validate the proposal.
Until now I have created a CA private key on the server, I have created a root certificate. With the root certificate I have signed two CSR, so I get one certificate for the server and one certificate for the client.
I also have installed the client certificate + root certificate on the client, and the server certificate + root certificate on the server.
I want now to try to establish a connection between openssl s_server and openssl s_client and verify that they get both authenticated mutually, but I cannot wrap my mind with the documentation on how to do it. Any help or any guide on that?
Once I have that set up, the next step is to test the own developed client against that server, and our own developed server against the s_client. Can we use that for testing?
Thanks
It looks like you are trying to set up a root of trust with (1) s_client and s_server for testing; and (2) programmatically within your code using OpenSSL.
To ensure openssl s_client (or openssl s_server) uses your root, use the following options:
-CAfile option to specify the root
-cert option for the certificate to use
-key option for the private key of the certificate
See the docs on s_client(1) and s_server(1) for details.
To do the same programmatically on the client, you would use:
SSL_CTX_load_verify_locations to load the trusted root
SSL_CTX_use_certificate to specify the client certificate
SSL_CTX_use_PrivateKey to load the private key for the client certificate
To do the same programmatically on the server, you would use:
SSL_CTX_load_verify_locations to load the trusted root
SSL_CTX_use_certificate_chain_file to specify the server certificate
SSL_CTX_use_PrivateKey to load the private key for the server certificate
SSL_CTX_set_client_CA_list to tell the client to send its client certificate
If you don't want to use the parameters for every connection (i.e. the common context), then set it for each SSL connection with, for example, SSL_use_certificate and SSL_use_PrivateKey.
A lot goes on with SSL_CTX_set_client_CA_list. It (1) loads the CA's to the server uses to verify a client, (2) it causes the server to send a list of CAs it accepts when verifing a client, and (3) it triggers the ClientCertificate message at the client if the client has a certificate that satisfies the server's accepted CAs list.
Also see the docs on SSL_CTX_load_verify_locations(3), SSL_CTX_use_certificate(3), SSL_CTX_set_client_CA_list and friends.
The easiest certificate and key format to use is PEM. PEM is the one that uses, for example, ----- BEGIN CERTIFICATE -----. For the server certificate, be sure the file is a concatenation of the server's certificate and any intermediates needed by the client to build the chain.
Having the server send all required certificates is standard practice for a problem known as the "which directory" problem. Its a well known problem in PKI, and its essentially the problem that clients don't know where to go to fetch missing intermediate certificates.
In general, you now know the functions that you need to use. Download a small server like nginx, and see how a production server uses them in practice. You could even use a SQL server like Postgres since it sets up a SSL/TLS server. Simply search the source files for SSL_CTX_load_verify_locations or SSL_load_verify_locations, and you will find the right place.
Though I don't recommend it, you could even look at s_client.c and s_server.c. They are located in <openssl dir>/apps. But the code can be difficult to read at times.
Generate two pairs of certificates/keys, one for the server and one for the client. Also create test.txt with any content.
To set up an SSL server that checks a client certificate, run the following command:
openssl s_server -cert server_cert.pem -key server_key.pem -WWW -port 12345 -CAfile client_cert.pem -verify_return_error -Verify 1
To test the server with client certificate, run the following command:
echo -e 'GET /test.txt HTTP/1.1\r\n\r\n' | openssl s_client -cert client_cert.pem -key client_key.pem -CAfile server_cert.pem -connect localhost:12345 -quiet
Alternatively you can use curl command:
curl -k --cert client_cert.pem --key client_key.pem https://localhost:12345/test.txt

Installing SSL cert on Amazon EC2 ELB

I'm new to Amazon Web Services and have been trying to install a SSL certificate on the EC2 instance. I tried following AWS documentation, but found it baffling. I then followed the guide at http://www.robertbrewitz.com/2014/09/aws-and-setting-up-a-custom-ssl-certificate/.
I bought my SSL cert with Go Daddy, and generated 2 files using openssl:
server.key
server.csr
The guide said I should expect 3 certificates:
DigiCertCA.crt
TrustedRoot.crt
star_yourdomain_com.crt
Instead, I confusingly received 2 files named:
f6f65901b1708ae5.crt
gd_bundle-g2-g1.crt
I assume f6f65901b1708ae5.crt is my domain cert (but I'm not certain). Anyway, the guide says I need an Elastic Load Balancer in order to install an SSL cert so I created one.
I generated the private key with:
openssl rsa -in server.key -text
and the public key certficate with:
openssl x509 -inform PEM -in f6f65901b1708ae5.crt
I was also required to enter a Certificate Chain. I wasn't sure what this was and how to get it, so I guessed the command:
openssl x509 -inform PEM -in gd_bundle-g2-g1.crt
and entered the resulting certificate key that began with “-----BEGIN CERTIFICATE-----”
The guide continues, saying I then need to set up Cloudfront. I installed the aws command line tool and in order to generate the PEMs I ran:
openssl rsa -in server.key -text > aws_private.pem
openssl x509 -inform PEM -in f6f65901b1708ae5.crt > aws_public.pem
openssl x509 -inform PEM -in gd_bundle-g2-g1.crt > aws_public.pem
I uploaded the SSL cert with:
aws iam upload-server-certificate --server-certificate-name mydomain_com \
--certificate-body file://aws_public.pem --private-key file://aws_private.pem \
--certificate-chain file://aws_chain.pem --path /cloudfront/mydomain_com/
This was successful.
I then had to create a Cloudfront distribution, which I did, choosing the SSL cert.
However when I go to my https url (https://www.example.org/), it's not working. http://www.example.org/ however does work.
As there are an extraordinarily large amount of steps just for installing an SSL cert, I suspect I've made a mistake along the way. The problem is, I don't know where. Has anybody any pointers?
Also, are there not any simpler ways for installing an SSL cert? It seems to be ridiculously complex for something so common. I'd be willing to pay an expert to do it for me (I'm a software developer with almost no knowledge of anything SSL related), but its difficult to find anyone for such a task (and there is the problem of having to hand over login details etc). Any help greatly appreciated.
EDIT
The suggestion below is that I should use AWS Certificate Manager. I've had a look and this seems like a far more painless option. However, I did spend 86 euro on an SSL cert from Go Daddy so I'd prefer if that didn't go to waste. Is any of my work salvageable? Are there mean to resell SSL certs?
EDIT
I still haven't found a real solution to this. To clarify, I have a very niche site that will have very few visitors. I have the site on a EC2 instance. I followed the site above which advised using a Load Balancer and Cloudfront in order to encrypt with SSL. However, it's not working and it's probably overkill anyway. Can anyone help me with this? I'd like to use the SSL cert I paid for, but if not, should I use something like Lets Encrypt?
You mentioned the guide saying to expect 3 files, including a "DigiCertCA.crt" file; sounds like it was written with DigiCert as your cert provider in mind, rather than GoDaddy.
Certificates
First, to make sure that that "f6f65901b1708ae5.crt" contains your requested certificate (and lay to rest any doubts there). To do this, you can compare the data (e.g. Common Name (CN), DNS Subject Alternative Names (SANs), etc) in your "server.csr" file (that's the Certificate Signing Request) with what's in that "f6f65901b1708ae5.crt" file:
$ openssl req -noout -text < server.csr
This should display human-readable text about the details for your domain in the CSR file. Compare that with:
$ openssl x509 -noout -text < f6f65901b1708ae5.crt
This should display similar human-readable text, with more details/fields filled in. But they should roughly have what you expect. Note that if you see an error similar to this:
51299:error:0906D06C:PEM routines:PEM_read_bio:no start line:/SourceCache/OpenSSL098/OpenSSL098-52.40.1/src/crypto/pem/pem_lib.c:648:Expecting: TRUSTED CERTIFICATE
Then it suggests that your "f6f65901b1708ae5.crt" file is in DER format, not PEM format. If not, then you already have a PEM file, which is what AWS ELBs expect. If you have a DER formatted cert, it's easy to convert to PEM format, using:
$ openssl x509 -in f6f65901b1708ae5.crt -inform DER -out f6f65901b1708ae5.pem -outform PEM
I just wanting to be thorough by mentioning this part.
Assuming, now, that we know that that "f6f65901b1708ae5.crt" contains the PEM formatted certificate for your domain, we're ready to handle the "certificate chain" part.
I looked at GoDaddy's online certificate repository, to see if the "gd_bundle-g2-g1.crt" file you mentioned was there, and it was. (It's OK for these files to be publicly available, as they contain the public certificates which are meant for anyone/everyone to use.) Looking at that gd_bundle-g2-g1.crt file, I found that it contains multiple certificates. This is important.
See, a "certificate chain" is a list of files which provide a trust path (or "chain"), from that "f6f65901b1708ae5.crt" certificate you have to the trusted root GoDaddy CA certificate. Each certificate has a subject (who it was issued to), and an issuer (who issued it). This means that you can walk "backward", from your certificate to the issuer's certificate, to that certificate's issuer's certificate, etc. This walking backward is the "certificate chain".
The fact that that "gd_bundle-g2-g1.crt" file contains multiple certificates implies that that file contains the certificate chain you need. It also means that you do not want to do this:
$ openssl x509 -inform PEM -in gd_bundle-g2-g1.crt > aws_public.pem
because openssl x509 only reads the first certificate in the specified file, and you need all of them.
Given all of the above, you might need only the following (to make sure that your private key is PEM formatted):
$ openssl rsa -in server.key -text > aws_private.pem
And then, since we'll assume that "f6f65901b1708ae5.crt" is already PEM formatted (and if not, you know how to convert it above), and we know that "gd_bundle-g2-g1.crt" is already PEM formatted, we'll now be ready to upload these to the AWS ELB.
AWS ELB
To upload the certificates and key for use by the ELB, use something like this:
$ aws iam upload-server-certificate \
--server-certificate-name redmatterapp_com2 \
--certificate-body file://f6f65901b1708ae5.crt \
--private-key file://aws_private.pem \
--certificate-chain file://gd_bundle-g2-g1.crt \
--path /cloudfront/redmatterapp_com/
Note that I used a different --server-certificate-name, just to make sure it didn't override/conflict with your existing configuration. As a suggestion, you might include the date of when the cert being uploaded was created (or, better, when it will expire), as part of the name, as a hint to your future self of when that cert was added, e.g. "redmatterapp_com-2016-02-18".
Also note that if you are not using CloudFront, then you should not use the --path option. If you were using CloudFront and then removed it, I'd highly recommend doing the above aws iam upload-server-certificate command again, only with a different --server-certificate-name and no --path option (and removing the previous name). This may mean re-configuring any existing ELB HTTPS listeners to use the new certificate name, but it may be necessary, as that --path affects SSL handling.
Once the above is done, using the e.g. AWS Console, you should be able to configure your AWS ELB, and under the "Listeners" tab, click "Edit". Add a Listener e.g. for "https". When adding any SSL-capable listener, you'll see a "Change" link appear under the "SSL Certificate" column/tab. Click that "Change", and choose the "existing certificate" button. Under the "Certificate Name" dropdown, then, you should see an entry for the --server-certificate-name string/label you used above. Choose that entry, then click "Save". Now connections to that Listener, on your AWS ELB, should be properly configured for SSL/TLS connections that browsers will trust.
Thus the HTTPS Listener configuration would look something like:
Load Balancer Protocol: HTTPS
Load Balancer Port: 443
Instance Protocol: HTTP
Instance Port: 80
Cipher: (default policy)
SSL Certificate: (--server-certificate-name name)
Note that you do not want to use port 443 for the instance port as well; if you do, that says that you want HTTPS from the ELB to your instance, which is not usually needed. (Some secure sites want this, but that's a different topic/store.) Thus the above configures the ELB to handle the SSL termination; to your Node.js server on the instance, it only ever receives plain HTTP requests on port 80:
client ---> HTTP ---> ELB port 80 ---> HTTP ---> server port 80
client ---> HTTPS --> ELB port 443 --> HTTP ---> server port 80
If your server needs to know whether the original request was HTTP or HTTPS, look for the X-Forwarded-For (and other request headers) that the AWS ELB will automatically add to the request.
Now, once you have the ELB configured, it's a good step to verify that it's working properly. First, you can use openssl s_client to verify that the SSL handshake works:
$ openssl s_client -connect example.com:443
CONNECTED(00000003)
depth=3 /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
verify error:num=19:self signed certificate in certificate chain
verify return:0
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=redmatterapp.com
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2
2 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2
i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
3 s:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/CN=example.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 4929 bytes and written 456 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES128-SHA
...
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
The above shows that we successfully completed an SSL handshake, and you can see that it shows the cert chain with the GoDaddy names; the "Server certificate" section should match the "f6f65901b1708ae5.crt" PEM file you loaded.
HTTPS and DNS
Now to test the HTTPS part; for this, I like to use curl, like so:
$ curl -kv https://example.com/
The -v option display more information, in particular whether the certificate matches the DNS name or not; the -k tells curl to ignore any certificate mismatches/issues, so that we can then see the verbose information about things.
This is where a little more configuration is necessary. If you use the auto-generated ELB DNS name in your curl command (or in your browser), then you will most likely see security warnings/issues. Why? Because part of HTTPS is verifying that the DNS name you use in your URL matches the domain/host name in your server certificate, either as the Common Name (CN) in your certificate, or as one of the DNS Subject Alternative Names (SANs). And you probably didn't include the ELB DNS name in your Certificate Signing Request (CSR) to GoDaddy.
This means, then, that the next step is to configure a DNS CNAME record that points to the AWS ELB name, e.g.:
www.example.com CNAME to aws-elb-1.elb.amazonaws.com
If you're using AWS for DNS, then you could do this using e.g. Route 53. Then you retry your curl command (or browser):
curl -kv https://www.example.com/
Another important thing to watch for (and you can see this in the curl -v output) is the contents of the Host request header. Some HTTP servers (i.e. the one running on your backend instance) are very picky about the value there; they want the Host header to match something in their configuration, and if it doesn't, they may refuse the request.
Another common gotcha is:
HTTP 503 Service Unavailable: Back-end server is at capacity
If you get this response from your ELB, it usually means that your backend servers are not responding or are failing to respond to the ELB healtcheck. Double-check that the port and path/URL used for the ELB healtcheck are correct, and that any firewalls or AWS Security Groups allow connectivity from the ELB to your instances.
So, now you should have an ELB which forwards port 80 (HTTP) and port 443 (HTTPS) to your backend instances. And you have a CNAME record for "www.example.com" in DNS that points to that ELB name. What's left?
HTTP Redirects
I highly recommend configuring your HTTP server to always redirect to the HTTPS equivalent of the same URL. Why?
For security of the data between your clients and your server, using SSL/TLS is now expected. So much so that more and more browsers automatically try HTTPS first, and only begrudgingly use HTTP as a fallback. Browsers like Chrome (and others) want to avoid this HTTP fallback so much so that they've introduced mechanisms like HTTP Strict Transport Security: a way for your site to tell the browser to only ever use HTTPS for the site, never HTTP. Plus it's always a better marketing story; in fact, you can expect to get negative reactions from your clients/users if you don't use HTTPS.
There's another thing that using HTTPS for all your site's traffic protects you from: someone else using their DNS name and your ELB. You have a CNAME record for "www.example.com" that points to "aws-elb-1.elb.amazonaws.com". But what if I also create my own CNAME for "www.evilco.com", which points to the same "aws-elb-1.elb.amazonaws.com"? Folks going to "www.evilco.com" will see your site, and think it's mine!
By forcing all traffic for your site to be HTTPS, you force all of the HTTPS clients to verify that the certificate presented by the server (i.e. your "f6f65901b1708ae5.crt" file) contains a Common Name (CN) or DNS Subject Alternative Name (SAN) that matches the domain name the client used in its URL. Obviously your certificate does not contain CN or DNS SANs for "www.evilco.com", and thus that verification process would fail -- and the end user would see that something is fishy. But if you allow HTTP traffic as well for your site, this phenomenon could happen -- and you, looking at your site's logs, would never know!
Postscript
I've not used the AWS Certificate Manager, or CloudFront, myself (so I can't comment on them), but I have used the above process, multiple times, for various certificate provisioning of multiple domains, at multiple jobs.
Hope this helps!

Checking server certificate with openssl versus a web request

Working with a standard MediaTemple server setup with an installed GeoTrust domain certificate I am getting different responses from openssl and web requests.
Visiting the site from a site checker site I get a good response and see my domain certificate and the full Geotrust certificate chain.
When using
openssl s_client -connect subdomain.domain.com:443 -showcerts -ssl3
from my local machine I see
Server certificate
subject=/C=US/ST=Virginia/L=Herndon/O=Parallels/OU=Parallels
Panel/CN=Parallels Panel/emailAddress=info#parallels.com
issuer=/C=US/ST=Virginia/L=Herndon/O=Parallels/OU=Parallels Panel/CN=Parallels
Panel/emailAddress=info#parallels.com
and Verify return code: 18 (self signed certificate)
openssl version -d = OPENSSLDIR: "/etc/pki/tls"
It's a Centos 6.x box.
The apache httpd.conf file points to a certificate and CA list in a completely different location: /usr/local/psa/var/certificates/ which would seem fine to me.
Where is the openssl s_client finding the Parallels certificate? It is not located in /etc/pki/tls. Is there a way to configure the box so that the openssl requests and apache use the same server certificate?
Thanks in advance!
openssl s_client gets the certificate from the server during the SSL handshake. OPENSSLDIR is only the place where any (optional) configurations for the openssl tool gets stored.
Note that you might get a different certificate with openssl than you have configured on your server because you need to use SNI (Server Name Indication) like the browser do. This feature is used if you have multiple certificates behind the same IP. To use this feature with openssl add the -servername hostname parameter and provide the name you expect. You must also remove the -ssl3 option since this restricts the connection to SSL 3.0 which is not only insecure but also does not support SNI.
Turns out that on MediaTemple servers they maintain certs in two locations. The apache server has a location for the CA file in its conf files that is different from where openssl maintains its CA files.
You can find the apache location in the conf files and the openssl location with
openssl version -d
Within MediaTemple's web administration pages you can use plesk to install the domain cert into the openssl location as the "server's" cert. The apache server should already have the cert and CA files in the right location. The MediaTemple custom apache configuration overrides the standard apache setup which sets apache's cert locations to be the same as openssl's.

Testing SSL/TLS Client Authentication with OpenSSL

I am developing a client/server application with TLS. My idea is to use a certificate on the client so it is authenticated by the server. Also another certificate on the server so the client is also able to authenticate that it is connecting to the right server.
I want first to test and use openssl s_server and openssl s_client to validate the proposal.
Until now I have created a CA private key on the server, I have created a root certificate. With the root certificate I have signed two CSR, so I get one certificate for the server and one certificate for the client.
I also have installed the client certificate + root certificate on the client, and the server certificate + root certificate on the server.
I want now to try to establish a connection between openssl s_server and openssl s_client and verify that they get both authenticated mutually, but I cannot wrap my mind with the documentation on how to do it. Any help or any guide on that?
Once I have that set up, the next step is to test the own developed client against that server, and our own developed server against the s_client. Can we use that for testing?
Thanks
It looks like you are trying to set up a root of trust with (1) s_client and s_server for testing; and (2) programmatically within your code using OpenSSL.
To ensure openssl s_client (or openssl s_server) uses your root, use the following options:
-CAfile option to specify the root
-cert option for the certificate to use
-key option for the private key of the certificate
See the docs on s_client(1) and s_server(1) for details.
To do the same programmatically on the client, you would use:
SSL_CTX_load_verify_locations to load the trusted root
SSL_CTX_use_certificate to specify the client certificate
SSL_CTX_use_PrivateKey to load the private key for the client certificate
To do the same programmatically on the server, you would use:
SSL_CTX_load_verify_locations to load the trusted root
SSL_CTX_use_certificate_chain_file to specify the server certificate
SSL_CTX_use_PrivateKey to load the private key for the server certificate
SSL_CTX_set_client_CA_list to tell the client to send its client certificate
If you don't want to use the parameters for every connection (i.e. the common context), then set it for each SSL connection with, for example, SSL_use_certificate and SSL_use_PrivateKey.
A lot goes on with SSL_CTX_set_client_CA_list. It (1) loads the CA's to the server uses to verify a client, (2) it causes the server to send a list of CAs it accepts when verifing a client, and (3) it triggers the ClientCertificate message at the client if the client has a certificate that satisfies the server's accepted CAs list.
Also see the docs on SSL_CTX_load_verify_locations(3), SSL_CTX_use_certificate(3), SSL_CTX_set_client_CA_list and friends.
The easiest certificate and key format to use is PEM. PEM is the one that uses, for example, ----- BEGIN CERTIFICATE -----. For the server certificate, be sure the file is a concatenation of the server's certificate and any intermediates needed by the client to build the chain.
Having the server send all required certificates is standard practice for a problem known as the "which directory" problem. Its a well known problem in PKI, and its essentially the problem that clients don't know where to go to fetch missing intermediate certificates.
In general, you now know the functions that you need to use. Download a small server like nginx, and see how a production server uses them in practice. You could even use a SQL server like Postgres since it sets up a SSL/TLS server. Simply search the source files for SSL_CTX_load_verify_locations or SSL_load_verify_locations, and you will find the right place.
Though I don't recommend it, you could even look at s_client.c and s_server.c. They are located in <openssl dir>/apps. But the code can be difficult to read at times.
Generate two pairs of certificates/keys, one for the server and one for the client. Also create test.txt with any content.
To set up an SSL server that checks a client certificate, run the following command:
openssl s_server -cert server_cert.pem -key server_key.pem -WWW -port 12345 -CAfile client_cert.pem -verify_return_error -Verify 1
To test the server with client certificate, run the following command:
echo -e 'GET /test.txt HTTP/1.1\r\n\r\n' | openssl s_client -cert client_cert.pem -key client_key.pem -CAfile server_cert.pem -connect localhost:12345 -quiet
Alternatively you can use curl command:
curl -k --cert client_cert.pem --key client_key.pem https://localhost:12345/test.txt