I can create a ssl key and certificate with the following script:
<?php
$name='example_ss'; //Will add _key.pem, _crt.pem, _csr.pem
$data = [
"countryName" => "US",
"stateOrProvinceName" => "CA",
"localityName" => "San Francisco",
"organizationName" => "example.net",
"organizationalUnitName" => "example.net",
"commonName" => "example.net",
"emailAddress" => "exampleATgmailDOTcom"
];
$pem_passphrase = 'secret';
// Generate certificate
$key = openssl_pkey_new();
$csr = openssl_csr_new($data, $key);
$csr = openssl_csr_sign($csr, null, $key, 365);
// Generate PEM file
$pem = [];
openssl_x509_export($csr, $pem[0]);
file_put_contents($name.'_crt.pem', $pem[0]);
//openssl_pkey_export($key, $pem[1], $pem_passphrase); //Remove $pem_passphrase for none
openssl_pkey_export($key, $pem[1]); //Remove $pem_passphrase for none
$pem = implode($pem);
file_put_contents($name.'_key.pem', $pem);
Move them as required:
sudo mv example_ss_crt.pem /etc/pki/tls/certs/example_ss_crt.pem
sudo mv example_ss_key.pem /etc/pki/tls/private/example_ss_key.pem
And everything works find.
But when I create them manually: (w/ and w/o using -sha256)
openssl req -x509 -newkey rsa:4096 -keyout example_ss_key.pem -out example_ss_crt.pem -days 365 -nodes
And move them as I did for the previous, I get the following error:
Unable to complete SSL/TLS handshake: stream_socket_enable_crypto():
Unable to set local cert chain file `/etc/pki/tls/private/example_ss_key.pem';
Check that your cafile/capath settings include details of your certificate and its issuer
What is the difference between these two sets of keys and how do I create them manually? I don't think it is relevant, but they are being used for a tcp sockets connection between Centos7 and a Raspberry Pi.
EDIT. The server is created with the following script. I am not using a passphrase.
$loop = \React\EventLoop\Factory::create();
$server = new \React\Socket\TcpServer($this->host['url'].':'.$this->host['port'], $loop);
if($this->host['tls']) {
$server = $this->host['privateKey']
?new \React\Socket\SecureServer($server, $loop, ['local_cert' => $this->host['privateKey'],'passphrase' => $this->host['passphrase'] ])
:new \React\Socket\SecureServer($server, $loop, ['local_cert' => $this->host['privateKey'] ]);
}
Dummy mistake. http://php.net/manual/en/context.ssl.php
local_cert string Path to local certificate file on filesystem. It
must be a PEM encoded file which contains your certificate and private
key. It can optionally contain the certificate chain of issuers. The
private key also may be contained in a separate file specified by
local_pk
.
Related
I am facing the problem that a certificate issued by a CA (Microsoft AD Certification service in this case) does not match to the private key that is used to create the certificate request in PHP and I have no idea, how this can be.
Here's my code:
<?php
$dn = array(
"countryName" => "NL",
"stateOrProvinceName" => "state",
"localityName" => "City",
"organizationName" => "Company",
"organizationalUnitName" => "Unit",
"commonName" => exec("hostname").'.'.exec("hostname -d"),
"emailAddress" => "mail#example.com"
);
$res_privkey = openssl_pkey_new();
openssl_pkey_export_to_file($res_privkey, '/var/www/html/privkey.pem');
$res_csr = openssl_csr_new($dn, $res_privkey);
openssl_csr_export_to_file($res_csr, '/var/www/html/csr.pem');
?>
I then use csr.pem as a signing request and get a base64 encoded certificate certnew.cer back from my CA. But when I use the private key privkey.pem and the certificate certnew.cer in my apache config I get the apache error message
AH02565: Certificate and private key 127.0.0.1:443:0 from ... and ... do not match
Any ideas?
Not really a solution to the question, but at least a work-around: I changed my php to call a bash script that does the job perfectly:
<?php
$key = "/var/www/html/privkey.pem";
$csr = "/var/www/html/csr.pem";
$subj = "/C=NL/ST=state/L=City/O=Company/OU=Unit/CN=".exec("hostname").'.'.exec("hostname -d")."/emailAddress=mail#example.com";
exec("sudo ./create_csr.sh $key $csr $subj");
?>
And here is the bash script:
#!/bin/bash
key=$1
csr=$2
subj=$3
openssl req -new -key $key -out $csr -subj $subj
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 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();
}
I want to generate example.com.crt and example.com.pem using php. The Linux command to get the files is given:
openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes
-out example.com.crt -keyout example.com.pem
I want to get the two file contents in two string in php. What is the php equivalent code for this?
UPDATE: Please don't ask to execute the Linux command.
here another command:
<?php
// generate 2048-bit RSA key
$pkGenerate = openssl_pkey_new(array(
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA
));
// get the private key
openssl_pkey_export($pkGenerate,$pkGeneratePrivate); // NOTE: second argument is passed by reference
// get the public key
$pkGenerateDetails = openssl_pkey_get_details($pkGenerate);
$pkGeneratePublic = $pkGenerateDetails['key'];
// free resources
openssl_pkey_free($pkGenerate);
// fetch/import public key from PEM formatted string
// remember $pkGeneratePrivate now is PEM formatted...
// this is an alternative method from the public retrieval in previous
$pkImport = openssl_pkey_get_private($pkGeneratePrivate); // import
$pkImportDetails = openssl_pkey_get_details($pkImport); // same as getting the public key in previous
$pkImportPublic = $pkImportDetails['key'];
openssl_pkey_free($pkImport); // clean up
// let's see 'em
echo "\n".$pkGeneratePrivate
."\n".$pkGeneratePublic
."\n".$pkImportPublic
."\n".'Public keys are '.(strcmp($pkGeneratePublic,$pkImportPublic)?'different':'identical').'.';
?>
You could use phpseclib, a pure PHP X.509 implementation:
http://phpseclib.sourceforge.net/x509/examples.html#selfsigned
<?php
include('File/X509.php');
include('Crypt/RSA.php');
// create private key / x.509 cert for stunnel / website
$privKey = new Crypt_RSA();
extract($privKey->createKey());
$privKey->loadKey($privatekey);
$pubKey = new Crypt_RSA();
$pubKey->loadKey($publickey);
$pubKey->setPublicKey();
$subject = new File_X509();
$subject->setDNProp('id-at-organizationName', 'phpseclib demo cert');
//$subject->removeDNProp('id-at-organizationName');
$subject->setPublicKey($pubKey);
$issuer = new File_X509();
$issuer->setPrivateKey($privKey);
$issuer->setDN($subject->getDN());
$x509 = new File_X509();
//$x509->setStartDate('-1 month'); // default: now
//$x509->setEndDate('+1 year'); // default: +1 year
$result = $x509->sign($issuer, $subject);
echo "the stunnel.pem contents are as follows:\r\n\r\n";
echo $privKey->getPrivateKey();
echo "\r\n";
echo $x509->saveX509($result);
echo "\r\n";
?>
That'll create a private key and a self-signed X.509 cert (as your CLI example does) with the private keys corresponding public key.
Execute the command with the exec() or system() function, then read the output files with file_get_contents().
There is OpenSSL extension to PHP, you should check if it is not enough for you as it would be better to use it insteaf of exec():
http://php.net/manual/en/book.openssl.php
Perhaps it's not the finest solution, but you can use exec() and file_get_contents(), something like:
exec("openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes -out example.com.crt -keyout example.com.pem");
$crtFile = file_get_contents("example.com.crt");
$pemFile = file_get_contents("example.com.pem");
Be aware file permissions and obviously file paths. Better if you add some error handling around these commands.
Read more: http://php.net/manual/en/function.exec.php
And: http://php.net/manual/en/function.file-get-contents.php