1
|
|
|
<?php |
2
|
|
|
namespace Jsq\Cache\EnvelopeEncryption; |
3
|
|
|
|
4
|
|
|
use Doctrine\Common\Cache\Cache; |
5
|
|
|
use InvalidArgumentException as IAE; |
6
|
|
|
use Jsq\Cache\EncryptedValue; |
7
|
|
|
use Jsq\Cache\EncryptingDecorator; |
8
|
|
|
|
9
|
|
|
class Decorator extends EncryptingDecorator |
10
|
|
|
{ |
11
|
|
|
/** @var resource */ |
12
|
|
|
private $publicKey; |
13
|
|
|
/** @var resource */ |
14
|
|
|
private $privateKey; |
15
|
|
|
/** @var string */ |
16
|
|
|
private $cipher; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @param Cache $decorated |
20
|
|
|
* @param mixed $cert |
21
|
|
|
* @param mixed $key |
22
|
|
|
* @param string|null $passphrase |
23
|
|
|
* @param string $cipher |
24
|
|
|
* |
25
|
|
|
* @throws IAE If OpenSSL keys cannot be extracted from $cert and $key. |
26
|
93 |
|
*/ |
27
|
|
|
public function __construct( |
28
|
|
|
Cache $decorated, |
29
|
|
|
$cert, |
30
|
|
|
$key, |
31
|
|
|
$passphrase = null, |
32
|
|
|
$cipher = 'aes-256-cbc' |
33
|
93 |
|
) { |
34
|
93 |
|
parent::__construct($decorated); |
35
|
93 |
|
$this->setPublicKey($cert); |
36
|
93 |
|
$this->setPrivateKey($key, $passphrase); |
37
|
93 |
|
$this->cipher = $cipher; |
38
|
|
|
} |
39
|
57 |
|
|
40
|
|
|
public function __destruct() |
41
|
57 |
|
{ |
42
|
57 |
|
openssl_free_key($this->publicKey); |
43
|
57 |
|
openssl_free_key($this->privateKey); |
44
|
|
|
} |
45
|
57 |
|
|
46
|
|
|
protected function isDataDecryptable($data, string $id): bool |
47
|
|
|
{ |
48
|
57 |
|
return $data instanceof Value |
49
|
18 |
|
&& $this->validateSignature( |
50
|
18 |
|
$id . $data->getCipherText(), |
51
|
57 |
|
$data->getSignature() |
52
|
|
|
); |
53
|
|
|
} |
54
|
36 |
|
|
55
|
|
|
protected function encrypt($data, string $id): EncryptedValue |
56
|
36 |
|
{ |
57
|
36 |
|
$key = $this->generateIv($this->cipher); |
58
|
36 |
|
$iv = $this->generateIv($this->cipher); |
59
|
|
|
$cipherText = $this->encipher(serialize($data), $this->cipher, $key, $iv); |
60
|
36 |
|
|
61
|
36 |
|
return new Value( |
62
|
36 |
|
$cipherText, |
63
|
36 |
|
$this->cipher, |
64
|
36 |
|
$iv, |
65
|
36 |
|
$this->encryptEnvelopeKey($key), |
66
|
36 |
|
$this->signString($id . $cipherText) |
67
|
|
|
); |
68
|
|
|
} |
69
|
18 |
|
|
70
|
|
View Code Duplication |
protected function decrypt($data) |
|
|
|
|
71
|
18 |
|
{ |
72
|
|
|
if (!$data instanceof Value) return false; |
73
|
18 |
|
|
74
|
18 |
|
return unserialize($this->decipher( |
75
|
18 |
|
$data->getCipherText(), |
76
|
18 |
|
$data->getMethod(), |
77
|
18 |
|
$this->decryptEnvelopeKey($data->getEnvelopeKey()), |
78
|
18 |
|
$data->getInitializationVector() |
79
|
|
|
)); |
80
|
|
|
} |
81
|
93 |
|
|
82
|
|
|
private function setPublicKey($cert) |
83
|
93 |
|
{ |
84
|
93 |
|
$this->publicKey = @openssl_pkey_get_public($cert); |
85
|
6 |
|
if (!$this->validateOpenSslKey($this->publicKey)) { |
86
|
|
|
throw new IAE('Unable to create public key from provided' |
87
|
6 |
|
. ' certificate. Certificate must be a valid x509 certificate,' |
88
|
6 |
|
. ' a PEM encoded certificate, or a path to a file containing a' |
89
|
|
|
. ' PEM encoded certificate.'); |
90
|
93 |
|
} |
91
|
|
|
} |
92
|
93 |
|
|
93
|
|
|
private function setPrivateKey($key, $passphrase) |
94
|
93 |
|
{ |
95
|
93 |
|
$this->privateKey = @openssl_pkey_get_private($key, $passphrase); |
96
|
3 |
|
if (!$this->validateOpenSslKey($this->privateKey)) { |
97
|
|
|
throw new IAE('Unable to create private key from provided key. Key' |
98
|
3 |
|
. ' must be a PEM encoded private key or a path to a file' |
99
|
|
|
. ' containing a PEM encoded private key.'); |
100
|
93 |
|
} |
101
|
|
|
} |
102
|
93 |
|
|
103
|
|
|
private function validateOpenSslKey($key) |
104
|
93 |
|
{ |
105
|
|
|
return is_resource($key) && 'OpenSSL key' === get_resource_type($key); |
106
|
|
|
} |
107
|
36 |
|
|
108
|
|
|
private function signString($string) |
109
|
36 |
|
{ |
110
|
|
|
openssl_sign($string, $signature, $this->privateKey); |
111
|
36 |
|
|
112
|
|
|
return $signature; |
113
|
|
|
} |
114
|
18 |
|
|
115
|
|
|
private function validateSignature($signed, $signature) |
116
|
18 |
|
{ |
117
|
|
|
return openssl_verify($signed, $signature, $this->publicKey); |
118
|
|
|
} |
119
|
36 |
|
|
120
|
|
|
private function encryptEnvelopeKey($key) |
121
|
36 |
|
{ |
122
|
|
|
openssl_public_encrypt($key, $sealedKey, $this->publicKey); |
123
|
36 |
|
|
124
|
|
|
return $sealedKey; |
125
|
|
|
} |
126
|
18 |
|
|
127
|
|
|
private function decryptEnvelopeKey($sealedKey) |
128
|
18 |
|
{ |
129
|
|
|
openssl_private_decrypt($sealedKey, $key, $this->privateKey); |
130
|
18 |
|
|
131
|
|
|
return $key; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.