Client Certificates not bind to specific IP or Domain? - ssl

The client certificate works if I generate the client certificate as below
openssl req -config openssl.cnf -new -nodes -subj '/C=RO/ST=Bucharest/L=Bucharest/O=XXX/emailAddress=wdw#gmail.com/CN=192.1.61.7' -keyout private/client.key -out client.csr
192.1.61.7 is the IP address of my client which uses the generated client certificate.
I want the client certificate to be used by machines from any IP addresses.
I tried CN=*.*.*.* and CN=*, neither works.
The server side is logstash tcp input filter which I can't change it.
Must the client certificate be bind to a specific IP or domains? how to generate a client certificate not binding to any IP and domain?

Must the client certificate be bind to a specific IP or domains?
No. But how things are handled depends on the PKI you are running. Each PKI can be different.
You can identify a user with the traditional Distinguished Name (DN) from RFC 4511, like Organization (O) and Organizational Unit (OU). Or you can identify them with an alternate name.
Here are the ways you can identify a subject by an alternate name. Its from RFC 5280, Section 4.2.1.6, Subject Alternative Name:
GeneralName ::= CHOICE {
otherName [0] OtherName,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
x400Address [3] ORAddress,
directoryName [4] Name,
ediPartyName [5] EDIPartyName,
uniformResourceIdentifier [6] IA5String,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER }
In the above, you are used to using dNSName for server certificates. Client certificates can use rfc822Name, directoryName, otherName and other GeneralName.
What you use depends on your PKI. I believe Microsoft client certificates use otherName and a custom OID. Also see How to encode a username in PKIX certificate on the Information Security Stack Exchange.
You might also want to search for Client Certificate GeneralName and do some reading.
How to generate a client certificate not binding to any IP and domain?
It depends on the tools you are using and your PKI.
At minimum, you are probably going to have to stop using the openssl command line tool without a CONF file because its too limited. You cannot set subject alternate names through command line options. Instead, you are going to have to use a CONF file.

Related

OpenSSL - Internally using the wrong certificates, externally is fine

I am seeing an odd issue with two of our externally facing systems where access over the web on HTTPS works, and uses the correct COMODO certificates, but internally it is using self-signed certificates.
The command being used to test this is:
openssl s_client -showcerts -connect wiki.domain.com:443
Externally, this returns:
Server certificate
subject=/OU=Domain Control Validated/OU=EssentialSSL Wildcard/CN=*.domain.com
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
Internally (from the local subnet) I see:
Server certificate
subject=/C=US/ST=Somewhere/L=Somecity/O=CompanyName/OU=Organizational Unit Name (eg, section)/CN=Common Name (eg, YOUR name)/emailAddress=Email Address
issuer=/C=US/ST=Somewhere/L=Somecity/O=CompanyName/OU=Organizational Unit Name (eg, section)/CN=Common Name (eg, YOUR name)/emailAddress=Email Address
Any idea why this would be happening please? I can't figure out what's going on here.
Please ask if you need further details, am happy to provide. Thank you.

Certificate hostname verification in java - subject alternative names

I am using a certificate with subject alternative names in the "Subject" field instead of x509 extensions.
A java client that I use still fails connecting to https url complaining that hostname in certificate didn't match. My understanding is as long as the hostname is listed in Subject Alt Names it should work.
Here is the format of the Subject field in the certificate
C=US,ST=.......CN=x.yz.com/emailAddress=a#b.com/subjectAltName=DNS.1=x2.y.com,DNS.2=x3.y.com
Is it necessary to define SAN as X509 extensions
The subjectAltName is expected to be an X509v3 extension of the certificate, not a part of the Subject field. Therefore, if you listed the SAN names into the Subject, your certificate is invalid.
Here's an example of a certificate that defines an SAN. This answer contains the list of allowed fields for a subject.

Securing a private IP address (https certificate)

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

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!

How to determine a server's list of CA certificates that it will accept from client?

According to https://wiki.jasig.org/display/CASUM/X.509+Certificates,
After the Server sends the certificate that identifies itself, it then can then send a list of names of Certificate Authorities from which it is willing to accept certificates.
I am wondering how to determine what this list is, and how to modify it.
The reason I am asking is that I am getting an infinite redirect between my server and my client after successful validation (i.e., the ticket stage), and I think it has to do with the CAS server not recognizing the CAS client's certificate (the client's certificate is self-signed).
If you want to see what this list is, you can use OpenSSL:
echo "" | openssl s_client -connect your.server:port
This will show various messages regarding the handshake, including the certificates and the list of CAs in the CertificateRequest message.
Ultimately, it's determined by the active X509TrustManager's getAcceptedIssuers() method. By default, this will be the list of Subject DNs of all your trust anchors (that is, the Subject DNs of all the certificates in your trust store).
Your client certificate will have to be verified by the server. This is normally done during the handshake by the trust manager, which (unless tweaked) will build a chain to a known CA (or at least known cert if it's the user cert itself) in the trust store.
Adding your self-signed certificate to your trust store should be sufficient. It doesn't have to be the cacerts file bundled with the JVM, you could make a copy of it and use the trust store settings of Apache Tomcat's connector to set it up.