I wish to verify a server's certificate. I have a boost::asio::ssl::context
This successfully verifies the certificate:
context.load_verify_file("E:\\a\\windows\\Path\\to\\certificate\\9207bca9.0");
However, I do not wish to explicitly specify the files to verify certificates against. I want to be able to put them in one directory, and tell the context to use the files in that folder to verify the certificates. So I do this instead:
context.add_verify_path("E:\\a\\windows\\Path\\to\\certificate");
And verification is unsuccessful
Note: The file name 9207bca9.0: 9207bca9 is the hash of the subject of the CA certificate, and its extension is '.0' to satisfy the requirements of the add_verify_path method found here (also the only contents of this file are the root certificate. Keep in mind I have been successful in verifying certificates with this file):
http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/reference/ssl__context/add_verify_path/overload1.html
Any suggestions?
There are only two possibilities that I can think of, the first is that your hash value is incorrect, this can be verified like so:
openssl x509 -noout -hash -in ca-certificate-file
The second is that there is some error in your directory configuration preventing OpenSSL from using the CA directory, for example, permissions and ownership are important on Linux systems, not sure to what extent these matter on Windows platforms. The only way to pinpoint this sort of error is to trace the verification stage via a debugger and observe the code directly as it scans the directory.
Related
I'm writing a cross platform websocket client using mbedtls. Now I'm left with the burden of feeding a suitable pack of trustet root certificates to the mbedtls API which looks like this:
/* Structure to load trusted root certs into. */
mbedtls_x509_crt ca_certs;
mbedtls_x509_crt_init(&ca_certs);
/* Parse the file with root certificates. */
if (mbedtls_x509_crt_parse_file(&ca_certs, "trusted_certs.pem") != 0) {
exit(EXIT_FAILURE);
}
/* Set the certificates as trusted for this session. */
mbedtls_ssl_conf_ca_chain(&conf, &ca_certs, NULL);
It's pretty straightforward, I just need to concatenate all found certificates and feed it into the function.
I know that on my embedded system (esp32) which is also using
mbedtls, there's already a library that provides me with the
system-wide CA store, so that should be no problem.
On linux systems I know that the certificate bundle will mostly
reside under /etc/ssl/certs. Question: Is this true for every
(major) distribution?
On Windows I frankly have no idea. There seems to be something like a certificate store, but how do I get my certificates out of there?
On Apple I don't really care (yet) <3
A reference codebase would also be very helpful!
I'm trying to send a client certificate to a webserver that requires it.
If I make the request from the command line curl command, it works.
I pass --key filename and --cert filename and everything works fine.
When I do it from the libcurl library, I do this:
curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert_file.c_str());
curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key_file.c_str());
Same exact thing, the server says the client certificate is missing.
I tried doing things like giving it a nonexistent filename in hopes of provoking some other error messages that might give me a clue what's wrong, but libcurl (and command line curl for that matter) with verbose on don't yield any error messages when something goes wrong. Surely there's a failure case, but nothing is yielding me any kind of error to follow up on.
Is there any way to get more diagnostics out of libcurl?
Pulling what's left of my hair out here.
This is on windows 10 by the way, we built libcurl ourselves. SSL is built in, https connections work, I just can't get it to send the client cert and there's no indication what's going wrong.
UPDATE:
I think I found my problem...
https://curl.haxx.se/docs/todo.html#Add_support_for_client_certifica
Does anybody know how to make this work?
Okay so I worked something out.
It turns out you can build libcurl on windows with openssh or schannel.
If you use schannel, then the CURLOPT_SSLCERT and CURLOPT_SSLKEY do nothing. (no warning or error mind you.)
Maybe they do do something, in relation to schannel, but I couldn't find any docs on that.
So in short if you build libcurl with openssl you can supply a client cert to send in the TLS handshake.
The tipoff was here:
https://curl.haxx.se/docs/todo.html#Add_support_for_client_certifica
Support for CURLOPT_SSLCERT in conjunction with WinSSL/SChannel was added in curl 7.60.0 (changelog).
As the documentation for CURLOPT_SSLCERT states:
Client certificates must be specified by a path expression to a certificate store. [...] You can use "<store location>\<store name>\<thumbprint>" to refer to a certificate in the system certificates store, for example, "CurrentUser\MY\934a7ac6f8a5d579285a74fa61e19f23ddfe8d7a". [...] Following store locations are supported: CurrentUser, LocalMachine, CurrentService, Services, CurrentUserGroupPolicy, LocalMachineGroupPolicy, LocalMachineEnterprise.
The TL;DR version
I'd like to know:
Where does the specification for the use of ECDHE get defined (in a cert parameter or a server configuration of SSL contexts, or elsewhere)?
In a non-home-rolled certificate setup, who's responsibility is it to define the ECDHE public and private information (the end user or cert provider)?
Can an existing Certificate which does not appear to use ECDHE be made to without causing issues with the Certificate?
Are there any examples of someone using SSL in Boost::ASIO with an ECDHE setup?
The Longer Version
We've been building an application which is using a proper-paid-for certificate from an external Cert Authority. The application uses a home-rolled server setup based off of Boost ASIO and Boost Beast, and we only recently noticed it doesn't play nice with iOS - ASIO says there is no shared cipher.
Reading into how TLS works has led me to the fact that some part of our server was preventing us from serving TLS using the ECDHE-* suite of ciphers (which iOS seems to want) - but I'm having difficulty in figuring out how to wrangle ASIO and our current cert/key into serving ECDHE.
What I've tried:
Using the same cert and key, adding in the results of openssl dhparam into ASIO using set_tmp_dh, then specifying ciphers. Curl reports that this allows a connection using DHE but not ECDHE. Specifying ciphers that only use ECDHE causes errors when connecting.
Trying to pass the output of openssl ecparam to ASIO using a similar method to the above. I've not been able to format something that ASIO accepts.
Trying to see if there is a way you can use the output of openssl ecparam with another combining function to modify the original cert into one that uses ECDHE. I clued onto this one from the OpenSSL wiki suggesting that if the cert does not contain the line ASN1 OID: prime256v1 (or a similar named curve), then it is not suitable for ECDHE usage.
At this point I'm unsure as to where the issue truly lies (in ASIO, in the certificates or in how I'm putting it all together) and most of the information on the internet I can find relates to home-rolling everything from scratch, rather than working with existing certs.
Update 11/05/19
https://github.com/chriskohlhoff/asio/pull/117 pulled in changes for ASIO with ECDHE. Will need to wait a while to see which Boost lib version it makes it into.
Original Answer
I seem to have found an answer for any googlers - ASIO does not appear to support ECDHE natively at the time of writing. This issue from the main repo suggests that ECDHE is on the cards for support but is not yet implemented.
Here is a link to the ECDHE implementation that's been waiting to be merged since 2016: https://github.com/chriskohlhoff/asio/pull/117.
+1 to get the attention of the Boost ASIO maintainer; he's been pretty slow with it.
I have an application which statically linked with all dependent libraries and at the end I have a single binary file for Windows and Linux.
Is there a way to set ca certificate at compile time the content wil be included together with binary and it will not be necessary to move it together with application binary.
If you use the native SSL library on Windows (sometimes referred to as winssl), there's no need to ship any CA cert at all since curl will then use the internal one Windows features.
If you built libcurl to use OpenSSL, you can set a callback to verify the CA with a fixed built-in CA store. Showed in the cacertinmem example on the curl web site using the CURLOPT_SSL_CTX_FUNCTION option.
Shipping an app with a fixed internal CA cert storage might be troublesome when the services your app are using update/change their certs along the way, so maybe using an external file that you can update occasionally is still a better idea?
Novice to Qt and developing a cross platform app, which requires SSL authentication from the server as well as client sides The .pem based encryption is working on Linux, Android, Windows. However there are problems with Mac OSX. Our code looks like below:
QFile privateKeyFile(":/Certificate.pem"); // --> has certificate + key
privateKeyFile.open(QIODevice::ReadOnly | QIODevice::Text);
setLocalCertificateChain(QSslCertificate::fromPath(":/Certificate.pem", QSsl::Pem));
setPrivateKey(QSslKey(privateKeyFile.readAll(), QSsl::Rsa));
In above code privateKey().isNull() returns true for Mac. When we referred this post, it says that Mac doesn't support .pem based encryption.
The Secure Transport back-end to curl only supports client IDs that are in PKCS#12 (P12) format; it does not support client IDs in PEM format because Apple does not allow us to create a security identity from an identity file in PEM format without using a private API. And we can't use the private API, because apps that use private API are not allowed in any of Apple's app stores.
With my limited understanding, I interpreted that .pem is not a good idea for SSL communication with the server. Please stop me if it's wrong!
Hence, we decided to move to .pfx for all the platforms. We already had a .pfx file with a passphrase. We converted above code to be compatible with .pfx (i.e. "Certificate.pfx", we had this old file along with "Certificate.pem"). Instead of QSsl::Pem, we tried QSsl::Der. But as expected, it didn't work. However, there was no encryption error either, but we are sure that we are doing something wrong. :-)
We referred this post and try to regenerate a .pfx from .pem, but that also didn't help.
QSslCertificate::importPkcs12 fails to parse PFX file
In above case, the QSslCertificate::importPkcs12() returns false for the original .pfx file. Even if we generate a new .pfx from the command line, that also fails for the above function.
Question: Can someone help with exact way of performing the .pfx encryption with the server?
.pem authentication is also fine.
Note:
Server supports both .pfx & .pem. That we confirmed with regular C OpenSSL libraries. But we want to achieve it using Qt.
We are open to formats other than .pfx, should they work in all the platforms
DISCLAIMER: I am writing this from the top of my mind, since I don't personally own a Mac and cannot verify it anymore.
We had this exact problem about a year or two ago at my last job.
It all boils down to Apple dropping support for OpenSSL.
Because of that, Qt switched from OpenSSL backend to Secure Transport backend on Mac with Qt5.6. Now the Secure Transport implementation is lacking some features. For example we were not able to load private key pem-files. I think switching from PKCS#8 to PKCS#1 helped, which can both be stored in .pem files and look almost identical, so that took a while to figure out.
We also noticed that a successfully loaded private key will be stored inside the Mac's key store and could be viewed and exported from there by the user, which we also did not want.
We finally went with re-compiling the QtNetwork module to use OpenSSL instead of Secure Transport. You will need to provide OpenSSL for that, since OSX does not include the headers anymore. A homebrew installation was sufficient I think. Other than that the compilation was surprisingly painless and fast, since you just have to compile one small module, not the whole Qt.
The easiest way to do this is:
download the source distribution of the Qt version you are running
./configure it to use OpenSSL (the -openssl switch I believe)
cdinto the network folder
make
copy the generated QtNetwork.framework inside your Qt-Installation and replace the existing one.
With that everything worked as expected.