CentOS PHP curl无法协商一组可接受的安全参数

On an Ubuntu 14.04.3 this code works fine:

$url_login = "https://test.example.com/login.do";

$cert_file = '/var/www/html/test/cert.pem';
$ssl_key = '/var/www/html/test/cert_private.pem';

$post_fields = 'userAction=1&cancelReason=&cancelType=&account=&memoType=&userText=&userid=99999999&password=xxxxxxxxxxxxxxxx';

$ch = curl_init();

$options = array( 
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_HEADER         => 1,
    CURLOPT_FOLLOWLOCATION => 1,
    CURLOPT_SSL_VERIFYHOST => 0,
    CURLOPT_SSL_VERIFYPEER => 0,

    CURLOPT_USERAGENT => 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)',
    CURLOPT_VERBOSE        => 0,
    CURLOPT_URL => $url_login ,

    CURLOPT_SSLCERT => $cert_file ,
    CURLOPT_SSLCERTTYPE, 'PEM',
    CURLOPT_SSLKEY => $ssl_key,

    CURLOPT_COOKIESESSION => 1,

    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $post_fields
);

curl_setopt_array($ch , $options);

$output = curl_exec($ch);

The php on Ubuntu is using curl with openssl.

On a Centos 7 if fails with:

Curl Error : SSL peer was unable to negotiate an acceptable set of security parameters.

curl is here with nss.

The "cert.pem" contains only the client certificate with the cert-chain, and the "cert_private.pem" contains the private key not password protected. (-----BEGIN RSA PRIVATE KEY-----).

How can i get the above PHP code work with both? openssl and nss implementations of curl?

How about correcting:

CURLOPT_SSLCERTTYPE, 'PEM',

to

CURLOPT_SSLCERTTYPE => 'PEM',

?

I've also come across this problem using client certificate authentication with nss, while openssl works fine.

After much testing, this is what I've established with the server we're trying to contact:

  • curl using TLS v1.2 (default in some cases) with client certificate fails
  • curl using TLS v1.2 with client cert required by server, but not used by client, connects successfully. However client is not authenticated.
  • curl using TLS v1.0 with client certificate is successful

The above happens regardless of cipher suite, generally we're using rsa_aes_256_cbc_sha_256.

The quick workaround is to force TLS v1.0:

CURLOPT_SSLVERSION => 4,

Clearly this isn't ideal, and your server may not support it.

Another option is to compile curl with openssl or even GnuTLS (although I haven't tested the latter) instead of nss. Again, this may not be an option.

So far this points to a problem with NSS. I'll update this answer if further debugging generates any useful information.

Just for reference, this is the full error message using curl on the command line:

* NSS error -12227 (SSL_ERROR_HANDSHAKE_FAILURE_ALERT)
* SSL peer was unable to negotiate an acceptable set of security parameters.
* Closing connection 0
curl: (35) SSL peer was unable to negotiate an acceptable set of security parameters.

Update 2015-11-24: Further testing with Wireshark and ssltap shows the initial handshake is succeeding and the connection gets as far as the client sending ChangeCipherSpec, followed by its encrypted "Finished" message.

The server should then decrypt the client's "Finished" message, verify the hash and MAC and respond with its own encrypted "Finished" message. Instead, the server is responding with "handshake_failure" at this point.

This should provide a clue as to where NSS is failing.

Chrome, Openssl and Charles Proxy can all authenticate using the client certificate. Firefox (using NSS) and curl (with NSS) both fail at this point.

Update 2015-11-27: Additional information provided by the server's operations team suggests this may be an issue with a non-compliant server. The problem only arises when using TLS 1.2 under certain circumstances. This would explain why some SSL libraries, such as OpenSSL, are flexible enough to work around it.

NSS may be more strict in its compliance with RFCs. I'll update the answer if/when we hear more from the operations team managing the server.

Update 2017-01-25: The webserver software and load balancers are custom built for a specific bank's payment gateway. We've recently tried again with a new client and the server now appears to work with both Curl built with either NSS or OpenSSL and are no longer seeing the error. In summary: the workaround was to use a different SSL library and wait for the developers to fix the server software.