Some HTTP clients accept this certificate, and others do not. What could make the difference?
Java rejects it.
((javax.net.ssl.HttpsURLConnection)new java.net.URL("https://www.lucidpress.com")
.openConnection())
.getInputStream()
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject
alternative DNS name matching www.lucidpress.com found. at
sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at
sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1715) at
sun.security.ssl.Handshaker.fatalSE(Handshaker.java:257) at
sun.security.ssl.Handshaker.fatalSE(Handshaker.java:251) at
sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1168)
at
sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:609) at
sun.security.ssl.Handshaker.process_record(Handshaker.java:545) at
sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:963) at
sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1208)
at
sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1235)
at
sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1219)
at
sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:440)
at
sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at
sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1139)
at
sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
Python requests rejects it.
import requests
requests.get('https://www.lucidpress.com')
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 55, in get
return request('get', url, **kwargs) File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 44, in request
return session.request(method=method, url=url, **kwargs) File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 456, in request
resp = self.send(prep, **send_kwargs) File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 559, in send
r = adapter.send(request, **kwargs) File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 382, in send
raise SSLError(e, request=request) requests.exceptions.SSLError: hostname 'www.lucidpress.com' doesn't match either of '*.lucidchart.com', 'lucidchart.com'
cURL accepts it.
$ curl -v https://www.lucidpress.com
- About to connect() to www.lucidpress.com port 443 (#0)
- Trying 54.236.129.63… connected
- successfully set certificate verify locations:
- CAfile: none CApath: /etc/ssl/certs
- SSLv3, TLS handshake, Client hello (1):
- SSLv3, TLS handshake, Server hello (2):
- SSLv3, TLS handshake, CERT (11):
- SSLv3, TLS handshake, Server key exchange (12):
- SSLv3, TLS handshake, Server finished (14):
- SSLv3, TLS handshake, Client key exchange (16):
- SSLv3, TLS change cipher, Client hello (1):
- SSLv3, TLS handshake, Finished (20):
- SSLv3, TLS change cipher, Client hello (1):
- SSLv3, TLS handshake, Finished (20):
- SSL connection using DHE-RSA-AES256-SHA
- Server certificate:
- subject: OU=Domain Control Validated; CN=*.lucidpress.com
- start date: 2014-05-12 16:20:34 GMT
- expire date: 2015-07-09 22:19:45 GMT
- subjectAltName: www.lucidpress.com matched
- issuer: C=US; ST=Arizona; L=Scottsdale; O=GoDaddy.com, Inc.; OU=http://certs.godaddy.com/repository/; CN=Go Daddy Secure
Certificate Authority – G2- SSL certificate verify ok.
wget rejects it.
wget https://www.lucidpress.com
–2014-08-09 19:55:41– https://www.lucidpress.com/ Resolving www.lucidpress.com (www.lucidpress.com)… 107.23.98.6, 54.236.129.63,
54.88.154.168 Connecting to www.lucidpress.com (www.lucidpress.com)|107.23.98.6|:443… connected. ERROR: no
certificate subject alternative name matches requested host name
'www.lucidpress.com'. To connect to www.lucidpress.com insecurely, use
'–no-check-certificate'.
Chrome, FF, and IE accept it.
Why is the behavior different?
Best Answer
The short answer: load balancing, virtual hosting and SNI.
The long answer... first, here's an analysis of the certificate. We need to go though this to ensure there's no obvious mistakes.
From the dump below, there's a wildcard DNS name in the Common Name. Placing a DNS name in the CN is deprecated by both the IETF and the CA/Browser Forums. A "friendly name" should be placed in the CN because its displayed to the user. While its deprecated, its not forbidden.
Instead, DNS names should go in the Subject Alternate Name. There should be two of them. The first would be
lucidpress.com
and the second would be*.lucidpress.com
. You need justlucidpress.com
because the wildcard needs to match a label.For reference, the IETF deprecates a DNS name in the CN in RFC 6125 Section 3.1 Server Identity; and Section 6.4.4 Checking of Common Names.
The CA/Browser Forums deprecates a DNS name in the CN in Baseline Requirements (BR) Section 9.2.2 Subject Common Name Field. Also, according to the CA/B, the Subject Alternate Name is required. See Section 9.2.1 Subject Alternative Name Extension.
Related: RFC 6125, Section 6.4.3, also does not allow the matching of
*.lucidpress.com
tolucidpress.com
. The CA/B BR covers wildcards in Section 11.1.3, but it does not discuss matching rules.With the background information above and the certificate below, here's what is going on.
You have 2 names in the default certificate. Its served by default by Apache because its the first virtual host in the configuration file.
lucidchart.com
*.lucidchart.com
You have 2 names in the Lucid Press' certificate.
lucidpress.com
*.lucidpress.com
I think the difference is Server Name Indication (SNI). Its a TLS extension, so you need TLS 1.0 or above. Those that have no trouble get the Lucid Press certifcate and use TLS 1.0 or above with SNI; those that have trouble get the default certificate and use SSLv3 or no SNI. Windows XP will use TLS 1.0 but not SNI, so its experienced often in the field due to the deployment base.
The browsers accept it because they are using TLS 1.0 or above and sending the SNI extension. Because SNI allows your Apache server to select the proper certificate during the handshake, there are no name matching problems.
Java rejects it because it uses SSLv3, even when you say
SSLContext.getInstance("TLS");
. You have to jump through some hoops to ensure you really get TLS 1.0 and above. There's a few questions on Stack Overflow about it. See, for example, Which Cipher Suites to enable for SSL Socket?.Python
rejects it because I'm guessing you are using 2.x, or you are allowing SSLv3. You need 3.0 or above to get SNI. See Python 3 Support? on the Python FAQ.wget
added support for SNI in version 1.14. I suspectwget
is not enabling its or using SSLv3.cURL
likely ensures SNI is used if available. Daniel is very thorough, and he tries to ensure a trouble free experience and secure posture out of the box.In the OpenSSL dump, the options of interest are
-tls1 -servername
. You can get TLS without SNI by omitting-servername
. So you need bothtls1
and-servername <host>
.If interested, this is from
sslscan
: