Skip to content

Commit e0dcc1a

Browse files
committed
SecureConnector: add optional TlsPeer, this...
...allows to capture your peer certificate and/or it's chain
1 parent 28fac70 commit e0dcc1a

File tree

4 files changed

+173
-1
lines changed

4 files changed

+173
-1
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,33 @@ $secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
13221322
));
13231323
```
13241324

1325+
In case you want to retrieve your peers certificate or certificate chain,
1326+
you can use the related context options:
1327+
1328+
```php
1329+
$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
1330+
'capture_peer_cert' => true,
1331+
'capture_peer_cert_chain' => true,
1332+
));
1333+
```
1334+
1335+
To show the peer certificate for every new connection this can be done as
1336+
follows:
1337+
1338+
```php
1339+
$secureConnector->connect('www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) {
1340+
assert($connection instanceof React\Socket\Connection);
1341+
if ($connection->hasTlsPeer()) {
1342+
$peer = $connection->getTlsPeer();
1343+
if ($peer && $peer->hasPeerCertificate()) {
1344+
$peerCert = $peer->getPeerCertificate();
1345+
openssl_x509_export($peerCert, $cert);
1346+
echo $cert;
1347+
}
1348+
}
1349+
});
1350+
```
1351+
13251352
By default, this connector supports TLSv1.0+ and excludes support for legacy
13261353
SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
13271354
want to negotiate with the remote side:

src/Connection.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class Connection extends EventEmitter implements ConnectionInterface
4141

4242
private $input;
4343

44+
/** @var TlsPeer|null */
45+
private $tlsPeer;
46+
4447
public function __construct($resource, LoopInterface $loop)
4548
{
4649
// PHP < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might
@@ -154,6 +157,31 @@ public function getLocalAddress()
154157
return $this->parseAddress(\stream_socket_get_name($this->stream, false));
155158
}
156159

160+
/**
161+
* @param TlsPeer $peer
162+
* @internal
163+
*/
164+
public function setTlsPeer(TlsPeer $peer = null)
165+
{
166+
$this->tlsPeer = $peer;
167+
}
168+
169+
/**
170+
* @return bool
171+
*/
172+
public function hasTlsPeer()
173+
{
174+
return $this->tlsPeer !== null;
175+
}
176+
177+
/**
178+
* @return TlsPeer|null
179+
*/
180+
public function getTlsPeer()
181+
{
182+
return $this->tlsPeer;
183+
}
184+
157185
private function parseAddress($address)
158186
{
159187
if ($address === false) {

src/SecureConnector.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ public function connect($uri)
5656
}
5757

5858
// try to enable encryption
59-
return $promise = $encryption->enable($connection)->then(null, function ($error) use ($connection, $uri) {
59+
return $promise = $encryption->enable($connection)->then(function () use ($connection) {
60+
$connection->setTlsPeer(
61+
TlsPeer::fromContextOptions(\stream_context_get_options($connection->stream))
62+
);
63+
64+
return $connection;
65+
}, function ($error) use ($connection, $uri) {
6066
// establishing encryption failed => close invalid connection and return error
6167
$connection->close();
6268

src/TlsPeer.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
namespace React\Socket;
4+
5+
use InvalidArgumentException;
6+
7+
class TlsPeer
8+
{
9+
/** @var resource of type OpenSSL X.509 */
10+
private $peerCertificate;
11+
12+
/** @var resource[] of type OpenSSL X.509 */
13+
private $peerCertificateChain;
14+
15+
public function __construct($peerCertificate = null, array $peerCertificateChain = null)
16+
{
17+
if ($peerCertificate !== null) {
18+
static::assertX509Resource($peerCertificate);
19+
$this->peerCertificate = $peerCertificate;
20+
}
21+
if ($peerCertificateChain !== null) {
22+
foreach ($peerCertificateChain as $resource) {
23+
static::assertX509Resource($resource);
24+
}
25+
$this->peerCertificateChain = $peerCertificateChain;
26+
}
27+
}
28+
29+
public static function fromContextOptions($options)
30+
{
31+
if (isset($options['ssl']['peer_certificate'])) {
32+
$peerCertificate = $options['ssl']['peer_certificate'];
33+
} else {
34+
$peerCertificate = null;
35+
}
36+
if (isset($options['ssl']['peer_certificate_chain'])) {
37+
$peerCertificateChain = $options['ssl']['peer_certificate_chain'];
38+
} else {
39+
$peerCertificateChain = null;
40+
}
41+
42+
return new static($peerCertificate, $peerCertificateChain);
43+
}
44+
45+
protected static function assertX509Resource($resource)
46+
{
47+
if (! \is_resource($resource)) {
48+
throw new \InvalidArgumentException(\sprintf(
49+
'Resource expected, got "%s"',
50+
\gettype($resource)
51+
));
52+
}
53+
if (\get_resource_type($resource) !== 'OpenSSL X.509') {
54+
throw new \InvalidArgumentException(\sprintf(
55+
'Resource of type "OpenSSL X.509" expected, got "%s"',
56+
\get_resource_type($resource)
57+
));
58+
}
59+
}
60+
61+
/**
62+
* @return bool
63+
*/
64+
public function hasPeerCertificate()
65+
{
66+
return $this->peerCertificate !== null;
67+
}
68+
69+
/**
70+
* @return null|resource (OpenSSL x509)
71+
*/
72+
public function getPeerCertificate()
73+
{
74+
return $this->peerCertificate;
75+
}
76+
77+
/**
78+
* @return bool
79+
*/
80+
public function hasPeerCertificateChain()
81+
{
82+
return $this->peerCertificateChain !== null;
83+
}
84+
85+
/**
86+
* @return null|array of OpenSSL x509 resources
87+
*/
88+
public function getPeerCertificateChain()
89+
{
90+
return $this->peerCertificateChain;
91+
}
92+
93+
protected function free()
94+
{
95+
if ($this->peerCertificate) {
96+
\openssl_x509_free($this->peerCertificate);
97+
$this->peerCertificate = null;
98+
}
99+
if (\is_array($this->peerCertificateChain)) {
100+
foreach ($this->peerCertificateChain as $cert) {
101+
\openssl_x509_free($cert);
102+
}
103+
$this->peerCertificateChain = null;
104+
}
105+
}
106+
107+
public function __destruct()
108+
{
109+
$this->free();
110+
}
111+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy