I am new in a php and write a code for push notification in codeigniter but I got these erros.
Here is my model..
function sendmessage($appid, $deviceid, $status, $message)
{
$deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78';
$message = 'My first push notification!';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
//stream_context_set_option($ctx, 'ssl', 'passphrase', 'essar#123');
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp){
exit("Failed to connect: $err $errstr" . PHP_EOL);
}
else {
print "Connection OK/n";
}
echo 'Connected to APNS' . PHP_EOL;
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
$payload = json_encode($body);
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
fclose($fp);
$data = array(
'message' => $this->message . 'add',
'appid' => $this->appid,
'deviceid' => $this->deviceid,
'status' => $status
);
$this->sendmessage($data);
Error message:
Message: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure Message: stream_socket_client(): Failed to enable crypto Message: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error)
Once you created the provisional certificates and push notification for both development and distribution. Follow the steps for Generate Push Notification
In order to use the certificates you generated, you need to create a PEM file that stores both, your Apple Push Notification Service SSL Certificate and your Private Key. You can create the PEM file from a terminal.
Navigate to the directory that contains the certificates and key you generated earlier and execute the following steps. The file names here reflect the names of the certificates that were generated as part of this lesson. You have to update the syntax according the names you gave your certificates.
First create the application certificate PEM file. You can do this by double clicking on the aps_developer_identity.cer certificate file, then opening the Keychain Assistant and exporting the certificate as ap12 file and then converting it to a PEM file, in the same fashion as the PushNotificationApp.p12 is converted to a PEM file. Alternatively you can use a single command line that converts the aps_developer_identity.cer certificate file directly to a PEM file. Here we are opting for the single command line option, as follows:
openssl x509 -inform der -outform pem -in aps_developer_identity.cer -out PushNotificationAppCertificate.pem
Now create the application key PEM file as follows. You need to enter the import password and PEM pass phrase:
openssl pkcs12 -in PushNotificationApp.p12 -out PushNotificationAppKey.pem -nocerts
Enter Import Password:
MAC verified OK
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
Now concatenate the two files:
cat PushNotificationAppCertificate.pem PushNotificationAppKey.pem > PushNotificationAppCertificateKey.pem
Open a Mac terminal and execute the following line from the directory that contains the certificates you generated:
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushNotificationAppCertificate.pem -key PushNotificationAppKey.pem
You are then asked to enter the pass phrase for the key you submitted:
Enter pass phrase for PushNotificationAppKey.pem:
If everything worked, then the server should send you a lot of information that may look something like the following:
CONNECTED(00000003)
depth=1 /C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
verify error:num=20:unable to get local issuer certificate verify return:0
...
Key-Arg : None
Start Time: 1326899631 Timeout : 300 (sec) Verify return code: 0 (ok)
At the end of this, you can enter some text and then select the return key. We entered the text "**Hello World**".
**Hello World
closed**
This completes the communication with the server and verifies that our certificates work.
I have been having the same issue as you mentioned here. This took some time and head scratching to find...
The solution that I found that corrected the handshake error was to download the entrust certificate, and include this in the stream context using the code below:
$entrustCert = '<full path to cert>/entrust_2048_ca.cer';
stream_context_set_option($ctx, 'ssl', 'cafile', entrustCert);
There does seem to intermittent connection issues with the sandbox APN service. I occasionally get errors returned like:
Warning: stream_socket_client(): SSL: Connection reset by peer
Warning: stream_socket_client(): Failed to enable crypto
I hope this is a time saver for someone!
function send_iOS_notifications($device_tokens, $notification_content)
{
$this->load->library('apn');
$this->apn->payloadMethod = 'enhance'; // you can turn on this method for debuggin purpose
$this->apn->connectToPush();
//$badge_count = $this->set_and_get_ios_active_notifications('',$device_token);
// adding custom variables to the notification
$this->apn->setData($notification_content);
$message = $notification_content['message'];
if ((strpos($message, 'http://') !== false) || (strpos($message, 'https://') !== false)) {
$message = "Image";
}
//$send_result = $this->apn->sendMessage($device_token, 'Test notif #1 (TIME:'.date('H:i:s').')', /*badge*/ 2, /*sound*/ 'default' );
$send_result = $this->apn->sendMessage($device_tokens, $message, /*badge*/ '', /*sound*/ 'default' );
if($send_result){
log_message('debug','Sending successful');
$result['status'] = true;
$result['message'] = 'Sending successful';
}
else{
log_message('error',$this->apn->error);
$result['status'] = true;
$result['message'] = $this->apn->error;
}
$this->apn->disconnectPush();
}
Related
Do you know how to sent push notifications to IOS app from php?
It's best answer for somebody http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1
But it doesn't work:
$production = 0;
$passphrase = "kaka";
$deviceToken = "bf38e156 29adac02 6b9b2ec6 35aa4381 df90df65 8238a13c d7f55261 798c3923";
$message = "Hello people! What are you doing?";
$deviceToken = str_replace(" ", "", $deviceToken);
// Create a Stream
$ctx = stream_context_create();
// Define the certificate to use
stream_context_set_option($ctx, "ssl", "local_cert", "CerAndKey.pem");
// Passphrase to the certificate
//stream_context_set_option($ctx, "ssl", "passphrase", $passphrase);
stream_context_set_option($ctx, "ssl", "verify_peer", true);
// load certificate
stream_context_set_option($ctx, "ssl", "cafile", "CerAndKey.pem");
if ($production) {
$gateway = "gateway.push.apple.com:2195";
} else {
$gateway = "gateway.sandbox.push.apple.com:2195";
}
$fp = stream_socket_client($gateway,
$err,
$errstr,
60,
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,
$ctx);
if (!$fp)
exit("Failed to connect amarnew: $err $errstr" . PHP_EOL);
echo "Connected to APNS" . PHP_EOL;
// Create the payload body
$body["aps"] = array(
"badge" => +1,
"alert" => $message,
"sound" => "default"
);
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Ensure that blocking is disabled
//stream_set_blocking($fp, 0);
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
print_r($result);
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered amar '.$message. PHP_EOL;
// Close the connection to the server
fclose($fp);
Many people don't know how is right. PHP can diferent work in linux-systems. If you want I tell you howto easy use it.
Step by step:
1. Generate ssl keys:
#!/bin/bash
## From
# http://stackoverflow.com/questions/24344325/how-to-use-linux-openssl-to-generate-csr-for-ios
openssl genrsa -out ios-dev.key 2048
openssl req -new -key ios-dev.key -out ios-dev.csr
# You must upload to Apple -> Identifiers -> App IDs -> <project> -> edit -> Push Notifications -> Create Certificate ->
# then download -> aps_development.cer
# and put file here
2. Prepare key whith certificate for server script.
#!/bin/bash
## from
# https://gist.github.com/stevenhaddox/1501893
# http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1
openssl x509 -in aps_development.cer -inform der -out CertAndKeyDevCert.pem
##cp CertAndKeyDevCert.pem /etc/ssl/certs/
openssl rsa -in ios-dev.key -out CertAndKeyDevKey.pem -outform PEM
cat CertAndKeyDevCert.pem CertAndKeyDevKey.pem > CertAndKey.pem
3. Connect to Apple server for test
#!/bin/bash
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert CertAndKey.pem -key CertAndKey.pem -CAfile CertAndKey.pem
Result in console:
CONNECTED(00000003)
depth=2 /O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Certification Authority (2048)
verify return:1
depth=1 /C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
verify return:1
depth=0 /C=US/ST=California/L=Cupertino/O=Apple Inc./CN=gateway.sandbox.push.apple.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=California/L=Cupertino/O=Apple Inc./CN=gateway.sandbox.push.apple.com
i:/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
1 s:/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
i:/O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Certification Authority (2048)
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFMzCCBBugAwIBAgIETCMmsDANBgkqhkiG9w0BAQUFADCBsTELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
Lm5ldC9ycGEgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
KGMpIDIwMDkgRW50cnVzdCwgSW5jLjEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eSAtIEwxQzAeFw0xNDA1MjMxNzQyNDJaFw0xNjA1MjQw
NzA1MTNaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYD
VQQHEwlDdXBlcnRpbm8xEzARBgNVBAoTCkFwcGxlIEluYy4xJzAlBgNVBAMTHmdh
dGV3YXkuc2FuZGJveC5wdXNoLmFwcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAOQpUlXpU3+LJ2XR01QdVooN7S9OFOINp3/tomPaenQAwFGo
qIakKFcN7AotWLFXFcR0QXKJkn4PL/zPKDBucyRFkc79S5+ZraGRISWfi7G8XeaG
G3GzgeVQ977Qrn0IdCswnbwLsJoErnmq4AveQajUbYueR9SxhkWBwMimSxXzXoOS
XUOPzRvzObCxVZrvBBDSRJCeNVnVxtCmb17DM3+z5GZatBwWnvw0jgvSQsgof+uC
idXgqcN4msv3tVH54ipmuD9kbbwvtnDCHBZRXMMmhUfFXZRuE8GBEbPfVkqB16ad
JV4TVrVxwFENwdnsX9CXavHCgFJhtHRWKOoCH48CAwEAAaOCAY0wggGJMAsGA1Ud
DwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwMwYDVR0fBCww
KjAooCagJIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9sZXZlbDFjLmNybDBkBggr
BgEFBQcBAQRYMFYwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0
MC8GCCsGAQUFBzAChiNodHRwOi8vYWlhLmVudHJ1c3QubmV0LzIwNDgtbDFjLmNl
cjBKBgNVHSAEQzBBMDUGCSqGSIb2fQdLAjAoMCYGCCsGAQUFBwIBFhpodHRwOi8v
d3d3LmVudHJ1c3QubmV0L3JwYTAIBgZngQwBAgIwKQYDVR0RBCIwIIIeZ2F0ZXdh
eS5zYW5kYm94LnB1c2guYXBwbGUuY29tMB8GA1UdIwQYMBaAFB7xq4kG+EkPATN3
7hR67hl8kyhNMB0GA1UdDgQWBBSSGfpGPmr9+FPcqRiStH0iKRBL7DAJBgNVHRME
AjAAMA0GCSqGSIb3DQEBBQUAA4IBAQAkj6+okMFVl7NHqQoii4e4iPDFiia+LmHX
BCc+2UEOOjilYWYoZ61oeqRXQ2b4Um3dT/LPmzMkKmgEt9epKNBLA6lSkL+IzEnF
wLQCHkL3BgvV20n5D8syzREV+8RKmSqiYmrF8dFq8cDcstu2joEKd173EfrymWW1
fMeaYTbjrn+vNkgM94+M4c/JnIDOhiPPbeAx9TESQZH+/6S98hrbuPIIlmaOJsOT
GMOUWeOTHXTCfGb1EM4SPVcyCW28TlWUBl8miqnsEO8g95jZZ25wFANlVxhfxBnP
fwUYU5NTM3h0xi3rIlXwAKD6zLKipcQ/YXRx7oMYnAm53tfU2MxV
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Cupertino/O=Apple Inc./CN=gateway.sandbox.push.apple.com
issuer=/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
---
Acceptable client certificate CA names
/C=US/O=Apple Inc./OU=Apple Certification Authority/CN=Apple Root CA
/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
/C=US/O=Apple Inc./OU=Apple Certification Authority/CN=Apple Application Integration Certification Authority
---
SSL handshake has read 3160 bytes and written 2176 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES256-SHA
Session-ID:
Session-ID-ctx:
Master-Key: 5602BDD396ED4E8E9FE2BDFAD5A0226DEBB53C568EB34E57DE69E0024041F1F9F242A551E88758F5B0D4A2CDBEACBCB2
Key-Arg : None
Start Time: 1456218193
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
Right responce:
1. verify return:1
2. Verify return code: 0 (ok)
Your certivicate and key are OK!
Next step is php-script:
$production = 0;
$keyFile = "CertAndKey.pem";
$deviceToken = "bf38e156 29adac02 6b9b2ec6 35aa4381 df90df65 8238a13c d7f55261 798c3923";
$message = "Ещё одно ...";
$deviceToken = str_replace(" ", "", $deviceToken);
if ($production) {
$gateway = 'gateway.push.apple.com:2195';
} else {
$gateway = 'gateway.sandbox.push.apple.com:2195';
}
// Create the payload body
$body['aps'] = array(
'badge' => +1,
'alert' => $message,
'sound' => 'default',
);
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
$filepath = "/tmp/sentPush.dat";
file_put_contents($filepath,$msg);
$command = "cat ".$filepath." | openssl s_client -connect ".$gateway." -cert ".$keyFile." -key ".$keyFile." -CAfile ".$keyFile." 2>&1";
exec($command, $tmp);
$last = end($tmp);
unlink($filepath);
if ($last=="DONE"){
$result = "Message successfully!";
}else{
$result = "ERROR:Message not send" . PHP_EOL;
}
This solution will work somewhere, because I use only openssl.
+1 is not allowed for badge
Store the badge number where you store the device tokens and update it with every push notification.
There are some points that should be kept in mind:
This problem might be occur due to $production = 0;. It should be $production = true;
Try to use the full path for .pem file .=> stream_context_set_option($ctx, "ssl", "local_cert", "CerAndKey.pem");
use __FILE__ to get full path like: stream_context_set_option($ctx, "ssl", "local_cert", "(full-path-to-file)/CerAndKey.pem");
Double check the device token used to send notifications.
And in last try chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $item[0])) . chr(0) . chr(strlen($payload)) . $payload;
instead of
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
May be it helps to you or someone else. Should try once.
Here are a few tips that should can help you figure out:
Go to entrust.net/downloads/root_request.cfm and download entrust_2048_ca.cer
Add following code:
stream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');
Make sure if path is right: 'CerAndKey.pem'? not'../XXfolder/CerAndKey.pem' ?
Cancel the line of verify_peer setting;
Instead, you should set passphrase - the password that you entered when exporting p12 from KeyChain.
Switch and try both the sandbox and live ssl links.
Switch dev and production pem and try both.
I have created a client-server solution for APNS in PHP.
The "server" is a continously running script that waits for connections and copies the content to APNS. This means it keeps the connection to APNS open all the time. I use fwrite and stream context as such. This is the part that accepts incoming connections:
if ($client = #stream_socket_accept($this->socket, -1)) {
$this->writeLine("Received connection.");
$this->inputBuffer = stream_get_contents($client);
}
It runs in a while-loop, so that next iteration copies the contents of inputBuffer to APNS as such:
$writtenBytes = #fwrite($this->connectionAPNS, $this->inputBuffer, strlen($this->inputBuffer));
I then check for bytes written like:
if ($writtenBytes > 0) {
$this->writeLine("Copied $writtenBytes bytes to APNS.");
return true;
}
Now, the problem arises when the connection has been sitting idle for some time. The first push notification WILL return the number of bytes written to APNS - such as "Copied 157 bytes to APNS.", indicating that the fwrite works, but the message is not delivered.
The next notification then returns 0 bytes written, reconnects automatically (part of my script) and writes the bytes that failed - then it works.
Why do I get bytes written returned if the connection actually failed to write or if APNS did not accept them?
I use the enhanced notification format and I check for errors returned, which seem to be none.
I use this code for that, which I found on a blog, where I pass the connection handle to the function after writing to APNS:
function checkAppleErrorResponse($fp)
{
$apple_error_response = fread($fp, 6);
if ($apple_error_response) {
error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);
if ($error_response['status_code'] == '0') {
$error_response['status_code'] = '0-No errors encountered';
} else {
// Here is some fancy if/else error handling, but I never end in this else , so I excluded it.
}
}
}
The 'client' part that keeps the connection APNS open looks like this, and is called whenever writtenBytes returns 0 (error) or when the script first loads:
function connectToAPNS()
{
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $this->certpath);
stream_context_set_option($ctx, 'ssl', 'passphrase', $this->passphrase);
// Open a connection to the APNS server
$error = "";
$errno = 0;
$this->connectionAPNS = stream_socket_client(
'ssl://gateway.push.apple.com:2195',
$errno,
$error,
60,
STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT,
$ctx
);
if ($this->connectionAPNS) {
stream_set_blocking($this->connectionAPNS, 0);
$this->writeLine("Connected to APNS!");
return true;
}
}
Is your app a release or a debug version? In each case you have to communicate with a different web-service and use different certificates.
In sandbox/debug mode communicate with ssl://gateway.sandbox.push.apple.com:2195
In production/relase mode communicate with ssl://gateway.push.apple.com:2195
The notification will be silently dropped, if you communicate with the wrong service.
I am trying to create the server side connection for apple push notifications.
First, I ask from the user (who probably will be an ios dev) to give the .cer and .p12 files that Apple provides in order to make it a .pem file.
Below is the .pem certificate creation.
$dir = $this->directory.'/certificates';
$password = 'a_user_password';
$certificate = $dir.'/certificate.cer';
$key_password = $dir.'/key.p12';
exec('openssl x509 -inform der -in '.$certificate.' -out '.$dir.'/certificate.pem');
exec('openssl pkcs12 -nocerts -out '.$dir.'/key.pem -in '.$key_password.' -passout pass:'.$password.' -passin pass:'.$password);
$filename = $key_password;
$results = array();
$worked = openssl_pkcs12_read(file_get_contents($filename), $results, $obj->password);
if($worked) {
$current = file_get_contents($dir.'/key.pem');
$current .= $results['pkey'];
file_put_contents($dir.'/key.pem', $current);
} else {
echo openssl_error_string();
}
exec('cat '.$dir.'/certificate.pem '.$dir.'/key.pem > '.$dir.'/apns_certificate.pem');
So far, so good. I have tested that the above generated apns_certificate.pem is successful with apple through command line via:
s_client -connect gateway.sandbox.push.apple.com:2195 -cert certificate.pem -key key.pem
However,
When I try to connect with apns through PHP I cannot. Follows the last php code that I have tried and I have seen that for others has worked:
$this->certificate = ROOT.'/certificates/apns_certificate.pem';
$this->socket = 'ssl://gateway.push.apple.com:2195';
if (!file_exists($this->certificate)) {
$this->error = 'Certificate file not found';
return false;
}
$this->stream_context = stream_context_create();
$this->stream_options = array(
'ssl' => array(
'local_cert' => $this->certificate,
'passphrase' => 'a_user_password', //same with the one used in my previous code
)
);
$success = stream_context_set_option($this->stream_context, $this->stream_options);
if ($success == false) {
$this->error = 'Secure connection failed';
return false;
}
$this->socket_client = stream_socket_client($this->socket, $con_error, $con_error_string, $this->timeout, STREAM_CLIENT_CONNECT, $this->stream_context);
if ($this->socket_client === false) {
$this->error = $con_error_string;
return false;
} else {
return true;
}
The above code returns me an error:
Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages: error:14094416:SSL routines:SSL3_READ_BYTES:sslv3 alert certificate unknown
Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195
Thank you in advance for your help!
The above code is correct. There was an error with the certification .p12 . Also I changed the exec for .p12 convertion file to:
exec('openssl pkcs12 -out '.$dir.'/key.pem -in '.$key_password.' -passout pass:'.$password.' -passin pass:'.$password.' -nodes');
I have an ssl server (test) in c - and a client written in php. the server works fine when a client written in c connects, but when the php client (virtually identical to the c client) sends, the message appears on the server only after buffer overflow or disconnect.
why a c program and not a php program would work is a mystery to me.
I've tried buffer control, unblocking, adding nulls to the send string.
php 5.4 centos linux 2.6
i can add code if appropriate - it's quite vanilla. but i thought there might be a difference between the way php handles stream traffic and c. my confidence in my code.
thanks for any thoughts.
#!/usr/local/bin/php
<?php
date_default_timezone_set('America/New_York');
$host = 'localhost';
$port = 9255;
$timeout = 5.0;
function gen_cert($pem_file,$pem_passphrase)
{
// Certificate data:
$dn = array(
"countryName" => "US",
"stateOrProvinceName" => "Texas",
"localityName" => "Houston",
"organizationName" => "<org name>",
"organizationalUnitName" => "Tech",
"commonName" => "kensie2",
"emailAddress" => "<email>"
);
// Generate certificate
$privkey = openssl_pkey_new();
$cert = openssl_csr_new($dn, $privkey);
$cert = openssl_csr_sign($cert, null, $privkey, 365);
// Generate PEM file
$pem = array();
openssl_x509_export($cert, $pem[0]);
openssl_pkey_export($privkey, $pem[1], $pem_passphrase);
openssl_pkey_export($privkey, $pem[1], $pem_passphrase);
$pem = implode($pem);
// Save PEM file
// $pemfile = $filename;
file_put_contents($pem_file, $pem);
chmod($pem_file,0400);
}
$pem_passphrase = 'foozot';
$pem_file = 'test3_client.pem';
if(!file_exists($pem_file))
gen_cert($pem_file,$pem_passphrase);
$context = stream_context_create();
// local_cert must be in PEM format
stream_context_set_option($context, 'tls', 'local_cert', $pem_file);
// Pass Phrase (password) of private key
stream_context_set_option($context, 'tls', 'passphrase', $pem_passphrase);
stream_context_set_option($context, 'tls', 'allow_self_signed', true);
stream_context_set_option($context, 'tls', 'verify_peer', true);
$fp = stream_socket_client('tls://'.$host.':'.$port, $errno, $errstr, $timeout,
STREAM_CLIENT_CONNECT, $context);
if($fp)
{
$buf = fread($fp,8192);
fwrite(STDOUT,"from server: $buf");
// tried this - no difference
// stream_set_write_buffer($fp,0);
// tried this - no difference
// stream_set_blocking($fp,0);
$ary = stream_get_meta_data($fp);
fwrite(STDERR,"meta=" . print_r($ary,true) . "\n");
while(1)
{
$buf = fgets(STDIN);
fwrite(STDOUT,"buf=$buf\n");
if(strstr($buf,"\n") === null)
$buf .= "\n\0";
$er = fwrite($fp, $buf,strlen($buf) + 1);
fwrite(STDERR,posix_strerror(posix_get_last_error()) . "\n");
}
}
else
{
echo "ERROR: $errno - $errstr<br />\n";
}
?>
I'musing code from Professional Linux Network Programming - Chapter 8 - server.c
By Nathan Yocom, plnp#yocom.org modified to prefork. it uses BIO for io.
as a model for the c code. i don't think i can post it without some copyright violation.
it does work. far more tested than my code.
I am working on below things:
Generate CSR(Certificate Signing Request)
Upload SSL Certificates
To generate SSL certificate I am using something like:
$privkey = openssl_pkey_new();
$csr = openssl_csr_new($dn, $privkey);
$sscert = openssl_csr_sign($csr, null, $privkey, $days);
openssl_csr_export($csr, $csrout);
openssl_pkey_export($privkey, $pkeyout, $_POST['password']);
openssl_pkey_export_to_file($privkey, "<path/to/store/server.key>");
openssl_csr_export_to_file($csr, "/tmp/".<domain-name>.".csr");
Now using that CSR request, I am able to generate(domain-name.cer),(DigitalCert.cer).
Now once I upload this(.cer) certificates, I need to verify those certificates.
Reason: Someone generated these certificates on say "a.com" and tries to upload on "b.com". this should not happen, so I want to validate the uploaded SSL certificates.
In PHP, we have
$ok = openssl_verify($data, $signature, $pubkeyid);
but i am not able to get what things would be treated as $data, $signature and $pubkeyid based on the above certificate generation process.
Check this out:
Verify SMTP in PHP
<?php
$server = "smtp.gmail.com"; // Who I connect to
$myself = "my_server.example.com"; // Who I am
$cabundle = '/etc/ssl/cacert.pem'; // Where my root certificates are
// Verify server. There's not much we can do, if we suppose that an attacker
// has taken control of the DNS. The most we can hope for is that there will
// be discrepancies between the expected responses to the following code and
// the answers from the subverted DNS server.
// To detect these discrepancies though, implies we knew the proper response
// and saved it in the code. At that point we might as well save the IP, and
// decouple from the DNS altogether.
$match1 = false;
$addrs = gethostbynamel($server);
foreach($addrs as $addr)
{
$name = gethostbyaddr($addr);
if ($name == $server)
{
$match1 = true;
break;
}
}
// Here we must decide what to do if $match1 is false.
// Which may happen often and for legitimate reasons.
print "Test 1: " . ($match1 ? "PASSED" : "FAILED") . "\n";
$match2 = false;
$domain = explode('.', $server);
array_shift($domain);
$domain = implode('.', $domain);
getmxrr($domain, $mxhosts);
foreach($mxhosts as $mxhost)
{
$tests = gethostbynamel($mxhost);
if (0 != count(array_intersect($addrs, $tests)))
{
// One of the instances of $server is a MX for its domain
$match2 = true;
break;
}
}
// Again here we must decide what to do if $match2 is false.
// Most small ISP pass test 2; very large ISPs and Google fail.
print "Test 2: " . ($match2 ? "PASSED" : "FAILED") . "\n";
// On the other hand, if you have a PASS on a server you use,
// it's unlikely to become a FAIL anytime soon.
// End of maybe-they-help-maybe-they-don't checks.
// Establish the connection
$smtp = fsockopen( "tcp://$server", 25, $errno, $errstr );
fread( $smtp, 512 );
// Here you can check the usual banner from $server (or in general,
// check whether it contains $server's domain name, or whether the
// domain it advertises has $server among its MX's.
// But yet again, Google fails both these tests.
fwrite($smtp,"HELO $myself\r\n");
fread($smtp, 512);
// Switch to TLS
fwrite($smtp,"STARTTLS\r\n");
fread($smtp, 512);
stream_set_blocking($smtp, true);
stream_context_set_option($smtp, 'ssl', 'verify_peer', true);
stream_context_set_option($smtp, 'ssl', 'allow_self_signed', false);
stream_context_set_option($smtp, 'ssl', 'capture_peer_cert', true);
stream_context_set_option($smtp, 'ssl', 'cafile', $cabundle);
$secure = stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
stream_set_blocking($smtp, false);
$opts = stream_context_get_options($smtp);
if (!isset($opts["ssl"]["peer_certificate"]))
$secure = false;
else
{
$cert = openssl_x509_parse($opts["ssl"]["peer_certificate"]);
$names = '';
if ('' != $cert)
{
if (isset($cert['extensions']))
$names = $cert['extensions']['subjectAltName'];
elseif (isset($cert['subject']))
{
if (isset($cert['subject']['CN']))
$names = 'DNS:' . $cert['subject']['CN'];
else
$secure = false; // No exts, subject without CN
}
else
$secure = false; // No exts, no subject
}
$checks = explode(',', $names);
// At least one $check must match $server
$tmp = explode('.', $server);
$fles = array_reverse($tmp);
$okay = false;
foreach($checks as $check)
{
$tmp = explode(':', $check);
if ('DNS' != $tmp[0]) continue; // candidates must start with DNS:
if (!isset($tmp[1])) continue; // and have something afterwards
$tmp = explode('.', $tmp[1]);
if (count($tmp) < 3) continue; // "*.com" is not a valid match
$cand = array_reverse($tmp);
$okay = true;
foreach($cand as $i => $item)
{
if (!isset($fles[$i]))
{
// We connected to www.example.com and certificate is for *.www.example.com -- bad.
$okay = false;
break;
}
if ($fles[$i] == $item)
continue;
if ($item == '*')
break;
}
if ($okay)
break;
}
if (!$okay)
$secure = false; // No hosts matched our server.
}
if (!$secure)
die("failed to connect securely\n");
print "Success!\n";
// Continue with connection...
?>
This works for me
$crt_md5=exec('openssl x509 -noout -modulus -in /path/to/domain.crt/ | openssl md5 | sed "s/^.* //"');
$key_md5=exec('openssl rsa -noout -modulus -in /path/to/server.key | openssl md5 | sed "s/^.* //"');
if($crt_md5 != $key_md5){
echo 'BAD';
}
else{
echo "GOOD";
}
sed "s/^.* //" - will remove (stdin)= thing from the output, so that
you get exact md5 string
this is how i do it...
system('openssl x509 -noout -modulus -in '.$crt.' | openssl md5', $crt_md5);
system('openssl rsa -noout -modulus -in '.$key.' | openssl md5', $key_md5);
if($crt_md5 != $key_md5){
echo 'BAD';
}
Try openssl_x509_check_private_key( $crt, $key ) it returns boolean
ref http://php.net/manual/en/function.openssl-x509-check-private-key.php
WARNING: openssl_x509_check_private_key will not work for some case.
Example:
SSL certificate like this:
-----BEGIN CERTIFICATE-----
xxxx
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
xxxx
xxxx
This certificate does not end with -----END CERTIFICATE----- , but it can still pass the check of this function. It will return true to tell you that it is correct, but it is not actually. If you upload this certificate to your application, such as Nginx , Nginx will tell you an error.
This doesn't seem to be an error that only appears in PHP. If you check with the openssl function on the command line, it will tell you the same result.
So I think the best way is that you need to check whether the paragraphs of the certificate are complete.
After confirming that the format is correct, use this function to verify the certificate and private key.