How to make certificate in OpenSSL to work with php? - php

Recently i decided to add banklink payment option for my php store and after reading specifications of implementation everything seems okey, but the 1 point of it. All public key(certificates) exchange are in X509 format. Now what does that last one mean and how it's different from regular password protected .pem file?
Also with regular password protected .pem file i cannot use php function like openssl_verify() signed by openssl_sign() function.
Could i get some advice here please since the bank that offering this payment method has very little information on this and im totally newb to this.
So the routine i need to do here is generate request.pem for them and send it to them. After that they will sign it or whatever i dunno and i should be able to use it in my application.
Please, tell me if my information is not enough because as i told i don't know much when in comes to certificates or openssl.

PEM file contains encrypted and base64-encoded 'raw' certificate/private key value, so functions that work with PEM should also work with raw certificates. OpenSSL should be able to convert from one format to another.

You have to use curl concept.
curl_setopt($ch, CURLOPT_SSLCERT,'CA.pem');

Honestly, the OpenSSL capabilities in PHP are abysmal. Your best bet is to go for an awesome package like phpseclib.

long time ago i was doing something similar. The difference is they just gave me the pem file, and i used it to connect to their server.
Below I wrote my guiessing for you. :)
Generate myreq.pem using openssl tool.
Send myreq.pem to them and get signed certificate ca.pem.
Use ca.pem in PHP like below.
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'verify_peer', false); //or can be true
stream_context_set_option($context, 'ssl', 'cafile', "ca.pem");
stream_context_set_option($context, 'ssl', 'local_cert', "ca.pem");
stream_context_set_option($context, 'ssl', 'passphrase', 'your pem file password');
//instead tls can be ssl, sslv3, sslv2 depending requirements, 10 is timeout
$sock = stream_socket_client("tls://host:port", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context);
fwrite($sock, "Hi server\r\n");
$strResponse;
while(!feof($sock)) { $strResponse = $strResponse . fgets($sock, 1024); }
fclose($s);
echo $strResponse;

Related

How to set trusted certificate authorities list to socket client in PHP?

In the context of the IHE Connectathon, I want to make a raw socket server responsing to the ATNA profile, which requires TLS sockets with both ends certificates.
My issue if summed up in this message :
https://groups.google.com/d/msg/eu_connectathon/O-VGI_3cltw/ARsElA65ZkkJ
Edit: Sorry, the Google Groups isn't public, here is the message :
Hi Florian,
What exactly does the error message "Server asked for a certificate,
but list of issuers doesn't contain valid certificate authority."
mean and did the implementation of the TLS Tools Client change during
the last years, or am I using the wrong certificates?
The message means that the server has sent the CertificateRequest
message to the clienmt with no values in the certificate_authorities
field.
I ran into this problem last year and discussed this with a developer
of the TLS Tools. He claimed that if a server didn't include this
field, the client wouldn't have a clue what kind of certificate to
return, assuming a scenario where you would connect top multiple
affinity domains, each with their own CA.
It appears that you can instruct OpenSSL to return this value by
calling SSL_CTX_set_client_CA_list, e.g. in
DcmTLSTransportLayer::addTrustedCertificateFile. I haven't tested this
with the TLS tools yet, but I hope to accomplish that before the
connectathon starts.
But my implementation, in PHP, is not the same as their's.
It looks like PHP is missing the "SSL CTX set client CA list" possibility, to tell the client which certificate authority it should use.
$context = stream_context_create();
if ($certificate) {
// Server certificate + private key
stream_context_set_option($context, 'ssl', 'local_cert', "/path/to/server.pem");
stream_context_set_option($context, 'ssl', 'passphrase', $passphrase);
// Client public certificates
stream_context_set_option($context, 'ssl', 'cafile', "/path/to/ca.pem");
stream_context_set_option($context, 'ssl', 'allow_self_signed', false);
stream_context_set_option($context, 'ssl', 'verify_peer', true);
stream_context_set_option($context, 'ssl', 'peer_name', "TlsTools2");
stream_context_set_option($context, 'ssl', 'capture_peer_cert', true);
stream_context_set_option($context, 'ssl', 'capture_peer_cert_chain', true);
}
$this->__socket = #stream_socket_server("tcp://$address:$port", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);
The IHE Gazelle TLS client tells me "Server asked for a certificate, but list of issuers doesn't contain valid certificate authority."
The messages between client and server pass, but the test is not OK, as "not enough secure", as tells the message.
Do you see a problem, and does PHP have more options I didn't see ?
Thanks for your help.
Edit: As #rdlowrey suggested me, I just created a bug report : https://bugs.php.net/bug.php?id=69215
As I mentioned in the initial comment:
PHP's stream server implementation has never actually used SSL_CTX_set_client_CA_list() for encrypted streams.
This has been corrected upstream as referenced in the associated bug report:
https://bugs.php.net/bug.php?id=69215
This change will be reflected once the PHP 5.6.8 binary is released (or you can build PHP manually against the current source prior to that release).
Implementation
Using an updated binary the OP's example code will work as expected without modification. A simple example of a crypto context to use in servers:
<?php
$serverCtx = stream_context_create(['ssl' => [
'local_cert' => '/path/to/my-server-cert.pem',
'passphrase' => 'elephpant',
'cafile' => '/path/to/my-ca-certs.pem',
'verify_peer' => true
]]);
In the above example PHP automatically uses the names from certs found in the my-ca-certs.pem file referenced above when sending the client CA list as part of the TLS handshake.
Notes
When enabling peer verification in encrypted server streams via "verify_peer" => true PHP will not automatically enable peer name verification. Unless you only wish to allow a single certificate holder (with a specific known name) access to your server this is exactly what you want. This default behavior supports the more common use-case of allowing any client whose certificate was signed by a trusted CA to establish connections to your server. However, if you wish to enforce name verification in an encrypted server modify the above context example as shown here:
<?php
$serverCtx = stream_context_create(['ssl' => [
'local_cert' => '/path/to/my-server-cert.pem',
'passphrase' => 'elephpant',
'cafile' => '/path/to/my-ca-certs.pem',
'verify_peer' => true,
'verify_peer_name' => true, // verify the name on the cert
'peer_name' => 'zanzibar' // ensure the cert's name matches this
]]);

php 5.6 update stream_socket_client - No longer opening socket

$certFile = 'ful_path/yourcert.pem';
$context = stream_context_create();
$result = stream_context_set_option($context, 'ssl', 'local_cert',
$certFile);
$result = stream_context_set_option($context, 'ssl', 'verify_peer',
false);
$result = stream_context_set_option($context, 'ssl', 'verify_host',
false);
$result = stream_context_set_option($context, 'ssl',
'allow_self_signed', true);
$sock = stream_socket_client('tls://www.somewhere.com:9999',
$errno,$errstr, 30, STREAM_CLIENT_CONNECT, $context);
have added: www.somewhere.com ca to server certs
have removed &$
have checked yourcert.pem expiry
short of migrating the entire code to curl, i am stuck. it has to be something so obvious as the nose on my face... but what...
First, let me say this:
PHP5.6 defaults to using CA certificate stores managed by your operating system. Unless you're connecting to a DNS name that exposes a self-signed certificate or something you probably don't need an SSL context at all.
Before you do anything else, try connecting without any ssl context. If the remote site's certificate is valid and was signed by any standard certificate authority it should "just work" automagically in PHP 5.6.
That said ... there are several very questionable things in your code snippet. It's really impossible to know which is the real problem without knowing more about what you're trying to do, so I'll just iterate over all of it.
$result = stream_context_set_option($context, 'ssl', 'local_cert',
$certFile);
^ Are you connecting to a site that requires YOU to provide a certificate that the remote server verifies? If not (and this is almost certainly the case), you should not be specifying the "local_cert" option.
$result = stream_context_set_option($context, 'ssl', 'verify_peer',
false);
^ This is a terrible idea as it exposes you to Man-in-the-Middle attacks. You should never do this unless you're testing something in a one-off scenario. DO NOT DO THIS.
$result = stream_context_set_option($context, 'ssl', 'verify_host',
false);
^ This is not even a thing. There's no "verify_host" option in PHP.
$result = stream_context_set_option($context, 'ssl',
'allow_self_signed', true);
So ... in summary there's no good way to answer this with the information you've provided. But I've pointed out the obvious issues ...

'Unable to set private key file' attempting to send push using php

I'm using the Ray Wenderlich push tutorial as a reference to set up push for my app, something I have done dozens of times before, literally dozens, and its always gone smoothly, until now.
When executing the php file to manually test sending the push I'm getting the error:
'Unable to set private key file ... ck.pem'
at the last line:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
ck.pem is a concatentation of the ssl certificate and private key and I CAN use these sussesfully with the following command:
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushCert.pem -key PushKey.pem
When I execute this I get a big spiel and output regarding SSK handshake reading/writing N bytes which indicates it was sucessful and thus the certificate and key must be valid.
So I don't understand why there is no problem using the certificate and key separately when used as arguments to the openssl command line above but there is when used in the concatenated from in the php file.
After getting the problem initially I deleted everything - I clearned out all the certificates and keys from the keychain, deleted all the provisioning profiles etc. The whole lot and started with a clean slate again to make sure I didn't make a mistake somewhere. Same results - keep getting the 'Unable to set private key file'.
I saw a past posting where somebody had the same problem and they solved it by executing the php file using sudo, but that didn't work for me.
Any suggestions, this is driving me mad, especially as I've done it before dozens of times previously sucessfully.
I had this once, after many hours of frustration I tracked the problem down to the fact that I was using TextEdit to edit the .php file's contents and TextEdit was inserting invisible characters.
Not at the end of the line, actually in the line itself i.e. if the original text in the Ray Wenderlich file is like this:
// Put your private key's passphrase here:
$passphrase = ‘cpushchat';
Then after using TextEdit to change the password it looked like this when you viewed it:
// Put your private key's passphrase here:
$passphrase = ‘mypassword';
But if you viewed it in a hex edited, what TextEdit has actually done was to insert invisible (invisible in the in the text viewer) characters like this:
// Put your private key's passphrase here:
$passphrase = ‘E28098mypassword';
Which resulted in the password being incorrect of course and thus leading to that message.

Making a connection in PHP using TLS

I wrote a small SIP client for a special purpose.
Basically it connects to port 5060 using function fsockopen()
$fp = fsockopen("10.0.0.1", 5060, $errno, $errstr, 30);
and then basically reads and writes SIP commands using fread() and fwrite().
Now my SIP service operator wants us clients to use SIPS which is
basically SIP over TLS. I've spent hours looking for information
on how to connect to a port with TLS using PHP but without any
success. Apparently the fsockopen() supports TLS to an extent but
when I replaced the above with:
$fp = fsockopen("tls://10.0.0.1", 5061, $errno, $errstr, 10);
I get nothing. I can connect to the server from my shell with with OpenSSL client:
$ openssl s_client -connect 10.0.0.1:5061
And I can communicate with the SIP server through that connection without problems. OpenSSL support is compiled in the PHP.
My problem is that I can't set up the TLS connection with the server in PHP. I noticed in some forums that in older versions of PHP it was possible to build and use the SSL context with fsockopen() but apparently not anymore, since I get an error about too many parameters.
I also tried using stream_context_create() with stream_context_set_option() but I get no replies from the server:
$context = stream_context_create();
$result=stream_context_set_option($context, 'ssl', 'verify_peer', 'TRUE');
$result=stream_context_set_option($context, 'ssl', 'cafile', 'cert.cer');
$result=stream_context_set_option($context, 'ssl', 'verify_depth', '5');
$fp = stream_socket_client('tls://'.$host.':'.$port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
but I still can't get any replies or errors from the server. What is the recommended and working way of using TLS in PHP?
This is enough to open a TLS connection:
$context = stream_context_create();
$fp = stream_socket_client('tls://'.$host.':'.$port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context)
if the certificates are in their place. If they are not, follow the instructions linked by Filippos.
I think some of your options are wrong for validating self signed certificates; the following should be enough to make it work:
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'allow_self_signed' => true,
'local_cert' => 'cert.cer',
],
]);
I just recently encountered this error, debugging here and there for connection problem or proxy problem, finally found the culprit ... and the solution was to install php-pecl-crypto extension.
My machine is centos 7 using remi-repo for PHP
You will also need to install the appropriate certificates to get TLS working. Check this SO question for an example of how to use your key files with regular TLS connections.
Also, check the specs of SIPS itself in RFC 5630.

PHP WebSocket SSL

I am trying to upgrade my PHP WebSocket library to SSL, using the wss://-protocol on the client-side. However, I find myself at a loss trying to figure out a) how to set up the server socket, and b) how to decode the incoming messages. Here is my current attempt at the former:
$context = stream_context_create();
// local_cert must be in PEM format
stream_context_set_option($context, 'ssl', 'local_cert', $pemFilePath);
// Pass Phrase (password) of private key
stream_context_set_option($context, 'ssl', 'passphrase', $pemPassPhrase);
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$serverSocket = stream_socket_server('ssl://'.$host.':'.$port, $errNo, $errStr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
However, when using SSL as the server protocol, the client does not seem to be sending any messages, and when using tcp, I get weird, encrypted data, although it was my guess that it would be magically decrypted due to my providing the options to the stream context.
What am I doing wrong?
I ran into the same problem with our implementation, and found a good workaround. Put your websocket server on any firewalled port, and tunnel connections from a secure port port. You can use stunnel with a configuration like this:
[websockets]
accept = 8443
connect = 8080
This way, stunnel does all the security, and your websocket server does all the websockets. Win!

Categories