Issues (58)

src/CryptKey.php (1 issue)

Severity
1
<?php
2
3
/**
4
 * Cryptography key holder.
5
 *
6
 * @author      Julián Gutiérrez <[email protected]>
7
 * @copyright   Copyright (c) Alex Bilbie
8
 * @license     http://mit-license.org/
9
 *
10
 * @link        https://github.com/thephpleague/oauth2-server
11
 */
12
13
declare(strict_types=1);
14
15
namespace League\OAuth2\Server;
16
17
use LogicException;
18
use OpenSSLAsymmetricKey;
19
20
use function decoct;
21
use function file_get_contents;
22
use function fileperms;
23
use function in_array;
24
use function is_file;
25
use function is_readable;
26
use function openssl_pkey_get_details;
27
use function openssl_pkey_get_private;
28
use function openssl_pkey_get_public;
29
use function sprintf;
30
use function trigger_error;
31
32
class CryptKey implements CryptKeyInterface
33
{
34
    private const FILE_PREFIX = 'file://';
35
36
    /**
37
     * @var string Key contents
38
     */
39
    protected string $keyContents;
40
41
    protected string $keyPath;
42
43 74
    public function __construct(string $keyPath, protected ?string $passPhrase = null, bool $keyPermissionsCheck = true)
44
    {
45 74
        if (str_starts_with($keyPath, self::FILE_PREFIX) === false && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
46 3
            $this->keyContents = $keyPath;
47 3
            $this->keyPath = '';
48
            // There's no file, so no need for permission check.
49 3
            $keyPermissionsCheck = false;
50 71
        } elseif (is_file($keyPath)) {
51 69
            if (str_starts_with($keyPath, self::FILE_PREFIX) === false) {
52 1
                $keyPath = self::FILE_PREFIX . $keyPath;
53
            }
54
55 69
            if (!is_readable($keyPath)) {
56
                throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
57
            }
58
59 69
            $keyContents = file_get_contents($keyPath);
60
61 69
            if ($keyContents === false) {
62
                throw new LogicException('Unable to read key from file ' . $keyPath);
63
            }
64
65 69
            $this->keyContents = $keyContents;
66 69
            $this->keyPath = $keyPath;
67
68 69
            if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) {
69
                throw new LogicException('Unable to read key from file ' . $keyPath);
70
            }
71
        } else {
72 2
            throw new LogicException('Invalid key supplied');
73
        }
74
75 72
        if ($keyPermissionsCheck === true && PHP_OS_FAMILY !== 'Windows') {
76
            // Verify the permissions of the key
77 69
            $keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
78 69
            if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
79
                trigger_error(
80
                    sprintf(
81
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
82
                        $this->keyPath,
83
                        $keyPathPerms
84
                    ),
85
                    E_USER_NOTICE
86
                );
87
            }
88
        }
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 19
    public function getKeyContents(): string
95
    {
96 19
        return $this->keyContents;
97
    }
98
99
    /**
100
     * Validate key contents.
101
     */
102 74
    private function isValidKey(string $contents, string $passPhrase): bool
103
    {
104 74
        $privateKey = openssl_pkey_get_private($contents, $passPhrase);
105
106 74
        $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
107
108 74
        if ($key === false) {
0 ignored issues
show
The condition $key === false is always false.
Loading history...
109 2
            return false;
110
        }
111
112 73
        $details = openssl_pkey_get_details($key);
113
114 73
        return $details !== false && in_array(
115 73
            $details['type'] ?? -1,
116 73
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
117 73
            true
118 73
        );
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 5
    public function getKeyPath(): string
125
    {
126 5
        return $this->keyPath;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 21
    public function getPassPhrase(): ?string
133
    {
134 21
        return $this->passPhrase;
135
    }
136
}
137