Client Certificates with LibCUrl - ssl

I am using libCurl to download a file from a remote server. That remote server requires client certificates. Here are the options that i have tried:
curl_easy_setopt(pCurl, CURLOPT_URL, url);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(pCurl, CURLOPT_CERTINFO, 1L);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 1);
//the following two lines specify the path to my valid client certificate
curl_easy_setopt(pCurl, CURLOPT_CAINFO, "c:\\Delta.p12");
curl_easy_setopt(pCurl, CURLOPT_CAPATH, "c:\\Delta.p12");
When I make the Https request, I get a 403: Forbidden error that says I have not specified the needed credentials. This certificate works via a browser, so I know that the cert is valid.
Any help to get this work is appreciated. Thanks!

If you get a 403, you already got passed the SSL layer so it would indicate that the certificate was good enough but that the server is there talking about something else.
But note that the CURLOPT_CA* options are used to specify your CA cert bundle (or path), so the above lines don't set any client certificate at all!
For a small example that shows how to use a client certificate with libcurl, see this:
http://curl.haxx.se/libcurl/c/simplessl.html

Related

Docusign Connect: Error - The request was aborted: Could not create SSL/TLS secure channel

After 4 years of reliable operation, my PHP listener script started to fail from October 8th, 2019 onwards with the
Error - The request was aborted: Could not create SSL/TLS secure channel.
I have not changed anything on my server. The SSL certificate is a valid v3 from Lets Encrypt. TLS is 1.2.
I have been on support calls for hours with no resolution, other than them telling me that I need to install their certificates here: https://www.docusign.com/trust/compliance/public-certificates
The problem is that I don't know how I would integrate that with my server, and my web host doesn't know either. When asked, they are not able to explain it either.
The listener script on my server is fairly simple:
function guid() {
$uuid = '';
if (function_exists('com_create_guid')){
$uuid = com_create_guid();
// somehow the function com_create_guid includes {}, while our webservice
// doesn't. Here we are changing the format by taking those curly braces out.
$uuid = str_ireplace("{", "", $uuid );
$uuid = str_ireplace("}", "", $uuid );
} else {
mt_srand((double)microtime()*10000);//optional for php 4.2.0 and up.
$charid = strtoupper(md5(uniqid(rand(), true)));
$hyphen = chr(45);// "-"
$uuid = substr($charid, 0, 8).$hyphen
.substr($charid, 8, 4).$hyphen
.substr($charid,12, 4).$hyphen
.substr($charid,16, 4).$hyphen
.substr($charid,20,12);
}
return $uuid;
}
// Figure out the URL of this server
// NOTE: DocuSign only pushes status to HTTPS!
$postBackPath = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
$postBackPath .= ($_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'] );
$postedXml = #file_get_contents('php://input');
if (!empty($_POST['post'])) {
// if this is a sample load
$xml = simplexml_load_file("post.sample") or die("Unable to load sample XML file!");
$xml->EnvelopeStatus->EnvelopeID = guid(); // here we replace the GUID so we have unique files
// using the curl library to get the post
$curl = curl_init();
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT ,30);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_URL, $postBackPath);
curl_setopt($curl, CURLOPT_HEADER, array("Content-Type: application/xml"));
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml->asXML());
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_exec ( $curl );
curl_close ($curl);
}
else if(!empty($postedXml)) {
// see if this is a post to this page if it is then we have to save it.
$xml = simplexml_load_string($postedXml);
$post = $xml;
print 'Got Envelope ID: ' . $xml->EnvelopeStatus->EnvelopeID . '<br />';
}
After that code, it just parses data from the xml that I pass into my CRM.
On the Docusign Connect setup, I have the log enabled and require acknowledgement. All of the integration and security settings are unchecked (HMAC signature, Include basic authentication header, require mutual TLS, use SOAP interface, enable mutual TLS are all unchecked).
UPDATE: A response from my webhost assisting in this issue:
Hello again.
I took a look at the SSL you presently have installed for
sub.domain.com (you can see the info from Chrome browser) If you
click on the lock in the url bar and then click on Certificate >
Details > Version you (or docusign) can see that that cert is version
3 as they specified that you need.
I'm not clear on where they think that you should install their
certificate though. The cert at the link provided is to cover
na2.docusign.net which is not hosted on your server so there's no
place to install that that I'm aware of. You can verify that by
downloading the NA2 certificate from the link they provided:
https://www.docusign.com/trust/compliance/public-certificates
Open the .cer file in the simplest text editor you have available and
paste the contents in here:
https://www.sslshopper.com/certificate-decoder.html
That will give you all the information about the certificate. Under
"Subject:" you'll see CN = na2.docusign.net which means that it covers
their domain not yours.
I'm afraid we'll need more information from DocuSign to be able to
assist you. If this SSL were installed on your domain, it would fail
authenticity checks run by any browser connecting to your site which
isn't going to instill any confidence for your visitors.
This was my impression as well, so I feel like we are misunderstanding how this certificate from Docusign would work in conjunction with the certificate we already have from Lets Encrypt.
Is this a coding issue?
I am using a very simple Docusign connect integration, which is just a php listener catching the XML from a completed envelope and parsing it so I can pass that to my CRM.
I had the same issue today. after 5 hours digging into the internet, Here my change which resolved my issue:
+=+ all the update is related to the Haproxy certification setting, I added these three lines to the haproxy.conf file:
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
I was able to find this article: https://developers.docusign.com/esign-rest-api/guides/mutual-tls-apache2 and pass this to my server administrator. This was apparently had the missing info they needed to get it working. Not really an answer, but if you are struggling like I was, this will help steer you in the right direction.
see information on this page:
https://www.docusign.com/trust/compliance/public-certificates
"The renewed Connect certificates listed below are slated to be introduced into the DocuSign Service in the September 2019 - November 2019 timeframe. The ‘offer’ date specified below is the date the renewed certificate will be available for consumption and the ‘enforce’ date is when the renewed certificate will be the only option i.e. the current certificate will no longer be available for consumption."
You would have to update the certificate from that page on your server to fix this.

PHP: How to fix curl_exec() error 60: unable to get local issuer certificate?

(This question was originally titled, "Why is curl_exec() failing in this script?" But by adding calls to curl_errno() and curl_error() in the script, I found out that the problem was the certificate, and I've edited the question accordingly.)
The following script:
<?php
$sDataFile = '<path>\journal-issue-ToC.htm';
$sURL = 'https://onlinelibrary.wiley.com/toc/14678624/2014/85/1';
$bHeader = false;
$cURLhandle = curl_init();
$FilePointer = fopen($sDataFile, 'wb');
curl_setopt($cURLhandle, CURLOPT_URL, $sURL);
curl_setopt($cURLhandle, CURLOPT_FILE, $FilePointer);
curl_setopt($cURLhandle, CURLOPT_HEADER, $bHeader);
$bResult = curl_exec($cURLhandle);
echo('<br>' . ($bResult === false ? 'Failed to execute' : 'Executed') . ' cURL.');
if(! $bResult) echo('<br>Error #' . curl_errno($cURLhandle) . ': ' . curl_error($cURLhandle));
curl_close($cURLhandle);
fclose($FilePointer);
saves the empty file "journal-issue-ToC.htm" and generates the following browser output:
Failed to execute cURL.
Error #60: SSL certificate problem: unable to get local issuer certificate
So it appears that cURL encounters a certificate problem that doesn't happen when I access the requested URL in the browser. What do I need to know about certificates in order to get this script to work?
I'm running PHP 7.2.2 on IIS 7.5 under Windows 7 64 bit.
What I needed to know about certificates in order to get cURL to work is in an article on GitHub, which explains the need for certificates in cURL and how to get and apply them:
What we're talking about are SSL certificates, needed for the https protocol. "CA" stands for "certificate authorities".
Download the certificates from https://curl.haxx.se/ca/cacert.pem (documentation).
In the file "php.ini", in the section on cURL, uncomment the command for the CURLOPT_CAINFO option and specify the location of the downloaded file "cacert.pem”. After saving the ini file, restart the webserver to effect the change.
I did this with a couple of variations:
I chose to set the value of CURLOPT_CAINFO in the function curl_setopt() instead of in the file "php.ini".
I at first got error 77, "error setting certificate verify locations". This was fixed by moving the file "cacert.pem" to a folder under the folder "Program Files\PHP".
After doing that, the script ran without error and the output file was not empty. But what it contained was still not what I need, which is the subject of a new question.

ACE SSL Error: peer did not return a certificate

I am making both server and client for an application, using the ACE library with OpenSSL. I am trying to get mutual authentication to work, o the server will only accept connections from trusted clients.
I have generated a CA key and cert, and used it to sign a server cert and a client cert (each with their own keys also). I seem to be loading the trusted store correctly, but I keep getting the error "peer did not return a certificate" during handshake.
Server side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_server);
context->certificate("../ACE-server/server_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-server/server_key.pem", SSL_FILETYPE_PEM);
if (context->load_trusted_ca("../ACE-server/trusted.pem", 0, false) == -1) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "load_trusted_ca"), -1);
}
if (context->have_trusted_ca() <= 0) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "have_trusted_ca"), -1);
}
Client side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_client);
context->certificate("../ACE-client/client_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-client/client_key.pem", SSL_FILETYPE_PEM);
I generated the certificates following these instructions: https://blog.codeship.com/how-to-set-up-mutual-tls-authentication/
And checking online, I found that if the .crt and .key files are readable, they should already be in .pem format and there is no need to convert them. So I just changed the extension and used them here.
Any help is appreciated!
My problem apparently was the same as seen here: OpenSSL client not sending client certificate
I was changing the SSL context after creating the SSL Socket. Now the mutual authentication works, but my client crashes when closing the connection. Though I don't know why that is yet.

CURLOPT_SSLVERSION on my webserver

i have a site with paypal payment method, when user try to pay the following error is displayed :
curl_error: 35
SSL connect error
I use curl to make paypal call :
ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_VERBOSE, 0);
//curl_setopt($ch, CURLOPT_SSLVERSION, 4);
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
//turning off the server and peer verification(TrustManager Concept)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/Certs/api_cert_chain.crt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
$headers_array = paypal_adaptive_setupHeaders();
if (!empty($sandboxEmailAddress)) {
$headers_array[] = "X-PAYPAL-SANDBOX-EMAIL-ADDRESS: " . $sandboxEmailAddress;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers_array);
curl_setopt($ch, CURLOPT_HEADER, false);
So i have two questions
1- how can i solve this issue ?
adding : PPHttpConfig::$DEFAULT_CURL_OPTS[CURLOPT_SSLVERSION] = 4; cause 500 server error
2- Is there a way to know the CURLOPT_SSLVERSION used on my webserver? (dedicated server and i can access all privileges to install update things ...)
3- updating OPEN SSL can solve this?
Thanks everybody.
cURL error 35 is CURLE_SSL_CONNECT_ERROR which states:
A problem occurred somewhere in the SSL/TLS handshake. You really want
the error buffer and read the message there as it pinpoints the
problem slightly more. Could be certificates (file formats, paths,
permissions), passwords, and others.
This is probably 1 of 2 things given recent changes PayPal made.
Your version of cURL/OpenSSL do not support TLS (several months ago PayPal stopped supporting SSLv3 and below which broke a lot of clients). Since you've disabled peer verification, this might be the issue.
The api_cert_chain.crt no longer contains the correct certificate authority certificate PayPal is using.
There is a leaflet of information on paypal about the security upgrade and the protocols your server has to support to communicate with PayPal.
Here is the link of the information page : https://www.paypal-knowledge.com/infocenter/index?page=content&widgetview=true&id=FAQ1913&viewlocale=en_US#TLS
You have to make sure that openssl and Curl have the correct version on your server to be enable to establish the connection.
Apparently you should have the following requirements :
- openssl version >= 1.0.1
- curl version >= 7.34.0.

SoapUI doesn't send client certificate

Using SoapUI 5.2.1 with the SSL client cert configured...
A python request to the endpoint like so
import requests
HOST = 'https://HOST'
CERT_FILE = 'CERT.crt'
KEY_FILE = 'KEY.key'
ping_response = requests.get(HOST, cert=(CERT_FILE, KEY_FILE))
print(ping_response)
works fine, but when I turned the cert/key into a PFX or a java keystore and load it into SoapUI like so
http://geekswithblogs.net/gvdmaaden/archive/2011/02/24/how-to-configure-soapui-with-client-certificate-authentication.aspx
I run a request and get a 400 Bad Request response with a body of "No required SSL certificate was sent"
It seems that the client cert is not being sent. Is there another step to configuring SoapUI for client auth? Do I need to specifically link it to the project or request somewhere?