Passed
Pull Request — master (#1180)
by
unknown
63:56 queued 28:51
created

CryptKey::getKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * Cryptography key holder.
4
 *
5
 * @author      Julián Gutiérrez <[email protected]>
6
 * @copyright   Copyright (c) Alex Bilbie
7
 * @license     http://mit-license.org/
8
 *
9
 * @link        https://github.com/thephpleague/oauth2-server
10
 */
11
12
namespace League\OAuth2\Server;
13
14
use Lcobucci\JWT\Signer\Key;
15
use Lcobucci\JWT\Signer\Key\InMemory;
16
use Lcobucci\JWT\Signer\Key\LocalFileReference;
17
use LogicException;
18
19
class CryptKey
20
{
21
    /** @deprecated left for backward compatibility check */
22
    const RSA_KEY_PATTERN =
23
        '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s';
24
25
    private const FILE_PREFIX = 'file://';
26
27
    /**
28
     * @var Key
29
     */
30
    protected $key;
31
32
    /**
33
     * @var string
34
     */
35
    protected $keyPath;
36
37
    /**
38
     * @var null|string
39
     */
40 60
    protected $passPhrase;
41
42 60
    /**
43
     * @param string      $keyPath
44 60
     * @param null|string $passPhrase
45 55
     * @param bool        $keyPermissionsCheck
46 1
     */
47
    public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
48
    {
49 55
        $this->passPhrase = $passPhrase;
50
51
        if (\strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
52 55
            $contents = $keyPath;
0 ignored issues
show
Unused Code introduced by
The assignment to $contents is dead and can be removed.
Loading history...
53 55
            $this->key = InMemory::plainText($keyPath, $this->passPhrase ?? '');
54 55
            $this->keyPath = '';
55
            // There's no file, so no need for permission check.
56 5
            $keyPermissionsCheck = false;
57 5
        } elseif (\is_file($keyPath)) {
58 5
            if (\strpos($keyPath, self::FILE_PREFIX) !== 0) {
59
                $keyPath = self::FILE_PREFIX . $keyPath;
60
            }
61 60
62
            if (!\is_readable($keyPath)) {
63 60
                throw new LogicException(\sprintf('Key path "%s" does not exist or is not readable', $keyPath));
64 60
            }
65
            $contents = \file_get_contents($keyPath);
66
            $this->keyPath = $keyPath;
67
            $this->key = LocalFileReference::file($this->keyPath, $this->passPhrase ?? '');
68
            if (!$this->isValidKey($contents, $this->passPhrase ?? '')) {
69
                throw new LogicException('Unable to read key from file ' . $keyPath);
70
            }
71
        } else {
72
            throw new LogicException('Unable to read key from file ' . $keyPath);
73
        }
74
75
        if ($keyPermissionsCheck === true) {
76 60
            // Verify the permissions of the key
77 2
            $keyPathPerms = \decoct(\fileperms($this->keyPath) & 0777);
78
            if (\in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
79 58
                \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 5
        }
89
    }
90 5
91 5
    /**
92
     * Get key
93 5
     *
94
     * @return Key
95
     */
96
    public function getKey(): Key
97 5
    {
98
        return $this->key;
99
    }
100
101
    /**
102
     * Validate key contents.
103 5
     *
104
     * @param string $contents
105
     * @param string $passPhrase
106
     *
107
     * @return bool
108
     */
109 5
    private function isValidKey($contents, $passPhrase)
110
    {
111
        $pkey = \openssl_pkey_get_private($contents, $passPhrase) ?: \openssl_pkey_get_public($contents);
112
        if ($pkey === false) {
0 ignored issues
show
introduced by
The condition $pkey === false is always false.
Loading history...
113
            return false;
114
        }
115
        $details = \openssl_pkey_get_details($pkey);
116
117
        return $details !== false && \in_array(
118
            $details['type'] ?? -1,
119
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
120 60
            true
121
        );
122 60
    }
123 60
124 1
    /**
125
     * Retrieve key path.
126 59
     *
127
     * @return string
128 59
     */
129 59
    public function getKeyPath()
130 59
    {
131 59
        return $this->keyPath;
132
    }
133
134
    /**
135
     * Retrieve key pass phrase.
136
     *
137
     * @return null|string
138
     */
139
    public function getPassPhrase()
140 21
    {
141
        return $this->passPhrase;
142 21
    }
143
}
144