i have the following php code that calls an API:
$forcetest = 1;
$url = "https://www.website.com/api/pointTotal.php?userId=" . $userId;
if ($forcetest || (isset($_SERVER['ON_TEST_SERVER']) && ($_SERVER['ON_TEST_SERVER']))){
$url = "https://wwwtest.website.com/api/pointTotal.php?userId=" . $userId;
}
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url
));
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'sharedSecret: abc123',
));
//curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //tested to see if this would work, didn't seem to matter
// Only do this if on dev/test
if ($forcetest || (isset($_SERVER['ON_TEST_SERVER']) && ($_SERVER['ON_TEST_SERVER']))) {
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
}
$response = curl_exec($curl);
print($response);
if(!$response){
die('Error: "' . curl_error($curl) . '" - Code: ' . curl_errno($curl));
}
my problem is when going to production (i.e. $forcetest = 0;) it works just fine, calling the prod api and using the prod database. if i switch it, and it goes to the wwwtest subdomain to call the test api, i get the error returned
{"success":false,"errors":["Invalid API request"]}
"invalid api request" isn't something in the api's code, so it seems to me that its something curl_exec is doing/erroring on but i can't figure out what. if i go to the api's directly, they both return the correct information in my browser, so i know it's up and working. is it maybe a cert issue or some sort of xss setting on that subdomain/server that's causing it to error? i'm kind of at a loss.
Thanks in advance.
edit: CLI curl command and header output:
[josh#mac] $ curl -H 'sharedSecret: abc123' 'https://wwwtest.website.com/api/pointTotal.php?userId=1472&rewardsType=f' -vvv
* About to connect() to wwwtest.website.com port 443
* Trying xxx.xx.xx.xx... connected
* Connected to wwwtest.website.com (xxx.xx.xx.xx) port 443
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSLv2, Client hello (1):
SSLv3, TLS handshake, Server hello (2):
SSLv3, TLS handshake, CERT (11):
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 RC4-SHA
* Server certificate:
* subject: /C=US/ST=New Mexico/L=Albuquerque/O=removed/CN=*.website.com
* start date: 2013-07-29 00:00:00 GMT
* expire date: 2016-10-05 12:00:00 GMT
* subjectAltName: wwwtest.website.com matched
* issuer: /C=US/O=DigiCert Inc/CN=DigiCert Secure Server CA
* SSL certificate verify ok.
> GET /api/pointTotal.php?userId=1472&rewardsType=f HTTP/1.1
> User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: wwwtest.website.com
> Accept: */*
> sharedSecret: abc123
>
< HTTP/1.1 200 OK
< Date: Wed, 29 Jun 2016 18:27:25 GMT
< Server: Apache/2.2.3 (CentOS) DAV/2 mod_ssl/2.2.3 OpenSSL/0.9.8e-fips-rhel5
< Access-Control-Request-Headers: accept, auth-key
< Access-Control-Allow-Origin: https://customeraccess.website.com
< Access-Control-Allow-Headers: sharedSecret
< Content-Length: 36
< Content-Type: text/html
Connection #0 to host wwwtest.website.com left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
{"userId":"1472","pointTotal":50600}
also added this to the php code making the call
curl_getinfo($curl, CURLINFO_HEADER_OUT) and it prints out a 404 which is what seems to be the problem but going to the url it's curl'ing immediatley returns the correct result so i'm not sure what's causing the 404
Sorry, just now saw this question was still open. The problem turned out to be bad internal DNS. the code and the curl we're all correct, i had to rely on the sys admin's to resolve it.
Related
(I edited the title, see EDIT below: GET path in the response contains spaces, requested path does not.)
I am working for a company that uses a "template system" to enable their markets (different countries) to host small web apps and making them "feel" like part of the main market website by pulling header, navigation, footer of the main website via curl:
So a php script uses curl to pull a special empty CMS page that contains only the relevant elements and placeholders for the web app to replace. This way, the web apps always have the current legal footer, header and navigation links to the main sections on the CMS and feel like being part of the cms albeit being hosted on their own somewhere else.
In this case, depending on an i18n parameter in the url, the script is curling a different template for each of ~50 markets.
Now the curious case: This is working for all templates but one, which is returning error 400 when being curled via https.
But when curled via http, 301 is returned, then another request to the previously faulty https URL is made, only this time it returns 200.
Does not work:
* Trying [server_ip]...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562a5065a130)
* Connected to www.[company_name].pt ([server_ip]) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.[company_name].pt
* start date: Aug 23 19:02:44 2022 GMT
* expire date: Nov 21 19:02:43 2022 GMT
* subjectAltName: host "www.[company_name].pt" matched cert's "*.[company_name].pt"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x562a5065a130)
> GET [path] HTTP/2
Host: www.[company_name].pt
User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)
Accept:text/html
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
Cache-Control:max-age=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* The requested URL returned error: 400
* stopped the pause stream!
* Connection #0 to host www.[company_name].pt left intact
Does work:
* Trying [server_ip]...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562a5066c0f0)
* Connected to www.[company_name].pt ([server_ip]) port 80 (#0)
> GET [path] HTTP/1.1
Host: www.[company_name].pt
User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)
Accept:text/html
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
Cache-Control:max-age=0
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html
< Content-Length: 185
< Connection: keep-alive
< Location: https://www.[company_name].pt/[path]
<
* Ignoring the response-body
* Connection #0 to host www.[company_name].pt left intact
* Issue another request to this URL: 'https://www.[company_name].pt/[path]'
* Trying [server_ip]...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562a5066c0f0)
* Connected to www.[company_name].pt ([server_ip]) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.[company_name].pt
* start date: Aug 23 19:02:44 2022 GMT
* expire date: Nov 21 19:02:43 2022 GMT
* subjectAltName: host "www.[company_name].pt" matched cert's "*.[company_name].pt"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x562a5066c0f0)
> GET [path]
Host: www.[company_name].pt
User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)
Accept:text/html
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
Cache-Control:max-age=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< content-type: text/html; charset=utf-8
< content-length: 15305
< date: Thu, 22 Sep 2022 09:25:35 GMT
< server: Apache
< x-robots-tag: noindex
< strict-transport-security: max-age=31536000
< x-content-type-options: nosniff
< x-frame-options: SAMEORIGIN
< x-xss-protection: 1; mode=block
< referrer-policy: strict-origin-when-cross-origin
< cache-control: public, max-age=3600
< expires: Thu, 22 Sep 2022 10:25:35 GMT
< pragma: public
< accept-ranges: bytes
< content-encoding: gzip
<
* Connection #1 to host www.[company_name].pt left intact
Calling the template in the browser works, with http and https.
curl from CLI works also for both http and https and returns 200, the error only occurs with PHP.
The script (a bit blown up for error search):
function curl($url){
$headers[] = "User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)";
$headers[] = "Accept:text/html";
$headers[] = "Accept-Encoding:gzip,deflate";
$headers[] = "Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7";
$headers[] = "Keep-Alive:115";
$headers[] = "Connection:keep-alive";
$headers[] = "Cache-Control:max-age=0";
/**/debug_to_console("init curl");
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_ENCODING, "gzip");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_SSLVERSION, 6);
curl_setopt($curl, CURLOPT_FAILONERROR, 1);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_STDERR, $verbose = fopen('php://temp', 'rw+'));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($curl);
if(curl_errno($curl)){
debug_to_console("curl url: " . $url);
debug_to_console("curl error: " . curl_error($curl));
//echo 'Request Error:' . curl_error($curl);
}
echo "Verbose information:\n<pre>", !rewind($verbose), htmlspecialchars(stream_get_contents($verbose)), "</pre>\n";
curl_close($curl);
return $data;
}
As mentioned, on other templates from other markets (on the same server) this is working fine. And I was told, that the instances are duplicated, so they all should have the same settings. Again, they are all on the same server. Unfortunately, I can't get them to check further until I'm pointing them into the right direction what to look at.
Tests with ssllabs.com showed no differences between the instances.
Being a Front End Dev, I'm really not getting any further at this point, after trying multiple suggestions for similar errors. I'm suspecting a wrong setting on that instance, but what instructions/hints can I give the server team?
Sorry I had to remove IPs and company/app names.
Thank you.
Edit: Not sure if this is relevant, for some reason, the line > GET /[path] HTTP/2 is different in the faulty curl attempt:
Every successful curl has one space between the path and HTTP/2, the faulty one has three spaces between the path and HTTP/2.
When using http instead of https, the line also has three spaces > GET /[path] HTTP/1.1), gets the 301 response and after redirect, the new attempt has only one space > GET /[path] HTTP/2.
The URL is not the problem, it is absolutely identical to other instances where there is no problem. Does the server add a space character itself on the initial curl attempt? What could cause added spaces? This has to be server side, the other instances don't mangle the initial GET path.
I have a bug I am hoping someone can shed some insight on. I am fetching data from a client's API, and it works...90% of the time. About 1/10 times (but random frequency), curl_exec returns false with error:
OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to api.site.com:443
I've checked numerous threads on this error, and in all cases I see the issue is happening 100% of the time for the person. Has anyone dealt with something like this? If it's working sometimes and failing other times, where is the issue likely to be (the site/server or the api)?
I have experimented with different settings and nothing has fixed the issue. I've tried with and without CURLOPT_PINNEDPUBLICKEY, with and without CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_SSL_VERIFYPEER => 0, with and without forcing HTTP and SSL versions. It fails always when I set these incorrectly, so I am pretty sure I have the right versions selected. The code exists in a custom joomla component which has been stable before this API switch. Here is my code:
$headers = array(
'X-apikey: '.$this->apikey,
"Cache-Control: no-cache",
);
$url = $this->url;
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2,
CURLOPT_PINNEDPUBLICKEY => '/path/to/api.site.com.pubkey.der',
));
$data = curl_exec($curl);
$result = json_decode($data);
curl_close($curl);
PHP version: 7.3.10
curl version: 7.66.0
openSSL library version: OpenSSL 1.1.1d 10 Sep 2019
Output from hanshenrik comment (removed company info):
Error output:
string(385) "* Trying xxx.xxx.xxx.xxx:443... * TCP_NODELAY set * Connected to api.site.com (xxx.xxx.xxx.xxx) port 443 (#0) * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /usr/local/etc/openssl#1.1/cert.pem CApath: /usr/local/etc/openssl#1.1/certs * OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to api.site.com:443 * Closing connection 0 "
Success Output
string(1324) "* Trying xxx.xxx.xxx.xxx:443... * TCP_NODELAY set * Connected to api.site.com (xxx.xxx.xxx.xxx) port 443 (#0) * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /usr/local/etc/openssl#1.1/cert.pem CApath: /usr/local/etc/openssl#1.1/certs * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server did not agree to a protocol * Server certificate: * subject: C=US; ST=Illinois; L=Chicago; O=Company Name; CN=*.site.com * start date: May 14 15:20:46 2019 GMT * expire date: Aug 8 15:41:00 2020 GMT * issuer: C=US; ST=Arizona; L=Scottsdale; O=GoDaddy.com, Inc.; OU=http://certs.godaddy.com/repository/; CN=Go Daddy Secure Certificate Authority - G2 * SSL certificate verify ok. > GET /endpoint/id?fields=field1,field2,etc HTTP/1.1 Host: api.site.com Accept: / X-apikey: xxxxx Cache-Control: no-cache * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Tue, 08 Oct 2019 21:21:45 GMT < Server: Apache < Content-Length: 614 < Content-Type: application/json < * Connection #0 to host api.site.com left intact "
Request to a single specific https endpoint fails on one of my servers with "NSS: client certificate not found (nickname not specified)" error from PHP CURL.
When I ran the same query from the command line with verbose option I got the same error, but after that I also got a valid response (after the error in the same CURL request).
Then I've enabled verbose option for my PHP CURL request and displayed it for testing like this:
$error = curl_error($curl);
if ($error) {
echo "cURL Error #:" . $error;
rewind($verbose);
$verboseLog = htmlspecialchars(stream_get_contents($verbose));
echo "<pre>";
print_r($verboseLog);
} else {
echo $response;
}
And got this as output:
cURL Error #:NSS: client certificate not found (nickname not specified)
* About to connect() to <address> port 8001 (#0)
* Trying <ip>... * connected
* Connected to <address> (<ip>) port 8001 (#0)
* skipping SSL peer certificate verification
* NSS: client certificate not found (nickname not specified)
* SSL connection using TLS_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=*.<address>m,O=XXX A.S.,OU=IT,L=Umraniye,ST=Istanbul,C=TR
* start date: Jun 11 12:52:03 2015 GMT
* expire date: Jun 09 14:16:13 2018 GMT
* common name: *.<address>
* issuer: CN=GlobalSign Organization Validation CA - SHA256 - G2,O=GlobalSign nv-sa,C=BE
> POST /<url_part> HTTP/1.1
Host: <address>:8001
Accept: */*
Accept-Encoding: deflate, gzip
authorization: Basic UlBlahBlahnJrcjEyMzQ1Ng==
cache-control: no-cache
content-type: application/json
postman-token: eeefe018-7cac-1706-7b6d-847800a7ad0f
Content-Length: 333
< HTTP/1.1 200 OK
< set-cookie: sap-usercontext=sap-client=100; path=/
< set-cookie: SAP_SESSIONID_CFP_100=ohZvfO5ZOwq_LTE76Zgz9L-C0NdhlRHngM0AF6R3IFY%3d; path=/
< content-type: application/json; charset=utf-8
< content-length: 1150
< cache-control: max-age=0
< sap-cache-control: +180
< sap-isc-uagent: 0
<
* Connection #0 to host <address> left intact
* Closing connection #0
So, CURL request finished with the error, but in verbose log we see that after the error valid response was also fetched.
Here's my questions:
Is there any way to overcome this NSS issue as long as other different https endpoints work completely fine? Is it connected with endpoint's server configuration and can be resolved there?
If the first item fails, is it possible not to throw CURL NSS error, but get the response instead? CURLOPT_FAILONERROR option does not help.
CURL was built with NSS:
curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz
Update:
The second item can be closed, because this error is really strange: curl_error returns the error, but curl_exec returns valid response at the same time.
Try giving a absolute path to the certificate and add a "./" prefix. For example "./path-to-your-cert"
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);
Is there any Curl function in PHP that is similar to the '-v' argument from the linux shell?
Because the '-v' argument gives me something like this:
$ curl -v https://www.bankinter.com
* About to connect() to www.bankinter.com port 443 (#0)
* Trying 195.235.25.185... connected
* Connected to www.bankinter.com (195.235.25.185) port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* 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):
*//I NEED HERE...*
* SSL connection using AES256-SHA
* Server certificate:
* subject: 1.3.6.1.4.1.311.60.2.1.3=ES; 2.5.4.15=V1.0, Clause
5.(b); serialNumber=A28157360; C=ES; ST=Madrid; L=Tres Cantos;
O=Bankinter S.A.; OU=Bankinter S.A.; OU=Terms of use at
www.verisign.com/rpa (c)05; CN=www.bankinter.com
* start date: 2009-12-07 00:00:00 GMT
* expire date: 2011-01-06 23:59:59 GMT
* common name: www.bankinter.com (matched)
* issuer: C=US; O=VeriSign, Inc.; OU=VeriSign Trust Network;
OU=Terms of use at https://www.verisign.com/rpa (c)06; CN=VeriSign
Class 3 Extended Validation SSL SGC CA
* SSL certificate verify ok.
* //...TO HERE*
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7
OpenSSL/0.9.8l zlib/1.2.3
> Host: www.bankinter.com
> Accept: */*
>
And I need to retrieve that same data to a txt file in PHP...
Anybody knows any way?
Thanks, and sorry for the poor english
P.S. "www.bankinter.com" it's just a test
From the CURL documentation:
-v, --verbose Make the operation more talkative
So with this in mind, you could add the CURL option VERBOSE and then save the output to a file. Try this:
CURL option
$outputFile = 'curlOutput.txt';
$handle=curl_init('https://www.bankinter.com');
curl_setopt($handle, CURLOPT_VERBOSE, true);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_STDERR,$f = fopen($outputFile, "w+"));
curl_setopt($handle, CURLOPT_CERTINFO, true);
curl_exec($handle);
fclose($f);
CURL alternative
$g = stream_context_create (array("ssl" => array("capture_peer_cert" => true)));
$r = stream_socket_client("ssl://www.google.com:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $g);
$cont = stream_context_get_params($r);
print_r( openssl_x509_parse($cont["options"]["ssl"]["peer_certificate"]) );
UPDATED to include CURLOPT_CERTINFO (credit Crayon Violent)