This is the flow I use to setup my HTTPS server with SSL.
It works perfectly on Windows, OS X and Ubuntu 13. But it's failing to work on Ubuntu 14 only and I don't know why.
It's not the full code once it's very big, but I can complete with more details if necessary.
SSL_library_init();
m_sslContext = SSL_CTX_new( SSLv23_server_method() );
SSL_CTX_use_certificate_chain_file( m_sslContext, "path/to/certificate.crt" );
SSL_CTX_use_PrivateKey_file( m_sslContext, "path/to/privatekey.pem", SSL_FILETYPE_PEM );
m_mainSocket = ::socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ) );
...
::listen( m_mainSocket, SOMAXCONN );
...
SOCKET childSocketHandle;
while ( ( childSocketHandle = ::accept( m_mainSocket, ... ) ) > 0 )
{
sslChildSocket = SSL_new( m_sslContext );
SSL_set_fd( sslChildSocket, childSocketHandle );
SSL_set_accept_state( sslChildSocket );
...
SSL_read( sslChildSocket, bufferIn, sizeof( bufferIn ) );
...
SSL_write( sslChildSocket, bufferOut, sizeof( bufferOut ) ) );
}
The problem is: when I try to connect from a browser (Google Chrom), it says:
Unable to make a secure connection to the server. This may be a problem with the server, or it may be requiring a client authentication certificate that you don't have. Error code: ERR_SSL_PROTOCOL_ERROR
Other browsers say similar messages...
When I try to connect from wget, I get:
wget https://example.com:443/
--2014-05-01 17:01:33-- https://example.com:443/
Resolving example.com (example.com)... 127.0.1.1
Connecting to example.com (example.com)|127.0.1.1|:443... connected.
ERROR: cannot verify example.com's certificate, issued by ‘/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=00000000’:
Unable to locally verify the issuer's authority.
To connect to example.com insecurely, use `--no-check-certificate'.
I just changed the serial of the certificate to 00000000.
So... If I finally follow the wget message and do...
wget https://example.com:443/ --no-check-certificate
... THEN the server works perfectly!
So, the conclusion I get is: the server itself IS working, but the handshake has some problem with the SSL certificate. The certificate is valid, used in other servers, Apache accepts it perfectly and as I told, once again, this same implementation works on Windows, OS X and Ubuntu 13. This problem is only happening on Ubuntu 14.
Things I tried to do:
- I tried to update the OpenSSL [compiled it by myself] but nothing
happened. - I tried to try other methods instead of
SSLv23_server_method()
, nothing happened - I compiled in Ubuntu 13 and executed in Ubuntu 14 (AND THIS WORKED!)
Weard (item 3) is that if I compile in Ubuntu 13 and run on Ubuntu 14, it works! So maybe some Ubuntu 14 static library is with problem?
Is my SSL implementation correct? What else can be done so I can fix it for Ubuntu 14 and my server work everywhere?
--
I do openssl s_client -connect example.com:443
and get:
CONNECTED(00000003)
140735262471008:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 322 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---
ERR_SSL_PROTOCOL_ERROR
indicates the client and server could not agree on a protocol - SSLv3, TLS 1.0, etc. I believe it corresponds to theprotocol_version
alert of TLS. See RFC 5246, Section 7.2.OpenSSL has been TLS 1.2 since 1.0.1. See the OpenSSL CHANGELOG. However, Ubuntu prior to 14 disabled TLS 1.1 and TLS 1.2 for interop reasons. See Ubuntu 12.04 LTS: OpenSSL downlevel version and does not support TLS 1.2. Ubuntu 14 (and subsequent) enables TLS 1.1 and TLS 1.2. (And TLS 1.3 is around the corner: The Transport Layer Security (TLS) Protocol Version 1.3 (draft-ietf-tls-rfc5246-bis-00)).
There could be another issues if you have to go through a proxy. The problem is related to the
ClientHello
size. TheClientHello
size increased with TLS 1.1 and TLS 1.2 because of additional cipher suites (more correctly, TLS 1.2 because TLS 1.1 did not add any cipher suites). Size should not matter except some proxies have fixed size buffer and other hard coded limits that simply break the exchange. It is an issue with some F5 and Ironport appliances.You can test for TLS 1.2 and
ClientHello
size sensitivity withs_client
:The above connects with TLS 1.2 and uses only 2 cipher suites (4 bytes). If it connects with 2 cipher suites, then drop the
-cipher
and see if it connects with the 80+ (over 160 bytes) that are builtin.If it does not connect with TLS 1.2, then try either
-tls1
or-ssl3
.EDIT: You problem is an ancient server and TLS 1.1 and TLS 1.2. See below on the steps to isolate the problem.
You have three potential fixes.
First
The first fix is to have the server upgrade to something that's not ancient. If its a proxy, then fix the proxy.
Second
If you need to modify the protocol versions, then perform the following to get SSLv3 or YLS 1.0 only:
Third
If you need to modify the cipher suite list:
EDIT: You problem is an ancient server and TLS 1.1 and TLS 1.2. You need to use (1) from above, or (2) from above. Ideally, the ancient server would be fixed so everyone can benefit.
TLS 1.2 does not work:
TLS 1.1 does not work:
TLS 1.0 does work:
SSL v3 does work:
This is a different issue.
wget
is likely avoiding the issue by incorporating one of the fixes above. A Wirehsark trace would tell you.Also, if you provided a real server name, we could help you identify the Root CA you should be using (to avoidUnable to locally verify the issuer's authority
).Here's what I am seeing with
s_client
:So you need Go Daddy Class 2 Certification Authority. You can get that from Go Daddy Repository, SSL Certificate Information. The file is
gd-class2-root.crt
, and you can pass it tos_client
and the result isVerify return code: 0 (ok)
: