AsymmetricEncryptionHelper::sign()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 3
1
<?php
2
3
namespace Smoren\EncryptionTools\Helpers;
4
5
use Smoren\EncryptionTools\Exceptions\AsymmetricEncryptionException;
6
use Smoren\EncryptionTools\Exceptions\JsonException;
7
8
/**
9
 * Class AsymmetricEncryptionHelper
10
 * @author Smoren <[email protected]>
11
 */
12
class AsymmetricEncryptionHelper
13
{
14
    /**
15
     * Generates RSA key pair
16
     * @return string[] [$privateKey, $publicKey]
17
     * @throws AsymmetricEncryptionException
18
     */
19
    public static function generateKeyPair(): array
20
    {
21
        $keyPair = openssl_pkey_new();
22
        if(!$keyPair) {
23
            throw new AsymmetricEncryptionException(
24
                'openssl_pkey_new() returned false',
25
                AsymmetricEncryptionException::OPENSSL_ERROR
26
            );
27
        }
28
29
        openssl_pkey_export($keyPair, $privateKey);
30
        $details = openssl_pkey_get_details($keyPair);
31
        if(!$details) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $details of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
32
            throw new AsymmetricEncryptionException(
33
                'openssl_pkey_get_details() returned false',
34
                AsymmetricEncryptionException::OPENSSL_ERROR
35
            );
36
        }
37
38
        $publicKey = $details["key"];
39
40
        return [$privateKey, $publicKey];
41
    }
42
43
    /**
44
     * Returns data encrypted by public key
45
     * @param mixed $data data to encrypt
46
     * @param string $publicKey public key
47
     * @return string encrypted data
48
     * @throws AsymmetricEncryptionException
49
     * @throws JsonException
50
     */
51
    public static function encryptByPublicKey($data, string $publicKey): string
52
    {
53
        static::validatePublicKey($publicKey);
54
        if(!openssl_public_encrypt(JsonHelper::encode($data), $dataEncrypted, $publicKey)) {
55
            throw new AsymmetricEncryptionException(
56
                'openssl_public_encrypt() returned false',
57
                AsymmetricEncryptionException::CANNOT_ENCRYPT
58
            );
59
        }
60
        return base64_encode($dataEncrypted);
61
    }
62
63
    /**
64
     * Returns data encrypted by private key
65
     * @param mixed $data data to encrypt
66
     * @param string $privateKey public key
67
     * @return string encrypted data
68
     * @throws AsymmetricEncryptionException
69
     * @throws JsonException
70
     */
71
    public static function encryptByPrivateKey($data, string $privateKey): string
72
    {
73
        static::validatePrivateKey($privateKey);
74
        if(!openssl_private_encrypt(JsonHelper::encode($data), $dataEncrypted, $privateKey)) {
75
            throw new AsymmetricEncryptionException(
76
                'openssl_private_encrypt() returned false',
77
                AsymmetricEncryptionException::CANNOT_ENCRYPT
78
            );
79
        }
80
        return base64_encode($dataEncrypted);
81
    }
82
83
    /**
84
     * Returns data decrypted by public key
85
     * @param string $dataEncrypted data to decrypt
86
     * @param string $publicKey public key
87
     * @return mixed decrypted data
88
     * @throws AsymmetricEncryptionException
89
     */
90
    public static function decryptByPublicKey(string $dataEncrypted, string $publicKey)
91
    {
92
        static::validatePublicKey($publicKey);
93
        openssl_public_decrypt(base64_decode($dataEncrypted), $dataDecrypted, $publicKey);
94
95
        if($dataDecrypted === null) {
96
            throw new AsymmetricEncryptionException(
97
                'cannot decrypt by public key',
98
                AsymmetricEncryptionException::CANNOT_DECRYPT
99
            );
100
        }
101
102
        return json_decode($dataDecrypted, true);
103
    }
104
105
    /**
106
     * Returns data decrypted by private key
107
     * @param string $dataEncrypted data to decrypt
108
     * @param string $privateKey private key
109
     * @return mixed decrypted data
110
     * @throws AsymmetricEncryptionException
111
     */
112
    public static function decryptByPrivateKey(string $dataEncrypted, string $privateKey)
113
    {
114
        static::validatePrivateKey($privateKey);
115
        openssl_private_decrypt(base64_decode($dataEncrypted), $dataDecrypted, $privateKey);
116
117
        if($dataDecrypted === null) {
118
            throw new AsymmetricEncryptionException(
119
                'cannot decrypt by private key',
120
                AsymmetricEncryptionException::CANNOT_DECRYPT
121
            );
122
        }
123
124
        return json_decode($dataDecrypted, true);
125
    }
126
127
    /**
128
     * Returns signature created for data with private key
129
     * @param mixed $data data to sign
130
     * @param string $privateKey private key
131
     * @param int $algorithm openssl algorithm
132
     * @return string signature
133
     * @throws AsymmetricEncryptionException
134
     * @throws JsonException
135
     */
136
    public static function sign($data, string $privateKey, int $algorithm = OPENSSL_ALGO_SHA256): string
137
    {
138
        static::validatePrivateKey($privateKey);
139
        openssl_sign(JsonHelper::encode($data), $signature, $privateKey, $algorithm);
140
        return $signature;
141
    }
142
143
    /**
144
     * Verifies the signature
145
     * @param mixed $data data to verify signature for
146
     * @param string $signature signature to verify
147
     * @param string $publicKey public key to verfy signature with
148
     * @param int $algorithm openssl algorithm
149
     * @throws AsymmetricEncryptionException if verification failure
150
     * @throws JsonException
151
     */
152
    public static function verify(
153
        $data,
154
        string $signature,
155
        string $publicKey,
156
        int $algorithm = OPENSSL_ALGO_SHA256
157
    ): void {
158
        static::validatePublicKey($publicKey);
159
        if(!openssl_verify(JsonHelper::encode($data), $signature, $publicKey, $algorithm)) {
160
            throw new AsymmetricEncryptionException('wrong signature', AsymmetricEncryptionException::CANNOT_VERIFY);
161
        }
162
    }
163
164
    /**
165
     * Validates public key
166
     * @param string $publicKey public key to validate
167
     * @throws AsymmetricEncryptionException if key is invalid
168
     */
169
    public static function validatePublicKey(string $publicKey): void
170
    {
171
        static::validateKey($publicKey, 'PUBLIC');
172
    }
173
174
    /**
175
     * Validates private key
176
     * @param string $privateKey private key to validate
177
     * @throws AsymmetricEncryptionException if key is invalid
178
     */
179
    public static function validatePrivateKey(string $privateKey): void
180
    {
181
        static::validateKey($privateKey, 'PRIVATE');
182
    }
183
184
    /**
185
     * Validates key
186
     * @param string $key key to validate
187
     * @param string $keyType key type (PUBLIC or PRIVATE)
188
     * @throws AsymmetricEncryptionException if key is invalid
189
     */
190
    protected static function validateKey(string $key, string $keyType): void
191
    {
192
        $arPublicKey = explode("\n", $key);
193
        $beginString = array_shift($arPublicKey);
194
        $endLineBreak = array_pop($arPublicKey);
195
        $endString = array_pop($arPublicKey);
196
        $lastPart = array_pop($arPublicKey);
197
198
        $isCorrect = true;
199
200
        if(
201
            $endLineBreak !== "" ||
202
            $beginString !== "-----BEGIN {$keyType} KEY-----" ||
203
            $endString !== "-----END {$keyType} KEY-----" ||
204
            !preg_match('/^.{1,63}$/', $lastPart ?? '')
205
        ) {
206
            $isCorrect = false;
207
        } else {
208
            foreach($arPublicKey as $part) {
209
                if(!preg_match('/^.{64}$/', $part)) {
210
                    $isCorrect = false;
211
                    break;
212
                }
213
            }
214
        }
215
216
        if(!$isCorrect) {
217
            throw new AsymmetricEncryptionException(
218
                'invalid key format',
219
                AsymmetricEncryptionException::INVALID_KEY_FORMAT
220
            );
221
        }
222
    }
223
}
224