├── src
└── Rsa
│ ├── Exceptions
│ ├── CouldNotDecryptData.php
│ ├── FileDoesNotExist.php
│ ├── InvalidPrivateKey.php
│ └── InvalidPublicKey.php
│ ├── KeyPair.php
│ ├── PublicKey.php
│ └── PrivateKey.php
├── CHANGELOG.md
├── psalm.xml.dist
├── LICENSE.md
├── .php-cs-fixer.dist.php
├── composer.json
└── README.md
/src/Rsa/Exceptions/CouldNotDecryptData.php:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Spatie bvba
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | in([
5 | __DIR__ . '/src',
6 | __DIR__ . '/tests',
7 | ])
8 | ->name('*.php')
9 | ->notName('*.blade.php')
10 | ->ignoreDotFiles(true)
11 | ->ignoreVCS(true);
12 |
13 | return (new PhpCsFixer\Config())
14 | ->setRules([
15 | '@PSR12' => true,
16 | 'array_syntax' => ['syntax' => 'short'],
17 | 'ordered_imports' => ['sort_algorithm' => 'alpha'],
18 | 'no_unused_imports' => true,
19 | 'not_operator_with_successor_space' => true,
20 | 'trailing_comma_in_multiline' => true,
21 | 'phpdoc_scalar' => true,
22 | 'unary_operator_spaces' => true,
23 | 'binary_operator_spaces' => true,
24 | 'blank_line_before_statement' => [
25 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
26 | ],
27 | 'phpdoc_single_line_var_spacing' => true,
28 | 'phpdoc_var_without_name' => true,
29 | 'class_attributes_separation' => [
30 | 'elements' => [
31 | 'method' => 'one',
32 | ],
33 | ],
34 | 'method_argument_space' => [
35 | 'on_multiline' => 'ensure_fully_multiline',
36 | 'keep_multiple_spaces_after_comma' => true,
37 | ],
38 | 'single_trait_insert_per_statement' => true,
39 | ])
40 | ->setFinder($finder);
41 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spatie/crypto",
3 | "description": "Encrypting and signing data using private/public keys",
4 | "keywords": [
5 | "spatie",
6 | "crypto"
7 | ],
8 | "homepage": "https://github.com/spatie/crypto",
9 | "license": "MIT",
10 | "authors": [
11 | {
12 | "name": "Freek Van der Herten",
13 | "email": "freek@spatie.be",
14 | "homepage": "https://spatie.be",
15 | "role": "Developer"
16 | }
17 | ],
18 | "require": {
19 | "php": "^7.4|^8.0",
20 | "ext-openssl": "*"
21 | },
22 | "require-dev": {
23 | "phpunit/phpunit": "^9.3",
24 | "symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0"
25 | },
26 | "autoload": {
27 | "psr-4": {
28 | "Spatie\\Crypto\\": "src"
29 | }
30 | },
31 | "autoload-dev": {
32 | "psr-4": {
33 | "Spatie\\Crypto\\Tests\\": "tests"
34 | }
35 | },
36 | "scripts": {
37 | "psalm": "vendor/bin/psalm",
38 | "test": "vendor/bin/phpunit",
39 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage",
40 | "format": "vendor/bin/php-cs-fixer fix --allow-risky=yes"
41 | },
42 | "config": {
43 | "sort-packages": true
44 | },
45 | "minimum-stability": "dev",
46 | "prefer-stable": true,
47 | "funding": [
48 | {
49 | "type": "github",
50 | "url": "https://github.com/sponsors/spatie"
51 | },
52 | {
53 | "type": "other",
54 | "url": "https://spatie.be/open-source/support-us"
55 | }
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/src/Rsa/KeyPair.php:
--------------------------------------------------------------------------------
1 | privateKeyType = $privateKeyType;
19 | $this->privateKeyBits = $privateKeyBits;
20 | $this->digestAlgorithm = (string)$digestAlgorithm;
21 | }
22 |
23 | public function password(string $password = null): self
24 | {
25 | $this->password = $password;
26 |
27 | return $this;
28 | }
29 |
30 | public function generate(
31 | string $privateKeyPath = '',
32 | string $publicKeyPath = ''
33 | ): array {
34 | /** @var \OpenSSLAsymmetricKey $asymmetricKey */
35 | $asymmetricKey = openssl_pkey_new([
36 | "digest_alg" => $this->digestAlgorithm,
37 | "private_key_bits" => $this->privateKeyBits,
38 | "private_key_type" => $this->privateKeyType,
39 | ]);
40 |
41 | openssl_pkey_export(
42 | $asymmetricKey,
43 | $privateKey,
44 | $this->password,
45 | );
46 |
47 | $rawPublicKey = openssl_pkey_get_details($asymmetricKey);
48 |
49 | $publicKey = $rawPublicKey['key'];
50 |
51 | if ($privateKeyPath !== '') {
52 | file_put_contents($privateKeyPath, $privateKey);
53 | }
54 |
55 | if ($publicKeyPath !== '') {
56 | file_put_contents($publicKeyPath, $publicKey);
57 | }
58 |
59 | return [$privateKey, $publicKey];
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Rsa/PublicKey.php:
--------------------------------------------------------------------------------
1 | publicKey = openssl_pkey_get_public($publicKeyString);
33 |
34 | if ($this->publicKey === false) {
35 | throw InvalidPublicKey::make();
36 | }
37 | }
38 |
39 | public function encrypt(string $data, int $padding = OPENSSL_PKCS1_OAEP_PADDING)
40 | {
41 | openssl_public_encrypt($data, $encrypted, $this->publicKey, $padding);
42 |
43 | return $encrypted;
44 | }
45 |
46 | public function canDecrypt(string $data): bool
47 | {
48 | try {
49 | $this->decrypt($data);
50 | } catch (CouldNotDecryptData $exception) {
51 | return false;
52 | }
53 |
54 | return true;
55 | }
56 |
57 | public function decrypt(string $data, int $padding = OPENSSL_PKCS1_PADDING): string
58 | {
59 | openssl_public_decrypt($data, $decrypted, $this->publicKey, $padding);
60 |
61 | if (is_null($decrypted)) {
62 | throw CouldNotDecryptData::make();
63 | }
64 |
65 | return $decrypted;
66 | }
67 |
68 | public function details(): array
69 | {
70 | return openssl_pkey_get_details($this->publicKey);
71 | }
72 |
73 | public function verify(string $data, string $signature, int $algorithm = OPENSSL_ALGO_SHA256): bool
74 | {
75 | return openssl_verify($data, base64_decode($signature), $this->publicKey, $algorithm);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Rsa/PrivateKey.php:
--------------------------------------------------------------------------------
1 | privateKey = $password ?
38 | openssl_pkey_get_private($privateKeyString, $password) :
39 | openssl_pkey_get_private($privateKeyString);
40 |
41 | if ($this->privateKey === false) {
42 | throw InvalidPrivateKey::make();
43 | }
44 | }
45 |
46 | public function encrypt(string $data, int $padding = OPENSSL_PKCS1_PADDING): string
47 | {
48 | openssl_private_encrypt($data, $decrypted, $this->privateKey, $padding);
49 |
50 | return $decrypted;
51 | }
52 |
53 | public function canDecrypt(string $data): bool
54 | {
55 | try {
56 | $this->decrypt($data);
57 | } catch (CouldNotDecryptData $exception) {
58 | return false;
59 | }
60 |
61 | return true;
62 | }
63 |
64 | public function decrypt(string $data, int $padding = OPENSSL_PKCS1_OAEP_PADDING): string
65 | {
66 | openssl_private_decrypt($data, $decrypted, $this->privateKey, $padding);
67 |
68 | if (is_null($decrypted)) {
69 | throw CouldNotDecryptData::make();
70 | }
71 |
72 | return $decrypted;
73 | }
74 |
75 | public function details(): array
76 | {
77 | return openssl_pkey_get_details($this->privateKey);
78 | }
79 |
80 | public function sign(string $data, int $algorithm = OPENSSL_ALGO_SHA256): string
81 | {
82 | openssl_sign($data, $signature, $this->privateKey, $algorithm);
83 |
84 | return base64_encode($signature);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Encrypting and signing data using private/public keys
2 |
3 | [](https://packagist.org/packages/spatie/crypto)
4 | 
5 | [](https://packagist.org/packages/spatie/crypto)
6 |
7 |
8 | This package allows you to easily generate a private/public key pairs, and encrypt/decrypt messages using those keys.
9 |
10 | ```php
11 | use Spatie\Crypto\Rsa\KeyPair;
12 | use Spatie\Crypto\Rsa\PrivateKey;
13 | use Spatie\Crypto\Rsa\PublicKey;
14 |
15 | // generating an RSA key pair
16 | [$privateKey, $publicKey] = (new KeyPair())->generate();
17 |
18 | // when passing paths, the generated keys will be written those paths
19 | (new KeyPair())->generate($pathToPrivateKey, $pathToPublicKey);
20 |
21 | $data = 'my secret data';
22 |
23 | $privateKey = PrivateKey::fromFile($pathToPrivateKey);
24 | $encryptedData = $privateKey->encrypt($data); // returns something unreadable
25 |
26 | $publicKey = PublicKey::fromFile($pathToPublicKey);
27 | $decryptedData = $publicKey->decrypt($encryptedData); // returns 'my secret data'
28 | ```
29 |
30 | Most functions in this package are wrappers around `openssl_*` functions to improve DX.
31 |
32 | ## Support us
33 |
34 | [
](https://spatie.be/github-ad-click/crypto)
35 |
36 | We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
37 |
38 | We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
39 |
40 | ## Installation
41 |
42 | You can install the package via composer:
43 |
44 | ```bash
45 | composer require spatie/crypto
46 | ```
47 |
48 | ## Usage
49 |
50 | You can generate a key pair using the `generate` function on the `KeyPair` class.
51 |
52 | ```php
53 | use Spatie\Crypto\Rsa\KeyPair;
54 |
55 | [$privateKey, $publicKey] = (new KeyPair())->generate();
56 | ```
57 |
58 | You can write the keys to disk, by passing paths to the `generate` function.
59 |
60 | ```php
61 | // when passing paths, the generate keys will to those paths
62 | (new KeyPair())->generate($pathToPrivateKey, $pathToPublicKey)
63 | ```
64 |
65 | You can protect the private key with a password by using the `password` method:
66 |
67 | ```php
68 | [$passwordProtectedPrivateKey, $publicKey] = (new KeyPair())->password('my-password')->generate();
69 | ```
70 |
71 | When using a password to generating a private key, you will need that password when instantiating the `PrivateKey` class.
72 |
73 | ### Loading keys
74 |
75 | To load a key from a file use the `fromFile` static method.
76 |
77 | ```php
78 | Spatie\Crypto\Rsa\PrivateKey::fromFile($pathToPrivateKey);
79 | Spatie\Crypto\Rsa\PublicKey::fromFile($pathToPublicKey);
80 | ```
81 |
82 | Alternatively, you can also create a key object using a string.
83 |
84 | ```php
85 | Spatie\Crypto\Rsa\PrivateKey::fromString($privateKeyString);
86 | Spatie\Crypto\Rsa\PublicKey::fromString($publicKeyString);
87 | ```
88 |
89 | If the private key is password protected, you need to pass the password as the second argument.
90 |
91 | ```php
92 | Spatie\Crypto\Rsa\PrivateKey::fromFile($pathToPrivateKey, $password);
93 | Spatie\Crypto\Rsa\PrivateKey::fromString($privateKeyString, $password);
94 | ```
95 |
96 | If you do not specify the right password, a `Spatie\Crypto\Exceptions\InvalidPrivateKey` exception will be thrown.
97 |
98 | ### Encrypting a message with a private key, decrypting with the public key
99 |
100 | Here's how you can encrypt data using the private key, and how to decrypt it using the public key.
101 |
102 | ```php
103 | $data = 'my secret data';
104 |
105 | $privateKey = Spatie\Crypto\Rsa\PrivateKey::fromFile($pathToPrivateKey);
106 | $encryptedData = $privateKey->encrypt($data); // encrypted data contains something unreadable
107 |
108 | $publicKey = Spatie\Crypto\Rsa\PublicKey::fromFile($pathToPublicKey);
109 | $decryptedData = $publicKey->decrypt($encryptedData); // decrypted data contains 'my secret data'
110 | ```
111 |
112 | If `decrypt` cannot decrypt the given data (maybe a non-matching private key was used to encrypt the data, or maybe tampered with the data), an exception of class `Spatie\Crypto\Exceptions\CouldNotDecryptData` will be thrown.
113 |
114 | ### Encrypting a message with a public key, decrypting with the private key
115 |
116 | Here's how you can encrypt data using the public key, and how to decrypt it using the private key.
117 |
118 | ```php
119 | $data = 'my secret data';
120 |
121 | $publicKey = Spatie\Crypto\Rsa\PublicKey::fromFile($pathToPublicKey);
122 | $encryptedData = $publicKey->encrypt($data); // encrypted data contains something unreadable
123 |
124 | $privateKey = Spatie\Crypto\Rsa\PrivateKey::fromFile($pathToPrivateKey);
125 | $decryptedData = $privateKey->decrypt($encryptedData); // decrypted data contains 'my secret data'
126 | ```
127 |
128 | If `decrypt` cannot decrypt the given data (maybe a non-matching public key was used to encrypt the data, or maybe tampered with the data), an exception of class `Spatie\Crypto\Exceptions\CouldNotDecryptData` will be thrown.
129 |
130 | ### Determining if the data can be decrypted
131 |
132 | Both the `PublicKey` and `PrivateKey` class have a `canDecrypt` method to determine if given data can be decrypted.
133 |
134 | ```php
135 | Spatie\Crypto\Rsa\PrivateKey::fromFile($pathToPrivateKey)->canDecrypt($data); // returns a boolean;
136 | Spatie\Crypto\Rsa\PublicKey::fromFile($pathToPublicKey)->canDecrypt($data); // returns a boolean;
137 | ```
138 |
139 | ### Signing and verifying data
140 |
141 | The `PrivateKey` class has a method `sign` to generate a signature for the given data. The `verify` method on the `PublicKey` class can be used to verify if a signature is valid for the given data.
142 |
143 | If `verify` returns `true`, you know for certain that the holder of the private key signed the message, and that it was not tampered with.
144 |
145 | ```php
146 | $signature = Spatie\Crypto\Rsa\PrivateKey::fromFile($pathToPrivateKey)->sign('my message'); // returns a string
147 |
148 | $publicKey = Spatie\Crypto\Rsa\PublicKey::fromFile($pathToPublicKey);
149 |
150 | $publicKey->verify('my message', $signature) // returns true;
151 | $publicKey->verify('my modified message', $signature) // returns false;
152 | ```
153 |
154 | ## Alternatives
155 |
156 | This package aims to be very lightweight and easy to use. If you need more features, consider using of one these alternatives:
157 |
158 | - [paragonie/halite](https://github.com/paragonie/halite)
159 | - [vlucas/pikirasa](https://github.com/vlucas/pikirasa)
160 | - [laminas/crypt](https://docs.laminas.dev/laminas-crypt/)
161 | - [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib)
162 |
163 | ## A word on the usage of RSA
164 |
165 | At the time of writing, RSA is secure enough for the use case we've built this package for.
166 |
167 | To know more about why RSA might not be good enough for you, read [this post on public-key encryption at Paragonie.com](https://paragonie.com/blog/2016/12/everything-you-know-about-public-key-encryption-in-php-is-wrong#php-openssl-rsa-bad-default)
168 |
169 | ## Testing
170 |
171 | ``` bash
172 | composer test
173 | ```
174 |
175 | ## Changelog
176 |
177 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
178 |
179 | ## Contributing
180 |
181 | Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
182 |
183 | ## Security Vulnerabilities
184 |
185 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
186 |
187 | ## Credits
188 |
189 | - [Freek Van der Herten](https://github.com/freekmurze)
190 | - [All Contributors](../../contributors)
191 |
192 | ## License
193 |
194 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
195 |
--------------------------------------------------------------------------------