I'm trying to connect to a service that requires a certificate for authorization. The process is that I send the service a CSR file. The service signs the CSR and sends me a certificate that I use for connection.
I generated the CSR by the following command line:
openssl req -new -nodes -newkey rsa:2048 -keyout cert.key -out cert.csr
I took the content of the cert.csr and sent to them. They generate the client certificate and I got a PEM file back.
I now try to connect using their certificate file in SSLCERT for curl() and providing the private key from cert.key as CURLOPT_SSLKEY - (which I got at step 1).
Fails with: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
What am I doing wrong in this process?
It works when I try with a received a test certificate including a private key from the service (self signed certificate). But when I use a certificate they generated from my CSR and then use my private key as key, it errors with handshake failure.
So I know it does not have something to do with that openssl / curl doesn't support v3/TLS etc. that others when researching for a solution found out their problem was.
Here is what I run:
curl -i -v --request POST https://service.com/ --cert clientcert.pem --key private_key.pem --cert-type pem --tlsv1.1 --insecure
* Connected to service.com (1xx.xxx.xxx.xx) port 443 (#0)
* 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, Request CERT (13):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS handshake, CERT verify (15):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS alert, Server hello (2):
* error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
* Closing connection 0
Running following versions: curl 7.35.0 (x86_64-pc-linux-gnu) libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3
Not a definite answer but too much to fit in comments:
I hypothesize they gave you a cert that either has a wrong issuer (although their server could use a more specific alert code for that) or a wrong subject. We know the cert matches your privatekey -- because both curl and openssl client paired them without complaining about a mismatch; but we don't actually know it matches their desired CA(s) -- because your curl uses openssl and openssl SSL client does NOT enforce that a configured client cert matches certreq.CAs.
Do openssl x509 <clientcert.pem -noout -subject -issuer and the same on the cert from the test P12 that works. Do openssl s_client (or check the one you did) and look under Acceptable client certificate CA names; the name there or one of them should match (exactly!) the issuer(s) of your certs. If not, that's most likely your problem and you need to check with them you submitted your CSR to the correct place and in the correct way. Perhaps they have different regimes in different regions, or business lines, or test vs prod, or active vs pending, etc.
If the issuer of your cert does match desiredCAs, compare its subject to the working (test-P12) one: are they in similar format? are there any components in the working one not present in yours? If they allow it, try generating and submitting a new CSR with a subject name exactly the same as the test-P12 one, or as close as you can get, and see if that produces a cert that works better. (You don't have to generate a new key to do this, but if you choose to, keep track of which certs match which keys so you don't get them mixed up.) If that doesn't help look at the certificate extensions with openssl x509 <cert -noout -text for any difference(s) that might reasonably be related to subject authorization, like KeyUsage, ExtendedKeyUsage, maybe Policy, maybe Constraints, maybe even something nonstandard.
If all else fails, ask the server operator(s) what their logs say about the problem, or if you have access look at the logs yourself.
What SSL private key should be sent along with the client certificate?
None of them :)
One of the appealing things about client certificates is it does not do dumb things, like transmit a secret (like a password), in the plain text to a server (HTTP basic_auth). The password is still used to unlock the key for the client certificate, its just not used directly to during exchange or tp authenticate the client.
Instead, the client chooses a temporary, random key for that session. The client then signs the temporary, random key with his cert and sends it to the server (some hand waiving). If a bad guy intercepts anything, its random so it can't be used in the future. It can't even be used for a second run of the protocol with the server because the server will select a new, random value, too.
Fails with: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
Use TLS 1.0 and above; and use Server Name Indication.
You have not provided any code, so its not clear to me how to tell you what to do. Instead, here's the OpenSSL command line to test it:
openssl s_client -connect www.example.com:443 -tls1 -servername www.example.com \
-cert mycert.pem -key mykey.pem -CAfile <certificate-authority-for-service>.pem
You can also use -CAfile to avoid the “verify error:num=20”. See, for example, “verify error:num=20” when connecting to gateway.sandbox.push.apple.com.
The solution for me on a CentOS 8 system was checking the System Cryptography Policy by verifying the /etc/crypto-policies/config reads the default value of DEFAULT rather than any other value.
Once changing this value to DEFAULT, run the following command:
/usr/bin/update-crypto-policies --set DEFAULT
Rerun the curl command and it should work.
In my case the cause of the error was that I only imported the certificate (e.g., cert1.pem) and the private key (e.g., privkey1.pem) into the keystore and to solve the issue, I had to import the full chain of certificates as well.
I got the following files from "Let's Encrypt" certification authority:
privkey1.pem
fullchain1.pem
chain1.pem
cert1.pem
So I did the following:
Join the content of three certificate files together into a file named all.pem:
cat cert1.pem chain1.pem fullchain1.pem > all.pem
Create my keystore containing the full certificate chain:
openssl pkcs12 -export -in all.pem -inkey privkey1.pem -out my_keystore.p12 -name mycertalias -CAfile chain1.pem -caname root
Related
I can use the openssl s_server command to accept TLS sessions from clients, and to require mutual TLS - i.e. request client certificate - using a command such as: -
openssl s_server -accept 4433 -cert myCert.crt -key -myKey.pem -Verify 2 -CAfile myCA.crt
When I connect from a client, I can see from tracing that s_client sends a certificate request, correctly stipulating the certificate contained within myCA.crt. However it seems that s_server will accept any client certificate, regardless of whether it was signed by myCA.crt or not - i.e. it doesn't care which client cert is sent.
Does anyone know if this is expected behaviour, or am I doing something wrong?
openssl s_server and s_client by default verify the peers certificate and show the verification status but don't stop on errors. If this is necessary use the -verify_return_error option.
Running this command inside wsl 2 windows delivers the below output.
Can anyone explain why there are mixed TLSv1.3 and TLSv1.2 IN and OUT and is this a potential reason as to why its unable to get local issuer certificate.
The Windows host OS is Enterprise
I have installed ca-certificates and ran update-ca-certificates
curl -v https://google.com:443/
* Trying 172.217.169.78...
* TCP_NODELAY set
* Connected to google.com (172.217.169.78) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
Are you using a network connection subject to monitoring or 'protection' such as antivirus, like one provided by a business, organization or school? If so you are probably getting a fake cert/chain from the interceptor.
Try openssl s_client -connect google.com:443 and look at the s:and i: lines under Certificate chain. (Many hosts today require SNI to respond correctly and if your OpenSSL is below 1.1.1 you need to add -servername x to provide SNI, but google is not one of them, and anyway since your curl is at least trying 1.3 it cannot be OpenSSL below 1.1.1.)
Or, if connecting from Chrome, Edge or IE (but maybe not Firefox) on the host Windows works normally, doubleclick the padlock and look at the cert chain to see if it leads to GlobalSign Root CA (as the real google does) or something else (like e.g. BlueCoat); if the latter the interceptor's root cert is installed in your host Windows store, but not the WSL system. You can export the cert from the host browser and put it in a file, and either use it manually with curl --cacert $file, or import it to the WSL system's truststore, but that depends on what system you are running in WSL which you didn't say.
Added: the mixture of TLS 1.3 and 1.2 in the logging info is probably because 1.3 uses the same record header version as 1.2 as a transition hack, with an extension that indicates it is really 1.3 only in the two Hello messages, and the callback probably doesn't deal with this.
Turns out there were missing certificates that once provided and installed it worked fine
It may sound very simple but I can't get it to work. I want to connect to an ftp with ssl certificate on windows with command line.
So I generate my certificate with IIS, I export it to "cer" format and after a lot of try I end with this command line :
curl -3 -v --cacert "XX\XX\test_certif.cer" --disable-epsv --ftp-skip-pasv-ip --ftp-ssl ftp://XXXXXXX --user XXXX
and after entering my password, i have this error :
Enter host password for user 'XXXXXX':
* Rebuilt URL to: ftp://XXXX/
* Trying XX::XX:XX:XX...
* TCP_NODELAY set
* Connected to XXXXX (XX::XX:XX:XX:XX) port 21 (#0)
< 220 Microsoft FTP Service
> AUTH SSL
< 234 AUTH command ok. Expecting TLS Negotiation.
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: XX\XX\test_certif.cer
CApath: none
* SSLv3 (OUT), TLS handshake, Client hello (1):
* Unknown SSL protocol error in connection to XXX:21
* Closing connection 0
curl: (35) Unknown SSL protocol error in connection to XXX:21
And i out of idea to make it work.
To go a little further my ftp work when i change ssl parameter of the ftp from "Needed ssl connexion" to "allowed ssl connexion" so the matter is not this way.
And Windows firewall is disable.
update i still work on it and i have now this command line :
curl -v --cacert "XX\XX\test_certif.cer" --ftp-ssl ftp://XXX --user XXX
with this output
Enter host password for user 'XXX':
* Rebuilt URL to: ftp://XXX
* Trying XX::XX:XX:XX:XX...
* TCP_NODELAY set
* Connected to XXX (XX::XX:XX:XX:XX) port 21 (#0)
< 220 Microsoft FTP Service
> AUTH SSL
< 234 AUTH command ok. Expecting TLS Negotiation.
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: XX\XX\test_certif.cer
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, Client hello (1):
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
got any idea?
Feel free to ask more info if needed ill update the question.
Thx by advance
I solve my issue. The matter was with the certificat générate by IIS not working with curl.
So i use OpenSSl to générate a key :
openssl genrsa -des3 -out key.pem -passout pass:password 1024
Then still use OpenSSL to générate a certificat with the key :
req -x509 -new -key key.pem -passin pass:password -days 3650 -out certificat.cer -config "C:\Program Files (x86)\GnuWin32\share\openssl.cnf"
-config is not necessary needed and is located on the folder of your openssl install
Now still with OpenSSL, convert key+certificat to pfx
pkcs12 -inkey key.pem -in certificat.cer -export -out iis_certificate.pfx
Now you can import the pfx certificate to IIS and use it to configure your FTP connection.
Create a pem file and copy past the content of certificat.cer inside him or just convert the file to pem. Lets name it "cacer.pem"
Now use curl to connect :
curl -v ftp://"IP or server name" --user « username » --ftp-ssl --cacert « Path of cacer.pem file »
Here is a little bonus, the command line to upload a file and set passord in the command line :
curl -upload-file “Path of file to transfert” ftp://"IP or server name" --user « user »: « password » --ftp-ssl --cacert " Path of cacer.pem file "
I've been asked to setup an FTPS connection, using a Client Authentication certificate. I'm the client, connecting to the server. I'm on a Windows7 workstation, behind a firewall. Some other info:
TLS 1.2 only
Passive mode only
My WAN IP is whitelisted
I'm connecting to a test URL, for now
There is no outbound firewall (only inbound rules)
Key should be sent in PEM format
I bought the certificate from Comodo, and generated the certificate with IE10. I exported the complete certificate from IE, with private key and extended properties, in the .pfx format.
I wrote a little batch file to extract the necessary parts from the .pfx, using OpenSSL:
::Extract the private key from the PFX
openssl pkcs12 -in comodo.pfx -nocerts -out encrypted.key
::Extract unencrypted private key
openssl rsa -in encrypted.key -out decrypted.key
::Extract the public cert in Base64 from the PFX
openssl pkcs12 -in comodo.pfx -clcerts -nokeys | openssl x509 -out comodo.cer
::Extract the chain bundle from the PFX
openssl pkcs12 -in comodo.pfx -nodes -nokeys -cacerts | openssl x509 -out comodo.crt
Using the extracted key parts, I have been trying various cURL (v7.46.0) commands to connect to the FTPS server. The connection keeps failing. My latest attempt is below. The error is almost always the same.
curl -3 -k -v --ftp-ssl --tlsv1.2 --ftp-ssl-reqd --ftp-pasv --key decrypted.key --cacert comodo.crt --cert comodo.cer --user REMOVED:REMOVED ftp://ftps.REMOVED/
An example of the output is:
> curl -3 -v -k --user REMOVED:REMOVED --ftp-ssl --tlsv1.2 --ftp-ssl-reqd --ftp-skip-pasv-ip --cert comodo.cer --key priv.pem ftp://ftps.REMOVED/
* Trying REMOVED_IP.
* Connected to ftps.REMOVED (REMOVED_IP) port 21 (#0)
< 220 Service ready for new user.
> AUTH SSL
< 234 Command AUTH okay; starting SSL connection.
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* Unknown SSL protocol error in connection to ftps.REMOVED:21
* Closing connection 0 curl: (35) Unknown SSL protocol error in connection to ftps.REMOVED:21
>pause
Press any key to continue . . .
Strangely, if I connect with FileZilla, i can seem to get past the TLS handshake, but then I get different errors when I try to upload a file. As this connection will ultimately be made with cURL, I've concentrated on using cURL.
I've been trying this for over a week. The server admin has bought a certificate from my provider, and can connect without issue. I'm stumped on this one. Any help would be greatly appreciated.
Moved this question to ServerFault, as there seemed to be quite a lot of similar topics on that community:
https://serverfault.com/questions/820851/debugging-curl-ftps-connection-using-client-authentication-certificates-openss
I'm very confused by this, and no doubt this is my misunderstanding or some such, but I'm trying to get my machine to talk to an upstream proxy, i'm using redsocks to transparently redirect to upstream.
Below we can see curl
root#Amachine:/# curl -v -k https://bower.herokuapp.com
* Rebuilt URL to: https://bower.herokuapp.com/
* Hostname was NOT found in DNS cache
* Trying 54.235.187.231...
* Connected to bower.herokuapp.com (54.235.187.231) port 443 (#0)
* 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 TLSv1.2 / ECDHE-RSA-AES256-SHA
* Server certificate:
* subject: C=US; ST=California; L=San Francisco; O=Heroku, Inc.; CN=*.herokuapp.com
* start date: 2014-01-21 00:00:00 GMT
* expire date: 2017-05-19 12:00:00 GMT
* issuer: CORPORATE PROXY
Issuer appears to be the corporate proxy. Breaking all ssl comms.
root#machine:/# openssl s_client -connect bower.herokuapp.com:443
CONNECTED(00000003)
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=California/L=San Francisco/O=Heroku, Inc./CN=*.herokuapp.com
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA
1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
What's baffling me is that they have different issuers. Granted curl seems to hide most of what is going on. I can specify the root ca path and openssl works, and gives me an ok, but curl somehow is using a different path
I'm actually not sure how to debug what on earth is happening in curl. I thought I would get a similar issuer. I may be misunderstanding how s_client works though, does anyone know what is happening?
You have a SSL interception proxy in your network and curl is using it while openssl does not use it, or the proxy does not intercept the connections. It is not clear from your description what the case is exactly, but it might be
that you are using different machines, and from one the connections get intercepted while on the other not
that the intercepting proxy will not intercept connections without server name indication (SNI). Curl does SNI while openssl does not the way you use it. Use the -servername argument to retry with SNI.
1) You used the -k option to curl, which makes it ignore the CA verification - but at least it's showing what would the problem be, an MITM SSL proxy.
Presumably you can't bypass it, in this case a better option might be to retrieve the "CORPORATE PROXY" CA itself, and make it a trusted CA on your workstation. This is generally not a good idea, as it's destroying any effort that the CA's made to verify the certificate subject. On the other hand corporate networks generally make this decision for you anyway.
2) openssl is complaining only because it does not check the CA chain by default. It also seems you're not on the same network and/or use a different set of proxies than with curl. You may learn this if you check the environment for http_proxy or similar:
# printenv|egrep -i '(http|proxy)'
Or, if all else fails, perhaps the curl you're using is hardwired to use a different socks proxy, you can check with strace, what IP address curl and openssl is connecting to. Look for the connect syscall use with:
# strace -f -e connect curl https://www.google.com:443
As you mentioned, openssl needs the -CApath CERTIFICATEDIR option to verify the issuers with the CA certificates specially named in the CERTIFICATEDIR. Apart from CERTIFICATEDIR, it's actually checking the system certificate directory as well which was provided by the distribution - so as a shortcut, something as simple can usually work:
# openssl s_client -CApath 1 -connect bower.herokuapp.com:443
1 will be checked as a directory for certificates, but if it does not exist, the system will be consulted. Other useful options you can find in the manual for s_client
-servername SNI
Will send a hostname option in the initial clienthello packet so that the server (and the corporate proxy) can better decide which certificate to use on the host.
-CAfile FILE
If you know there's only a single acceptable CA for the connection.
-showcerts
If you want to record and analyse all the certificates in PEM format.
-status
It asks the server to provide the OCSP status of its own certificate via OCSP stapling and openssl will verify if it is valid.
In my case I had environment variable https_proxy defining proxy, which curl was fetching and using, while openssl was not using it. Thus, corporate proxy was serving different issuers for the certificate. After adding command parameter -proxy to openssl, both curl and openssl were serving same certificate chains.