Proxy a SSL komunikace v PHP

Pokud potřebujete v PHP komunikovat pomocí ssl autentifikovaném certifikátem (formát PEM) a máte PHP s podporou openssl je velmi jednoduché řešení použít následující kód využívajíci funkci stream_socket_client():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$apns_settings = array(
        "host" => 'gateway.sandbox.push.apple.com',
        "port" => 2195,
        "certificate" => 'certificate/cert.pem',
);
$context_options = array(
        'ssl' => array(
                'local_cert' => $apns_settings["certificate"],
        ),
);
$stream_context = stream_context_create($context_options);
// connection to ssl server
$apns = stream_socket_client('ssl://'. $apns_settings["host"] . ":" . $apns_settings["port"], $error, $errorString, 2, STREAM_CLIENT_CONNECT, $stream_context);
// now you can use fwrite/fread from stream  
fwrite($apns, 'some message');
 
fclose($apns);

Situace se ale malinko komplikuje pokud se potřebujete připojit nejdříve k proxy serveru, který vám teprve umožní se připojit dál, ať už kvůli zabezpečení nebo z jiného důvodu.

Lépe než celý kód z příkladu výše přepisovat na komunikaci skrze nějaké jiné rozhraní co umí komunikovat skrz proxy, ať už cUrl (SSL by mělo jít, nezkoušel jsem) nebo další framework, je možné kód jen lehce upravit :) Nápad mám od kolegy z práce a popis řešení jsem našel v článku o tunelovaní ssl komunikace skrz proxy

Ve zkratce se jedná o toto:

  1. Spojení navázat s proxy namísto s cílovým serverem protokolem tcp:
  2. Poslat na proxy příkaz o spojení s cílovým serverem:
    “CONNECT server:port HTTP/1.0\r\n\r\n”
  3. Přijmout celou odpověď – při úspěchu proxy vrátí něco takového:
    “HTTP/1.0 200 Connection established\r\n\r\n”
  4. Přepnout komunikaci do cryptovaného ssl módu pomoci stream_socket_enable_crypto()
  5. Další komunikaci provádět jako předtím
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
/**
 * Example of use SSL communication through http proxy
 */
$apns_settings = array(
        "host" => 'gateway.sandbox.push.apple.com',
        "port" => 2195,
        "certificate" => 'certificate/cert.pem',
);
$context_options = array(
        'ssl' => array(
                'local_cert' => $apns_settings["certificate"],
        ),
);
$stream_context = stream_context_create($context_options);
// connection to your proxy server
$apns = stream_socket_client('tcp://192.168.1.126:8080', $error, $errorString, 2, STREAM_CLIENT_CONNECT, $stream_context);
// destination host and port must be accepted by proxy
$connect_via_proxy = "CONNECT ".$apns_settings["host"].":".$apns_settings["port"]." HTTP/1.0\r\n\r\n";
fwrite($apns,$connect_via_proxy,strlen($connect_via_proxy));
// read whole response and check successful "HTTP/1.0 200 Connection established"
if($response = fread($apns,1024)) {
        $parts = explode(' ',$response);
        if($parts[1] !== '200') { 
                die('Connection error: '.trim($response));
        } else {
                echo $response;
        }
} else {
        die('Timeout or other error');
}
// switch to SSL encrypted communication using local certificate from $context_options
stream_socket_enable_crypto($apns,true,STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
// now you can use fwrite/fread from stream  
fwrite($apns, 'some message');
 
fclose($apns);

Toť konec soboty strávené hledáním různých více či méně složitých (a vlastně nefunkčních) řešení :) myslím že by to mohlo někomu ušetřit hodiny hledání a pokusů/omylů.

Tagy: , , ,

Zanechte svůj komentář