php curl cannot get tls version that currently use - php

I am trying to get the version of tls which is currently used when invoking the url.
I'm trying to get the tls version from php curl like the command curl in Ubuntu.
for example:
curl -v https://qpyl.uas-gw.info
* Rebuilt URL to: https://qpyl.uas-gw.info/
* Trying 54.199.158.6...
* Connected to qpyl.uas-gw.info (54.199.158.6) port 443 (#0)
* found 1 certificates in ca.crt
* found 692 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_256_GCM_SHA384
like this to show the tls version result.
But I cannot get the version in php curl like the above curl does
SSL connection using TLS1.2 / ECDHE_RSA_AES_256_GCM_SHA384
php openssl version: openssl/1.0.2g
php version: 5.5.38.

Related

cURL randomly throws "curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL" after some failed requests

I have a PHP script that quickly sends a bunch of requests to the Apple API (APNs). Sometimes 10k requests are sent totally fine (just for the record, it takes ~30sec). However, when the API returns some non-200 codes, establishing new connections to this API throws the following error:
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to api.push.apple.com:443
With the debug mode enabled I get strange results:
When everything is fine:
* Found bundle for host api.push.apple.com: 0x55eba8b11660 [serially]
* Found bundle for host api.push.apple.com: 0x55eba8b11660 [serially]
* Trying 17.188.140.151...
* TCP_NODELAY set
* Hostname 'api.push.apple.com' was found in DNS cache
* Trying 17.188.140.151...
...
...
(full output is here).
After the issue happened:
* Found bundle for host api.push.apple.com: 0x561d83bb1f00 [serially]
* Server doesn't support multiplex (yet)
* Connection #0 is still name resolving, can't reuse
* Found bundle for host api.push.apple.com: 0x561d83bb1f00 [serially]
* Server doesn't support multiplex (yet)
* Connection #0 is still name resolving, can't reuse
* Connection #1 is still name resolving, can't reuse
* Trying 17.188.156.30:443...
* TCP_NODELAY set
* Hostname 'api.push.apple.com' was found in DNS cache
* Trying 17.188.156.30:443...
...
...
(full output is here).
So the IP is changed and something about multiplexing.
After it happens, sending requests to this API using the cli curl also stops working:
$ curl -v -I https://api.push.apple.com
* Trying 17.188.156.30:443...
* TCP_NODELAY set
* Connected to api.push.apple.com (17.188.156.30) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to api.push.apple.com:443
* Closing connection 0
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to api.push.apple.com:443
(full output is here).
The error may disappear after a few minutes, may not. The code runs in a docker container (php:7.3-cli-alpine), restarting the container usually resets the issue until some future requests get non-200 codes. Performing restarts isn't an option.
Presumably, curl somehow stores opened connections and tries to re-use them, but for some reason something is broken inside the curl and it doesn't allow curl to correctly re-use the connections.
While curl stops working, openssl works fine:
$ openssl s_client -connect api.push.apple.com:443
CONNECTED(00000003)
write:errno=0
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 320 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
Software versions:
$ curl --version
curl 7.67.0 (x86_64-alpine-linux-musl) libcurl/7.67.0 OpenSSL/1.1.1d zlib/1.2.11 nghttp2/1.40.0
Release-Date: 2019-11-06
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS HTTP2 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL TLS-SRP UnixSockets
$ openssl version
OpenSSL 1.1.1d 10 Sep 2019
Looks like a curl issue, but how to fix it?
Any help will be appreciated
Just in case it's helpful to you or someone else, the "one error breaks things for a long time" nature of this (and the re-use of Curl connections) reminded me a bit of the Curl bug:
https://github.com/curl/curl/issues/3966
Not sure whether the fix for that is in your Curl version though - from the date it might well be.

Php 5.6 unable to get local issuer certificate

I have a valid wildcard CA Certificate for my sites:
myapp.example.com
qamyapp.example.com
I need consume a rest service using php 5.6 curl.
When try to execute php curl calling qamyapp.example.com, show the next error:
* Trying xx.xxx.xxx.xx...
* Connected to qamyapp.example.com (xx.xxx.xxx.xx) 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
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
But when curl call myapp.example.com work fine.
How a can update the ca-certificates.crt file.
I have found the solution. In qamyapp.example.com server, is necesary concat to certificate.crt file, the content of bundle.crt file, to NGinx.

How to specify public certificate using GuzzleHttp/cURL

I have been fighting this issue for a while and cannot seem to get it right. To preface, this is my first experience having to work with SSL. The server I am working on is using PHP 5.6.30 and Laravel 5.1.46, and I'm trying to use GuzzleHttp 6.2.3 to post JSON to a third party. Up until now, I've been disabling ssl verification by specifying 'verify' => false within the client, and have been able to get successful responses. The third party provided me with a certificate to use, and gave me both .pem and .cer formats. They stated the certificate is not password protected since it is the public key. According to GuzzleHttp's documentation, my request should look like this:
$response = $client->request('POST', $endpoint, [
'cert' => /path/to/new/cert.pem,
'headers' => [
'Content-type' => 'application/json'
],
'body' => $request_body,
'connect_timeout' => 5,
]);
I cannot connect using this method, and unfortunately I can't get the low level error detail due to the design of the server I'm working on (it's a large pre-existing codebase I'm having to update, it will hopefully eventually be completely rewritten).
In order to get more detail, I decided to send the request using cURL. First I sent the request without the certificate:
// REQUEST
curl -v -H "Content-Type: application/json" -X POST -d '{"some_json_request": "value"}' https://sometargeturl.com:8443/dosomething
// OUTPUT
* Trying 204.71.178.10...
* TCP_NODELAY set
* Connected to sometargeturl.com (204.71.178.10) port 8443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* Server certificate:
* subject: CN=sometargeturl.com,OU=OU,O="Some Company Name, Inc",L=Pleasanton,ST=California,C=US
* start date: May 13 00:00:00 2016 GMT
* expire date: May 14 23:59:59 2018 GMT
* common name: sometargeturl.com
* issuer: CN=Symantec Class 3 Secure Server CA - G4,OU=Symantec Trust Network,O=Symantec Corporation,C=US
* NSS error -8179 (SEC_ERROR_UNKNOWN_ISSUER)
* Peer's Certificate issuer is not recognized.
* Curl_http_done: called premature == 1
* Closing connection 0
curl: (60) Peer's Certificate issuer is not recognized.
Then, with the certificate:
// REQUEST
curl -v --cert /path/to/the/cert.pem -H "Content-Type: application/json" -X POST -d '{"some_json_request": "value"}' https://sometargeturl.com:8443/dosomething
// OUTPUT
* Trying 204.71.178.10...
* TCP_NODELAY set
* Connected to sometargeturl.com (204.71.178.10) port 8443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* unable to load client key: -8178 (SEC_ERROR_BAD_KEY)
* NSS error -8178 (SEC_ERROR_BAD_KEY)
* Peer's public key is invalid.
* Curl_http_done: called premature == 0
* Closing connection 0
curl: (58) unable to load client key: -8178 (SEC_ERROR_BAD_KEY)
I've done a ton of research and have not been able to find a solution. I'm really just unsure of next steps at this point. I ultimately will need to use GuzzleHttp to send the request, but I don't what I'm missing. I have read about adding the new cert to an existing CA bundle, however I'm not sure if that is the right solution/how I would do that if so. Any help would be appreciated, thanks!
what are you exactly trying to do ?
Verifying remote server certificate with 'verify' => true
Authenticating yourself by presenting your certificate to remote ?
That being said, your curl is compiled against NSS and not openssl.
The NSS error -8178 (SEC_ERROR_BAD_KEY) comes from NSS that does not support providing the private key as a file

PayPal 14077410 error

I am receiving this error exception:
PayPal\Exception\PayPalConnectionException' with message 'error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
from my test website.
I am using Paypal sdk to run a simple payment (like the example that they have. In my test server locally works just fine).
I have tried various solutions that I found but no luck.
I contacted my web host provider and said that they support tls 1.2 and its a coding problem from my end.
Web server curl versions is : 7.15.5
and openssl : 0.9.8b
From what i searched online many people said that this versions do not support tls 1.2. Did the add backwards capability ?
I also run this test but i am not sure if i am reading it correctly:
Edit :
The server finally confirmed that they are no supporting this and they also said that this server cannot be upgraded so they offered to move me to one that support this. Thank you all for your help.
Support for TLS1.2 was not added in openssl until 1.0.1, 14 Mar 2012.
https://www.openssl.org/news/changelog.html#x19
You need to update SSL on your server, and educate your host, or find a new one.
We faced this back in August (we were at 0.9.8-e-fips-rhel5); updating SSL did the trick.
To add onto what #Kkinsey said, it sounds like your host is using CentOS/RHEL 5 (which is approaching end of life). CentOS 6, while it contains the higher level of openssl, is apparently not capable of automatically negotiating TLS 1.2. CentOS 7 for some reason can.
curl -v https://api-3t.sandbox.paypal.com/nvp
About to connect() to api-3t.sandbox.paypal.com port 443 (#0)
Trying 173.0.82.83... connected
Connected to api-3t.sandbox.paypal.com (173.0.82.83) port 443 (#0)
Initializing NSS with certpath: sql:/etc/pki/nssdb
CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
NSS error -12286
Closing connection #0
SSL connect error
curl: (35) SSL connect error
You have to force PHP's CURL to use TLS 1.2. Once you have upgraded your OpenSSL package (doing this on 0.9.8 WILL BREAK) you'll need to add this to your CURL request
curl_setopt($ch, CURLOPT_SSLVERSION, 6);

New APNS Provider API and PHP

I started creating some code based upon this for sending push notifications from PHP.
However now that I have understood there is a new API which utilizes HTTP/2 and provides feedback in the response, I am trying to figure out what I need to do to get that feedback.
I haven't been able to find any tutorials or sample code to give me direction (I guess because it is so new).
Is it possible to use the stream_socket_client() method of connecting to APNS with the new provider API? How do I get the feedback? All I get back from fwrite($fp, $msg, strlen($msg)) right now is a number. For all intents and purposes, you can consider my code the same as the code from the SO question I based my code on
Thanks!
With the new HTTP/2 APNS provider API, you can use curl to send push notifications.
EDIT
Before proceeding (as noted by #Madox), openssl >= 1.0.2e of should be installed (from package preferably). Verify with the command
openssl version
a) Your version of PHP should be >= 5.5.24 so that the constant CURL_HTTP_VERSION_2_0 is defined.
b) Make sure that you have curl version 7.46+ installed in your system with
curl --version
c) Curl should have http/2 support enabled. In the output when typing the previous command, you should see a line like this one:
Features: IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets
if HTTP2 doesn't show up, you can follow this excellent tutorial to install http/2 for curl https://serversforhackers.com/video/curl-with-http2-support
Verify that curl detected openssl >= 1.0.2e, doing curl --version should output something like this:
curl 7.47.1 (x86_64-pc-linux-gnu) libcurl/7.47.1 OpenSSL/1.0.2f zlib/1.2.8 libidn/1.28 nghttp2/1.8.0-DEV librtmp/2.3
e) Once you everything installed, you can test it in the command line:
curl -d '{"aps":{"alert":"hi","sound":"default"}}' \
--cert <your-certificate.pem>:<certificate-password> \
-H "apns-topic: <your-app-bundle-id>" \
--http2 \
https://api.development.push.apple.com/3/device/<device-token>
f) Here is a sample code in PHP that I have successfully tried:
if(defined('CURL_HTTP_VERSION_2_0')){
$device_token = '...';
$pem_file = 'path to your pem file';
$pem_secret = 'your pem secret';
$apns_topic = 'your apns topic. Can be your app bundle ID';
$sample_alert = '{"aps":{"alert":"hi","sound":"default"}}';
$url = "https://api.development.push.apple.com/3/device/$device_token";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $sample_alert);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("apns-topic: $apns_topic"));
curl_setopt($ch, CURLOPT_SSLCERT, $pem_file);
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pem_secret);
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
//On successful response you should get true in the response and a status code of 200
//A list of responses and status codes is available at
//https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH107-SW1
var_dump($response);
var_dump($httpcode);
}
I want to add some information to tiempor3al answer.
1) curl must be compiled with openssl version >=1.0.2 to fully support http/2. I receive "?##?HTTP/2 client preface string missing or corrupt..." error when I compiled it with CentOS stock openssl-1.0.1e.
2) if your version of php module mod_curl.so compiled without CURL_HTTP_VERSION_2_0 constant, you could replace it with integer number 3:
curl_setopt($ch, CURLOPT_HTTP_VERSION, 3);
I could successfully send push via HTTP2 using php CURL and read the feedback directly in the response body (here I wrote a short tutorial on how to do this: Sending Push Notification with HTTP2 (and PHP)).
I think that you can check the response body reading from the socket (I don't remember exactly the php function, perhaps "fgets").
I'm using CentOS 6 and the way to solve was to install from source cURL and OpenSSL.
It may look very simple but it took me 3 days to figure out the right configuration for the packages so I think I can help someone by posting what I did here.
It proved a little tricky for me since I'm not used to install packages from source, but I now can connect to APNs using HTTPS over HTTP/2.
The details of what I did is here:
Download and uncompress cURL and OpenSSL:
wget https://curl.haxx.se/download/curl-7.47.1.tar.gz
wget https://www.openssl.org/source/openssl-1.0.2h.tar.gz
Configure OpenSSL with the following flags (I'm not sure what they do, but is what worked for me):
export CXXFLAGS="$CXXFLAGS -fPIC"
./config zlib enable-ssl3 enable-shared
make and install OpenSSL
Configure cURL with the following flag:
./configure --with-ssl=/usr/local/ssl/
Make and install cURL
set LD_LIBRARY_PATH to /usr/local/ssl/lib/
export LD_LIBRARY_PATH=/usr/local/ssl/lib/
Test
/usr/local/bin/curl -v -d '{"aps":{"alert":"hi","sound":"default"}}' --cert cert.crt --key cert.key -H "apns-topic: topics" --http2 https://api.development.push.apple.com:443/3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
The result:
* Trying 17.172.238.203...
* Connected to api.development.push.apple.com (17.172.238.203) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
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 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=api.development.push.apple.com; OU=management:idms.group.533599; O=Apple Inc.; ST=California; C=US
* start date: Jun 19 01:49:43 2015 GMT
* expire date: Jul 18 01:49:43 2017 GMT
* subjectAltName: host "api.development.push.apple.com" matched cert's "api.development.push.apple.com"
* issuer: CN=Apple IST CA 2 - G1; OU=Certification Authority; O=Apple Inc.; C=US
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* TCP_NODELAY set
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x1091110)
> POST /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0 HTTP/1.1
> Host: api.development.push.apple.com
> User-Agent: curl/7.48.0
> Accept: */*
> apns-topic: topics
> Content-Length: 40
> Content-Type: application/x-www-form-urlencoded
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* We are completely uploaded and fine
< HTTP/2.0 400
<
* Connection #0 to host api.development.push.apple.com left intact
{"reason":"BadDeviceToken"}
As you can see, I got rid of the ugly
▒##▒HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 504f5354202f332f6465766963652f746573742048545450
Follow this guide [http://cloudfields.net/blog/ios-push-notifications-encryption/][1] to generate and merge your certificate and private key.
Once you merge and your sslcert and pkey as described in the guide with same file names, just try the curl command below.
curl -X POST -H 'apns-topic: com.mycompany.ios.BadassApp' -d '{"aps":{"content-available":1,"alert":"hi","sound":"default"}}' --cert apns_cert.pem:yourCertPassword --http2 'https://api.development.push.apple.com:443/3/device/b8de1sf067effefc398d792205146fc67dn0e96b0ff21ds81cabe384bbe71353'
To resolve HTTP/2 client preface string missing or corrupt errors in PHP 5.6 I created an Apache and CLI PHP Docker image that you might want to rely on or just look up what I did in the Dockerfile to build your own. Same can probably apply to PHP 7.0 although I haven't tried.
yes, it's possible:
$errno=0;$errstr="";
$sock = stream_socket_client(
"api.push.apple.com:443",
$errno,
$errstr,
4,
STREAM_CLIENT_CONNECT,
stream_context_create(["ssl" => ["local_cert" => "/path/to/cert.pem","verify_peer"=>false,"verify_peer_name"=>false]])
);
stream_socket_enable_crypto($sock, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);

Categories