ssl3_get_server_certificate:wrong certificate type (gost https server) #447

Closed
denizzzka opened this Issue Sep 21, 2015 · 23 comments

Projects

None yet

7 participants

@denizzzka

Setup:

$ cat /etc/ssl/openssl.cnf
[...]
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)

openssl_conf = openssl_def

[ new_oids ]

[..]

[openssl_def]
engines = engine_section

[engine_section]
gost = gost_section

[gost_section]
engine_id = gost
#soft_load=1
default_algorithms = ALL
CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
/EOF

Checking:

$ curl -v https://zakupki.gov.ru/pgz/services/upload
* Hostname was NOT found in DNS cache
*   Trying 194.105.148.87...
* Connected to zakupki.gov.ru (194.105.148.87) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS Unknown, Unknown (22):
* SSLv3, TLS handshake, Client hello (1):
* SSLv2, Unknown (22):
* SSLv3, TLS handshake, Server hello (2):
* SSLv2, Unknown (22):
* SSLv3, TLS handshake, CERT (11):
* SSLv2, Unknown (21):
* SSLv3, TLS alert, Server hello (2):
* error:0609E09C:digital envelope routines:PKEY_SET_TYPE:unsupported algorithm
* Closing connection 0
* SSLv2, Unknown (21):
* SSLv3, TLS alert, Client hello (1):
curl: (35) error:0609E09C:digital envelope routines:PKEY_SET_TYPE:unsupported algorithm
$ curl --version
curl 7.38.0 (x86_64-pc-linux-gnu) libcurl/7.38.0 OpenSSL/1.0.2d zlib/1.2.8 libidn/1.29 libssh2/1.4.3 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smtp smtps telnet tftp 
Features: AsynchDNS IDN IPv6 Largefile GSS-API SPNEGO NTLM NTLM_WB SSL libz TLS-SRP 

$ openssl ciphers|tr ':' '\n'|grep GOST
GOST2001-GOST89-GOST89
GOST94-GOST89-GOST89
@denizzzka denizzzka closed this Sep 21, 2015
@denizzzka denizzzka reopened this Sep 21, 2015
@frenche
Contributor
frenche commented Sep 21, 2015

Perhaps try to specfy the ciphers in CLI (see man).

@denizzzka

cURL was updated:

$ curl --version
curl 7.44.0 (x86_64-pc-linux-gnu) libcurl/7.44.0 GnuTLS/3.3.17 zlib/1.2.8 libidn/1.29 libssh2/1.4.3 nghttp2/1.3.2 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets 

when:

$ curl -v --engine gost --ciphers GOST2001-GOST89-GOST89 https://zakupki.gov.ru/pgz/services/upload
curl: (4) A requested feature, protocol or option was not found built-in in this libcurl due to a build-time decision.

Hmmm

$ curl --engine list
Build-time engines:
  <none>
$ openssl engine
(dynamic) Dynamic engine loading support
(gost) Reference implementation of GOST engine
$ openssl s_client -connect zakupki.gov.ru:443
[...]
SSL-Session:
    Protocol  : TLSv1
    Cipher    : GOST2001-GOST89-GOST89
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 516E6E94EF26EA04FCB94F648D6063D6261753E785D6D7B617ADBE93A34B68015CF66691BF88D5CC3E0298C84A05FD7B
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1442856403
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---
@denizzzka

curl: (4) A requested feature, protocol or option was not found built-in in this libcurl due to a build-time decision.

@denizzzka denizzzka closed this Sep 21, 2015
@denizzzka

Sorry, it is me again

On latest version of a cURL I am catched error:
SSL routines:ssl3_get_server_certificate:wrong certificate type

$ ./src/curl -v -k --engine gost https://zakupki.gov.ru/pgz/services/upload
* set default crypto engine 'gost'
* STATE: INIT => CONNECT handle 0x743028; line 1090 (connection #-5000) 
* Added connection 0. The cache now contains 1 members
*   Trying 194.105.148.87...
* STATE: CONNECT => WAITCONNECT handle 0x743028; line 1143 (connection #0) 
* Connected to zakupki.gov.ru (194.105.148.87) port 443 (#0)
* STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x743028; line 1240 (connection #0) 
* Marked for [keep alive]: HTTP default
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* STATE: SENDPROTOCONNECT => PROTOCONNECT handle 0x743028; line 1254 (connection #0) 
* TLSv1.0 (IN), TLS handshake, Server hello (2):
* TLSv1.0 (IN), TLS handshake, Certificate (11):
* TLSv1.0 (OUT), TLS alert, Server hello (2):
* error:1409017F:SSL routines:ssl3_get_server_certificate:wrong certificate type
* Marked for [closure]: Failed HTTPS connection
* Curl_done
* Closing connection 0
* The cache now contains 0 members
* TLSv1.0 (OUT), TLS alert, Client hello (1):
* Expire cleared
curl: (35) error:1409017F:SSL routines:ssl3_get_server_certificate:wrong certificate type

But server certificate looks ok (for me)

@denizzzka

Additional info about server cert:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 958144 (0xe9ec0)
    Signature Algorithm: GOST R 34.11-94 with GOST R 34.10-2001
[...]
        Subject Public Key Info:
            Public Key Algorithm: GOST R 34.10-2001
[...]
                Parameter set: id-GostR3410-2001-CryptoPro-XchA-ParamSet

Latest string is differs from openssl config string:

$ cat /etc/ssl/openssl.cnf |grep CRYPT_PARAMS
CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet

It is important in this case?

@denizzzka

but with CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet openssl connection works fine:

$ openssl s_client -connect zakupki.gov.ru:443
@denizzzka

Tried to use libcurl with same result (dlang code):

import std.net.curl;
import std.stdio;

void main()
{
    auto r = HTTP("zakupki");
    r.handle.set(etc.c.curl.CurlOption.sslengine, "gost");
    r.handle.set(etc.c.curl.CurlOption.verbose, 1);
    r.verifyPeer = false;
    r.verifyHost = false;

    string content = std.net.curl.post("https://zakupki.gov.ru/pgz/services/upload", "asd", r).idup; 

    writeln(content);
}
$ dub run
Performing "debug" build using dmd for x86_64.
zkp ~master: building configuration "production"...
Linking...
Running ./zkp 
*   Trying 194.105.148.87...
* Connected to zakupki.gov.ru (194.105.148.87) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* error:1409017F:SSL routines:ssl3_get_server_certificate:wrong certificate type
* Closing connection 0
std.net.curl.CurlException@std/net/curl.d(3705): SSL connect error on handle 1D090F0
----------------
@denizzzka denizzzka reopened this Sep 21, 2015
@denizzzka denizzzka changed the title from cURL CLI: please add support of GOST ciphers to ssl3_get_server_certificate:wrong certificate type (gost https server) Sep 21, 2015
@bagder bagder added the SSL/TLS label Sep 21, 2015
@bagder
Member
bagder commented Sep 21, 2015

To me it sounds like you need to use the openssl engine 'gost' and then you should be able to specify that as a cipher.

@denizzzka

To me it sounds like you need to use the openssl engine 'gost'

already use it

and then you should be able to specify that as a cipher.

cipher detected by openssl automatically, probably

"SSL routines:ssl3_get_server_certificate:wrong certificate type" is error from openssl library. But in openssl CLI request works fine.

@bagder
Member
bagder commented Sep 21, 2015

Then I suggest some good old fashioned debugging to figure out where it goes wrong and why...

@denizzzka

I wrote with hope that anybody point out to an issue in my actions...
I did not have enough skill to understand the difference between several types of GOST certificates etc.

@bagder
Member
bagder commented Sep 21, 2015

I don't know either. Never used gost.

The SSL_connect() call on line vtls/openssl.c:2078 returns -1 during the handshake for some reason. Clearly there's something missing. My local openssl build has an engine called 'gost' but that's about all I know.

@ghedo
Member
ghedo commented Sep 21, 2015

What version of OpenSSL are you using? Anyway, if the parameters in openssl.cnf are needed, the problem is probably that curl doesn't load it. I think you you can define CRYPT_PARAMS as an environment variable though, like:

$ CRYPT_PARAMS=id-Gost28147-89-CryptoPro-A-ParamSet ./src/curl -v -k --engine gost https://zakupki.gov.ru/pgz/services/upload

I haven't tested this though.

@bagder
Member
bagder commented Sep 21, 2015

I tested this, but unfortunately it doesn't work either. This is with openssl from git master from just a few minutes ago.

@denizzzka

What version of OpenSSL are you using?

1.0.2d-2 from Debian experimental

@jay
Member
jay commented Sep 22, 2015

TL;DR This is a bug, try the fix here (Edit 2015-09-28: Fix landed in 69b8905).

Long part:
OpenSSL isn't sending the GOST ciphers in the client cipher list. The GOST cipher methods aren't available in OpenSSL's method table because we call the SSL initialization before the config file is loaded. The initialization creates the method table and sets the GOST entry in the table to NULL.

https://github.com/bagder/curl/blob/curl-7_44_0/lib/vtls/openssl.c#L708-L729
https://github.com/openssl/openssl/blob/OpenSSL_1_0_2d/ssl/ssl_ciph.c#L423-L424

The easy solution for this is we initialize all algorithms after loading the config file. That was proposed in #206 several months ago but ultimately no change was made. I was unsure about it at that time and deferred. I think this is a pretty good use case for why we should take another look. To that end I've resurrected @ex-troll's patch which fixes the issue for me.

(Edit 2015-09-28: Fix landed in 69b8905)

This may be related just to a dynamic module being loaded due to config because I found someone on the internet has been using GOST successfully, presumably without this patch.

I needed the patch though.

curl 7.45.0-DEV (i386-pc-win32) libcurl/7.45.0-DEV OpenSSL/1.0.2d
Protocols: dict file ftp ftps gopher http https imap imaps ldap pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS Debug Largefile NTLM SSL

I didn't need to set ciphers or engine, I did curl -v -k https://zakupki.gov.ru and it works.

* SSL connection using TLSv1.0 / GOST2001-GOST89-GOST89

Take caution using the -k switch, it disables verification. I strongly urge you to obtain the CA certificate from the issuer (УЦ Федерального казначейства -- see the cert details for an e-mail and street address) and verify it is the right certificate, and then stop using -k and pass that cert with --cacert. If you really can't obtain the CA you could try public key pinning to add some security. You'll need to contact the company or government to verify you have the right server certificate before making the hash. The hash I got is:

curl -v -k https://zakupki.gov.ru --pinnedpubkey sha256//PTKzmgDvP4Y1EBBHUWsCS7CGkwM9DHKS+9xHGJ4Vyw4=
@denizzzka

Take caution using the -k switch, it disables verification. I strongly urge you to obtain the CA certificate from the issuer (УЦ Федерального казначейства

Of course! -k was used only for ease of explanation here.

@denizzzka

This is a bug, try the fix here.

This patch is work for me! Thanks!

@bagder
Member
bagder commented Sep 28, 2015

I think the diff looks fine and shouldn't cause any problems. Will you merge @jay to give us a few days to test this before the pending release?

@jay jay added a commit that referenced this issue Sep 29, 2015
@ex-troll @jay ex-troll + jay openssl: Fix algorithm init
- Change algorithm init to happen after OpenSSL config load.

Additional algorithms may be available due to the user's config so we
initialize the algorithms after the user's config is loaded.

Bug: #447
Reported-by: Denis Feklushkin
69b8905
@jay
Member
jay commented Sep 29, 2015

Will you merge @jay to give us a few days to test this before the pending release?

Ok, landed in 69b8905.

@ex-troll
Contributor

👍

@bagder bagder closed this Sep 30, 2015
@jgsogo jgsogo added a commit to jgsogo/curl that referenced this issue Oct 19, 2015
@ex-troll @jgsogo ex-troll + jgsogo openssl: Fix algorithm init
- Change algorithm init to happen after OpenSSL config load.

Additional algorithms may be available due to the user's config so we
initialize the algorithms after the user's config is loaded.

Bug: curl#447
Reported-by: Denis Feklushkin
843756d
@solardatov
solardatov commented Jul 5, 2016 edited

Catching up,
Is this fix applicable only for case when we use curl + openssl and loading crypto engines from openssl.cnf ? I have built libcurl 7.48 and libcrypto + libssl from openssl 1.0.1s with gost enabled for android platform. (I believe 1.0.1s has gost built-in and to enable gost support we just need to DO NOT specify no-gost flag) Btw, my libcrypto part of Android.mk is below. So, I believe now gost engine should be loaded in ENGINE_load_builtin_engines, but I still see the the curl error 35 when I try to make https request to the web server with gost cert. The code is in C++ but it does exactly the same as D code few posts above. I have feeling that I understand something wrong, maybe you can point me on the wrong steps I do?

My crypto.mk: https://gist.github.com/solardatov/8b57675f8b2e3a736de020f1f0ca078a

My code sample how I do https request: https://gist.github.com/solardatov/fe6b1488b12a2cef4b94aebbf1c5d533

Btw, curl_easy_setopt(pCurl, CURLOPT_SSLENGINE, "gost") always fail with CURLE_SSL_ENGINE_NOTFOUND

@jay
Member
jay commented Jul 5, 2016

@solardatov Yes, the fix is libcurl >= 7.45.0 will init algorithms after loading the conf. Your issue appears to be unrelated so please don't continue to discuss it in this thread.

Before filing an issue for this you should ask on the libcurl mailing list and see if anyone there is using GOST and can help you. Note libcurl also returns CURLE_SSL_ENGINE_NOTFOUND when it is built with OpenSSL but without engine support, and there should be an error message like "SSL Engine not supported".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment