Nginx – Error with Python2 as a https client with an nginx server and SSL certificate chaining

nginxpythonssl

I am having trouble getting Nginx 1.8 working with my certificates so that I can use a python2 based https client. (requests.py, URLLib)

I've tried using Comodo and GlobalTrust wildcard certificates and chaining them on the server when setting up nginx but get this error from python.

requests.exceptions.SSLError: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

On Nginx I am using these directives to set up SSL:

ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;

ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:HIGH:!MD5:!aNULL:!EDH:!CAMELLIA;
ssl_protocols TLSv1.2 TLSv1.1 SSLv3;
#ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;

ssl_certificate /etc/nginx/alphassl/cert_bundle.crt;
ssl_certificate_key /etc/nginx/newkey/key.key;

and I have chained the certificates together on the server with

cat server.crt intermediate.crt root.crt > cert_bundle.crt

and for Comodo:

cat server.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > ssl_bundled.crt

It works fine with curl, chrome, and python3, but I need to be able to access it from python 2.7.6 with requests using something like

import requests
requests.get('https://server.com/', verify=True)

I can also make a request to https://store.ssl2buy.com/ from python2 which is the vendor of my cert so I believe it is a problem with the server and not the python client.

Additionally here is what I get when debugging with openssl

openssl s_client -connect g.peakdata.net:443 -CAfile /etc/ssl/certs/ca-certificates.crt

CONNECTED(00000003)
depth=0 OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.peakdata.net
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.peakdata.net
verify error:num=27:certificate not trusted
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.peakdata.net
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=*.peakdata.net
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFUTCCBDmgAwIBAgIQYc2m4QZ+tkek2fUe/Y7+1jANBgkqhkiG9w0BAQsFADCB
kDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxNjA0BgNV
BAMTLUNPTU9ETyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD
QTAeFw0xNTA1MDUwMDAwMDBaFw0xNjA1MTIyMzU5NTlaMFsxITAfBgNVBAsTGERv
bWFpbiBDb250cm9sIFZhbGlkYXRlZDEdMBsGA1UECxMUUG9zaXRpdmVTU0wgV2ls
ZGNhcmQxFzAVBgNVBAMUDioucGVha2RhdGEubmV0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA5Jor5AzXAxaUdc5e1Oim3Fmo5fpAv8Ug42r6mTM81JGV
lxi4ICQ/3FszeJARUs0KhaS4k6nnf0VsCudw2DDw0u3jfAstjhS5JCNLDFAY1D01
GzKp1EZ0iU9xsE8qa/6iPFQ9v9zVzQ72wimk00X32pPAGKrVr8L9w11JtfTrjP1Q
N6QygQBu7KcCtMRCA5eWinz3PnYdZL7analYasY2sY+/tp5iQJasiOfuXKO82of+
wBV5wsh1igR1BzwKOAhU8IYN1pKilvbhMb6qeMP3CAZMe205ACQavqfq4a2aK1pR
oqK9r9jiqzUj4sULbTVS+Kd6OqydtSJF8xaUU/Di7QIDAQABo4IB2TCCAdUwHwYD
VR0jBBgwFoAUkK9qOpRaC9iQ6hJWc99DtDoo2ucwHQYDVR0OBBYEFEifaFvh1SCN
ceI/gwjjeCiiW7ArMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysGAQQBsjEB
AgIHMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BT
MAgGBmeBDAECATBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vY3JsLmNvbW9kb2Nh
LmNvbS9DT01PRE9SU0FEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3Js
MIGFBggrBgEFBQcBAQR5MHcwTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jcnQuY29tb2Rv
Y2EuY29tL0NPTU9ET1JTQURvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5j
cnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTAnBgNVHREE
IDAegg4qLnBlYWtkYXRhLm5ldIIMcGVha2RhdGEubmV0MA0GCSqGSIb3DQEBCwUA
A4IBAQB2m/qah9mHPUQn3yRd3LHRHIigxdDySh5Rb75255I2AuSPQ3HQoi7vt0TB
0oOgRbdZ2RuLtlZVd6nOfQ24dh0rJ5JOzXDAb4hpfsDimpvzEMoqPudJpJgbjLJc
Bhx30ny0MecheoS/In1t59bw8RKZUukyGRqWHsQDbRq5F2MUWc7WFokydmTY1o9o
EQBKdOykiYRS07M7oqFc5weyCnii/dE4WtT7g4gA5zQsvPzsEmeKqvxY5RI8eg1R
8cF/Mr5iov9xGyPelgHwgHroE34G9YclxXVeyDcUlSWUK2j30M28h/feGkVMj+U4
+SZk/Ek5WhfUJmgy+b6JTtB8/l67
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.peakdata.net
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
No client certificate CA names sent
---
SSL handshake has read 2036 bytes and written 427 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-RC4-SHA
    Session-ID: 8ACFB450411BE9FF0B26CAA57047D80D9A146F41CDD093C8247698216BCF6FE1
    Session-ID-ctx: 
    Master-Key: 515A3952C76176757C51EA8B8FA35FC71C2CD496ABDE0D6E1870E04D1B020F786267D761C7A38051EFA14DE552D10F53
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 600 (seconds)
    TLS session ticket:
    0000 - d7 35 92 7d 59 5c ba 5c-9a 4d 79 c7 b5 b6 3e 1a   .5.}Y\.\.My...>.
    0010 - df 9a 5f de f4 af 9d 29-0e a8 d1 23 01 08 aa 64   .._....)...#...d
    0020 - 81 0f 4d 6f e2 a6 19 3e-ca 7e a9 9f d3 c8 a0 96   ..Mo...>.~......
    0030 - ce a1 37 a3 99 96 da 26-6f 83 ff 41 96 41 dc 17   ..7....&o..A.A..
    0040 - d7 11 6c be 50 1f 0c 7d-ad e5 0f 34 d8 fd db be   ..l.P..}...4....
    0050 - 87 3e fe 05 92 06 c8 0e-fc fc cc 8f f6 ae 08 a6   .>..............
    0060 - a3 3e c1 3e 61 4a fd 41-1f 5b 24 8d 97 6a aa e3   .>.>aJ.A.[$..j..
    0070 - 36 6b 68 1d f2 31 e7 da-05 29 59 1b 8f 21 38 25   6kh..1...)Y..!8%
    0080 - dc 67 8e bf 8e 02 63 26-43 f1 c3 9e 7c 78 e5 e3   .g....c&C...|x..
    0090 - cd ea 63 77 02 03 6a 01-10 45 84 c1 d9 73 c4 b0   ..cw..j..E...s..
    00a0 - e7 2d 86 a8 72 d8 ed b9-05 5c 2c a1 4b 66 8f 8a   .-..r....\,.Kf..

    Start Time: 1431626677
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

Thanks

Best Answer

This is a combination of a bad server setup together with missing support for SNI (Server Name Indication) in python 2.7.6. SNI is needed to support multiple certificates on the same IP address. SNI support is in python3 and was added in version 2.7.9 too, but is not in version 2.7.6.

If you connect to the server without SNI you get only the certificate, but not the trust chain:

$ openssl s_client -connect g.peakdata.net:443
...
Certificate chain
 0 s:/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.peakdata.net
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA

If instead you connect to the server with SNI you get the full chain, including the root-CA (which you don't need to include, clients will ignore it):

$ openssl s_client -connect g.peakdata.net:443 -CAfile /etc/ssl/certs/ca-certificates.crt -servername g.peakdata.net
...
Certificate chain
 0 s:/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.peakdata.net
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
 3 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root

Since python3 and the browser use SNI they will succeed. Python 2.7.6 will instead not use SNI and get the right certificate but with the missing chain. Because of that it is not able to verify the certificate against the local root CA's.