Completed
Push — master ( f9df56...b2d053 )
by Arman
17s queued 16s
created

Cryptor::getAsymmetricInstance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.8.0
13
 */
14
15
namespace Quantum\Libraries\Encryption;
16
17
use Quantum\Exceptions\CryptorException;
18
use Quantum\Exceptions\AppException;
19
20
/**
21
 * Class Cryptor
22
 * @package Quantum\Libraries\Encryption
23
 */
24
class Cryptor
25
{
26
27
    /**
28
     * Asymmetric instance
29
     * @var Cryptor|null
30
     */
31
    private static $asymmetricInstance = null;
32
33
    /**
34
     * Symmetric instance
35
     * @var Cryptor|null
36
     */
37
    private static $symmetricInstance = null;
38
39
    /**
40
     * Asymmetric factor
41
     * @var boolean
42
     */
43
    private $asymmetric = false;
44
45
    /**
46
     * Application key
47
     * @var string
48
     */
49
    private $appKey;
50
51
    /**
52
     * Public and private keys
53
     * @var array
54
     */
55
    private $keys = [];
56
57
    /**
58
     * Digest algorithm
59
     * @var string
60
     */
61
    private $digestAlgorithm = 'SHA512';
62
63
    /**
64
     * Key type
65
     * @var integer
66
     */
67
    private $privateKeyType = OPENSSL_KEYTYPE_RSA;
68
69
    /**
70
     * Cipher method
71
     * @var string
72
     */
73
    private $cipherMethod = 'aes-256-cbc';
74
75
    /**
76
     * Key bites
77
     * @var integer
78
     */
79
    private $privateKeyBits = 1024;
80
81
    /**
82
     * The Initialization Vector
83
     * @var string
84
     */
85
    private $iv = null;
86
87
    /**
88
     * Cryptor constructor.
89
     * @param bool $asymmetric
90
     * @throws AppException
91
     * @throws CryptorException
92
     */
93
    private function __construct(bool $asymmetric = false)
94
    {
95
        if (!$asymmetric) {
96
            if (!env('APP_KEY')) {
97
                throw AppException::missingAppKey();
98
            }
99
            $this->appKey = env('APP_KEY');
100
        } else {
101
            $this->generateKeyPair();
102
        }
103
104
        $this->asymmetric = $asymmetric;
105
106
    }
107
108
    /**
109
     * Gets the cryptor instance
110
     * @param bool $asymmetric
111
     * @return Cryptor|null
112
     */
113
    public static function getInstance(bool $asymmetric = false): ?Cryptor
114
    {
115
        if ($asymmetric) {
116
            return self::getAsymmetricInstance();
117
        } else {
118
            return self::getSymmetricInstance();
119
        }
120
    }
121
122
    /**
123
     * Checks if the encryption mode is asymmetric
124
     * @return bool
125
     */
126
    public function isAsymmetric(): bool
127
    {
128
        return $this->asymmetric;
129
    }
130
131
    /**
132
     * Gets the Public Key
133
     * @return string
134
     * @throws CryptorException
135
     */
136
    public function getPublicKey(): string
137
    {
138
        if (!isset($this->keys['public'])) {
139
            throw CryptorException::noPublicKeyCreated();
140
        }
141
142
        return $this->keys['public'];
143
    }
144
145
    /**
146
     * Gets the Private Key
147
     * @return string
148
     * @throws CryptorException
149
     */
150
    public function getPrivateKey(): string
151
    {
152
        if (!isset($this->keys['private'])) {
153
            throw CryptorException::noPrivateKeyCreated();
154
        }
155
156
        return $this->keys['private'];
157
    }
158
159
    /**
160
     * Encrypts the plain text
161
     * @param string $plain
162
     * @param string|null $publicKey
163
     * @return string
164
     * @throws CryptorException
165
     */
166
    public function encrypt(string $plain, string $publicKey = null): string
167
    {
168
        if (!$this->isAsymmetric()) {
169
            $this->iv = $this->iv();
170
171
            $encrypted = openssl_encrypt($plain, $this->cipherMethod, $this->appKey, $options = 0, $this->iv);
172
173
            return base64_encode(base64_encode($encrypted) . '::' . base64_encode($this->iv));
174
        } else {
175
            if (!$publicKey) {
176
                throw CryptorException::publicKeyNotProvided();
177
            }
178
179
            openssl_public_encrypt($plain, $encrypted, $publicKey);
180
            return base64_encode($encrypted);
181
        }
182
    }
183
184
    /**
185
     * Decrypts the encrypted text
186
     * @param string $encrypted
187
     * @param string|null $privateKey
188
     * @return string
189
     * @throws CryptorException
190
     */
191
    public function decrypt(string $encrypted, string $privateKey = null): string
192
    {
193
        if (!$this->isAsymmetric()) {
194
            if (!valid_base64($encrypted)) {
195
                return $encrypted;
196
            }
197
198
            $data = explode('::', base64_decode($encrypted), 2);
199
200
            if (empty($data) || count($data) < 2) {
201
                throw CryptorException::invalidCipher();
202
            }
203
204
            $encrypted = base64_decode($data[0]);
205
            $iv = base64_decode($data[1]);
206
207
            return openssl_decrypt($encrypted, $this->cipherMethod, $this->appKey, $options = 0, $iv);
208
        } else {
209
            if (!$privateKey) {
210
                throw CryptorException::privateKeyNotProvided();
211
            }
212
213
            openssl_private_decrypt(base64_decode($encrypted), $decrypted, $privateKey);
214
            return $decrypted;
215
        }
216
    }
217
218
    /**
219
     * Gets the Initialization Vector used
220
     * @return string|null
221
     */
222
    public function getIV(): ?string
223
    {
224
        return $this->iv;
225
    }
226
227
    /**
228
     * Generates the Initialization Vector
229
     * @return string
230
     */
231
    private function iv(): string
232
    {
233
        $length = openssl_cipher_iv_length($this->cipherMethod);
234
        return openssl_random_pseudo_bytes($length);
235
    }
236
237
    /**
238
     * Generates Key Pair
239
     * @throws CryptorException
240
     */
241
    private function generateKeyPair()
242
    {
243
        $resource = openssl_pkey_new([
244
            "digest_alg" => $this->digestAlgorithm,
245
            "private_key_type" => $this->privateKeyType,
246
            "private_key_bits" => $this->privateKeyBits
247
        ]);
248
249
        if (!$resource) {
250
            throw CryptorException::configNotFound();
251
        }
252
253
        openssl_pkey_export($resource, $this->keys['private']);
254
        $this->keys['public'] = openssl_pkey_get_details($resource)['key'];
255
    }
256
257
    /**
258
     * Gets the symmetric cryptor insatnce
259
     * @return Cryptor|null
260
     */
261
    private static function getSymmetricInstance(): ?Cryptor
262
    {
263
        if (self::$symmetricInstance === null) {
264
            self::$symmetricInstance = new self(false);
265
        }
266
267
        return self::$symmetricInstance;
268
    }
269
270
    /**
271
     * Gets the asymmetric cryptor insatnce
272
     * @return Cryptor|null
273
     */
274
    private static function getAsymmetricInstance(): ?Cryptor
275
    {
276
        if (self::$asymmetricInstance === null) {
277
            self::$asymmetricInstance = new self(true);
278
        }
279
280
        return self::$asymmetricInstance;
281
    }
282
283
}
284