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.
Related
I'm having deep trouble with implementing a PHP server websocket. It works completely fine without SSL.
However, when I switch client to "wss://" and add SSL context to the server socket, I get random binary garbage after client connects. I searched everything on the web but cannot figure it out.
Client code does basically nothing:
var ws = new WebSocket('ws://127.0.0.1:8888');
ws.onopen = function() {
console.log('ws open');
}
Here is the minimal PHP code for SSL-less connection which works fine:
<?php
$socket = stream_socket_server(
'tcp://127.0.0.1:8888',
$errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN
);
$cn = stream_socket_accept($socket, 99999, $name);
$text = fread($cn, 1024);
echo $text;
?>
With it, and client using "ws://", I receive normal WebSocket upgrade request which I can process. Everything is fine.
Now, I change client call to "wss://" and add SSL to server:
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', '../ssl/cert.pem');
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$socket = stream_socket_server(
'tcp://127.0.0.1:8888',
$errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
$context
);
and this time I receive some binary encoded data which I'm unable to process:
▬♥☺☻☺☺♥♥{8T▬↕↑/Б{Ҭh#_W l0w2♣9x(j‹~Y{A∟MTT
::‼☺‼☻‼♥+/,0̨̩‼¶/5☺☺↨☺☺
↕►♦♦♦☺♣♣♣♠♠☺↕3+)**☺↔ JtD~[}▼$▲ɒNt◄vCyw-☻☺☺+♂
**♥♦♥♥♥☻♥☺♥☻☻ ☺§
I also tried all kinds of keys and certificates for SSL. I used Apache and Nginx certs, I created them manually with OpenSSL, I created them with PHP code that I found on web, nothing helps. I reproduced this on Windows and Ubuntu systems with PHP7.4 and Apache and Nginx web servers, used localhost and external IP addresses, does not differ.
I'm running the next script from my local host and the production server, and Im getting different outputs. Anyone knows why am I getting that false from my localhost?
<?php
$host = 'ssl://mail.companyname.org';
$port = 993;
$error = 0;
$errorString = "";
var_dump(fsockopen($host, $port, $error, $errorString, 30));
var_dump($errorString);
var_dump($error);
Local host output:
bool(false)
Production server output:
resource(4) of type (stream)
UPDATE: after the comments/answer I have modified the code and now Im getting this output on my local host:
PHP Warning: fsockopen(): SSL operation failed with code 1. OpenSSL
Error messages: error:1416F086:SSL
routines:tls_process_server_certificate:certificate verify failed in
/tmp/test.php on line 7 PHP Warning: fsockopen(): Failed to enable
crypto in /tmp/test.php on line 7 PHP Warning: fsockopen(): unable to
connect to ssl://mail.twmdata.org:993 (Unknown error) in /tmp/test.php
on line 7 bool(false) string(0) "" int(0)
it seems this is problem with server certificate :
first you can check if your server certificate and its chains are valid by this:
https://www.sslshopper.com/ssl-checker.htm
if somethings were wrong in ssl-checker?
you can try to correct SSL certificate configs in companyname.org
if you succeed and error was persists ?
you have to add Certificate files manually.
if you have a self-signed certificate:
you have to add Certificate files manually.
if you dont have certificate nor you dont care about man-in-the-middle attack,
you can still use SSL without Certificate.
turn off php fsock Certificate check (not recommended)
its recommended to have a certificate at least a self-signed. if you have a self-signed try 1 solution.
I have found the Problem
You have exposed your Domain name in your PHP Warning Log, so i have checked your domain SSL.
after i check your company`s domain certificate using this tool:
https://www.sslshopper.com/ssl-checker.html#hostname=twmdata.org
it had 2 errors with your certificates:
This certificate has expired (0 days ago). Renew now.
None of the common names in the certificate match the name that was entered (twmdata.org). You may receive an error when accessing this site in a web browser.
so it seems you have to renew your certificate first
Update:
i have found this answer maybe helpful
https://stackoverflow.com/a/40962061/9287628
it suggested to use
stream_context_create(['ssl' => [
'ciphers' => 'RC4-MD5'
]])
as #ChrisHaas suggested connecting with stream_context_create and stream_socket_client brings you a lot of option if you want to dictate the cert directory or you want to turn off certificate check.
Per the documentation for fsockopen
The function stream_socket_client() is similar but provides a richer set of options, including non-blocking connection and the ability to provide a stream context.
Basically, fsockopen is very low-level but without many options, or, arguably, "sane defaults".
Instead, you can switch to stream_socket_client which will allow you to specify a context as the last parameter, and that object has many options, including a dedicated one with over a dozen options specific to SSL. The object created from this function is compatible with fwrite and other functions, so it should do everything you are hoping for.
$context = stream_context_create([/*Options here*/]);
$connection = stream_socket_client($host, $errno, $errorString, 30, null, $context);
Now, what options should you use?
The worst option that might work is probably verify_peer. I say "worst" because you are throwing away the verifiability part of SSL/TLS and only using it for encryption, and doing this will make you susceptible to MitM attacks. However, there's a place and time for this, so you could try it if the other options are too complicated.
$context = stream_context_create(['ssl' => ['verify_peer' => false]]);
$connection = stream_socket_client($host, $errno, $errorString, 30, null, $context);
Instead, I'd recommend using either cafile or capath which do the same thing except the former is for a file while the latter is for a directory.
$context = stream_context_create(['ssl' => ['verify_peer' => true, 'cafile' => '/path/to/file']]);
$connection = stream_socket_client($host, $errno, $errorString, 30, null, $context);
What certs should you use? We use this library to pull in recent CA files on a periodic basis, very convenient. There's a little bit of setup that's per-project but once you get it it goes pretty fast. See this for pulling in a CA file at a well-known location.
One other last option is local_cert which you can use with a PEM file that holds the certificate and private key from the server, if you have access to that.
EDIT
The cert on mail.twmdata.org:993 is different than the web server's cert that other people are talking about, which is generally a best practice. You can inspect that cert using:
openssl s_client -connect mail.twmdata.org:993 -servername mail.twmdata.org
If you do that, you'll see that the server has a self-signed cert which you can get around by setting the verify_peer option to false.
Remove the # symbol. You are hiding error messages that might tell you what the problem is. You should also set a variable in the errorno argument to fsockopen() and echo it for debugging.
My guess would be that you haven't installed PHP with SSL support on your local server. See here.
Companyname.org might also block requests from your local server that are allowed from the production server.
I'm using Gallery 3 for image upload.
When I use https://domain the upload works fine. But as I use https://domain Gallery3 is not able to make a connection.
Errors : **fsockopen(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in fileName**
**fsockopen(): Failed to enable crypto in finleName**
**fsockopen(): unable to connect to ssl://domain:443 (Unknown error) in /**
Below are the observations:
The URL to connect becomes ssl://domain having port 443
fsockopen fails to make a connects and throws error.
What is going wrong ? I have a valid https certificate on my server and also openssl is installed.
Anything else required ?
PHP 5.6+ updated the default ciphers based on the Mozilla cipher recommendations. There is more detail about what ciphers are used in the RFC for improving tls defaults. Overall this change removed support for Anonymous Diffie-Hellman and RC4, it's likely your server still uses RC4.
There are two options:
Update the ciphers your server is using based on the Mozilla ciper recommendations
Update the gallery3 code to use RC4, since it hasn't been updated since 2013 you can probably do this option without too much concern
For option 2 it looks like the call is done in gallery3/modules/gallery/helpers/MY_remote.php on line 73/74:
$handle = fsockopen(
$url_components['fsockhost'], $url_components['port'], $errno, $errstr, 5);
You can change this to use stream_socket_client which is compatible with fsockopen:
$context = stream_context_create(['ssl' => [
'ciphers' => 'RC4-MD5'
]]);
$timeout = ini_get('default_socket_timeout');
$handle = stream_socket_client('ssl://' . $url_components['fsockhost'] . ':' . $url_components['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context);
In PHP7.0, when using fsockopen to connect to open a https connection (hosted in same
host), using a host name like tls://foo.localhost, I am getting an error that
looks like
fsockopen(): Peer certificate CN="bar" did not match expected CN="foo.localhost"
Connection is not opened.
Same code worked in PHP 5.5 (that is less strict checking vertificates). I do
not really care about verifying certificates (this code runs in a unit test and
integration suite, connecting only to localhost). Tests need to be run against
http and https, though.
I know how to disable those checks when using file_get_contents.
How can I disable the peer certificate verification when using fsockopen?
fsockopen don't provide method to ignore peer certificate errors.
You have to use stream_socket_client with context. Available ssl/tls context options are available here.
Example of opening socket without peer verification
$context = stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
$hostname = "tls://foo.localhost:8123";
$socket = stream_socket_client($hostname, $errno, $errstr, ini_get("default_socket_timeout"), STREAM_CLIENT_CONNECT, $context);
I need to check with the php scripts to given website contains ssl certificate or not if it contains then what is the expire date of certificate.
Is there any way to get this information for any website using php scripts?
If yes, then plzzz let me know. I have tried so many times on google, but I did not find anything.
Thanks.
Yes, you should be able to get certificate information by using the stream_socket_client function built into PHP.
This particular method works nicely because it doesn't actually make an HTTP request so you don't have to do a fopen.
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));
$read = stream_socket_client("ssl://www.google.com:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get);
$cert = stream_context_get_params($read);
var_dump($cert["options"]["ssl"]["peer_certificate"]);
PHP: stream_context_create
PHP: stream_socket_client
PHP: stream_context_get_params
You'll then need to use OpenSSL to work the the certificate information at this point. You can read about OpenSSL in the PHP manual.