Failed Conditions
Push — v7 ( d25f5c...477009 )
by Florent
02:07
created

RSAKey::toPEM()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 4
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Core\Util;
15
16
use Base64Url\Base64Url;
17
use Jose\Component\Core\JWK;
18
19
final class RSAKey
20
{
21
    /**
22
     * @var array
23
     */
24
    private $values = [];
25
26
    /**
27
     * @var BigInteger
28
     */
29
    private $modulus;
30
31
    /**
32
     * @var int
33
     */
34
    private $modulus_length;
35
36
    /**
37
     * @var BigInteger
38
     */
39
    private $public_exponent;
40
41
    /**
42
     * @var BigInteger|null
43
     */
44
    private $private_exponent = null;
45
46
    /**
47
     * @var BigInteger[]
48
     */
49
    private $primes = [];
50
51
    /**
52
     * @var BigInteger[]
53
     */
54
    private $exponents = [];
55
56
    /**
57
     * @var BigInteger|null
58
     */
59
    private $coefficient = null;
60
61
    /**
62
     * @param JWK $data
63
     */
64
    private function __construct(JWK $data)
65
    {
66
        $this->loadJWK($data->all());
67
        $this->populateBigIntegers();
68
    }
69
70
    /**
71
     * @param JWK $jwk
72
     *
73
     * @return RSAKey
74
     */
75
    public static function createFromJWK(JWK $jwk): RSAKey
76
    {
77
        return new self($jwk);
78
    }
79
80
    /**
81
     * @return BigInteger
82
     */
83
    public function getModulus(): BigInteger
84
    {
85
        return $this->modulus;
86
    }
87
88
    /**
89
     * @return int
90
     */
91
    public function getModulusLength(): int
92
    {
93
        return $this->modulus_length;
94
    }
95
96
    /**
97
     * @return BigInteger
98
     */
99
    public function getExponent(): BigInteger
100
    {
101
        $d = $this->getPrivateExponent();
102
        if (null !== $d) {
103
            return $d;
104
        }
105
106
        return $this->getPublicExponent();
107
    }
108
109
    /**
110
     * @return BigInteger
111
     */
112
    public function getPublicExponent(): BigInteger
113
    {
114
        return $this->public_exponent;
115
    }
116
117
    /**
118
     * @return BigInteger|null
119
     */
120
    public function getPrivateExponent(): ?BigInteger
121
    {
122
        return $this->private_exponent;
123
    }
124
125
    /**
126
     * @return BigInteger[]
127
     */
128
    public function getPrimes(): array
129
    {
130
        return $this->primes;
131
    }
132
133
    /**
134
     * @return BigInteger[]
135
     */
136
    public function getExponents(): array
137
    {
138
        return $this->exponents;
139
    }
140
141
    /**
142
     * @return BigInteger|null
143
     */
144
    public function getCoefficient(): ?BigInteger
145
    {
146
        return $this->coefficient;
147
    }
148
149
    /**
150
     * @return bool
151
     */
152
    public function isPublic(): bool
153
    {
154
        return !array_key_exists('d', $this->values);
155
    }
156
157
    /**
158
     * @param RSAKey $private
159
     *
160
     * @return RSAKey
161
     */
162
    public static function toPublic(RSAKey $private): RSAKey
163
    {
164
        $data = $private->toArray();
165
        $keys = ['p', 'd', 'q', 'dp', 'dq', 'qi'];
166
        foreach ($keys as $key) {
167
            if (array_key_exists($key, $data)) {
168
                unset($data[$key]);
169
            }
170
        }
171
172
        return new self(JWK::create($data));
173
    }
174
175
    /**
176
     * @return array
177
     */
178
    public function toArray(): array
179
    {
180
        return $this->values;
181
    }
182
183
    /**
184
     * @param array $jwk
185
     */
186
    private function loadJWK(array $jwk)
187
    {
188
        if (!array_key_exists('kty', $jwk)) {
189
            throw new \InvalidArgumentException('The key parameter "kty" is missing.');
190
        }
191
        if ('RSA' !== $jwk['kty']) {
192
            throw new \InvalidArgumentException('The JWK is not a RSA key.');
193
        }
194
195
        $this->values = $jwk;
196
    }
197
198
    private function populateBigIntegers()
199
    {
200
        $this->modulus = $this->convertBase64StringToBigInteger($this->values['n']);
201
        $this->modulus_length = mb_strlen($this->getModulus()->toBytes(), '8bit');
202
        $this->public_exponent = $this->convertBase64StringToBigInteger($this->values['e']);
203
204
        if (!$this->isPublic()) {
205
            $this->private_exponent = $this->convertBase64StringToBigInteger($this->values['d']);
206
207
            if (array_key_exists('p', $this->values) && array_key_exists('q', $this->values)) {
208
                $this->primes = [
209
                    $this->convertBase64StringToBigInteger($this->values['p']),
210
                    $this->convertBase64StringToBigInteger($this->values['q']),
211
                ];
212
                if (array_key_exists('dp', $this->values) && array_key_exists('dq', $this->values) && array_key_exists('qi', $this->values)) {
213
                    $this->exponents = [
214
                        $this->convertBase64StringToBigInteger($this->values['dp']),
215
                        $this->convertBase64StringToBigInteger($this->values['dq']),
216
                    ];
217
                    $this->coefficient = $this->convertBase64StringToBigInteger($this->values['qi']);
218
                }
219
            }
220
        }
221
    }
222
223
    /**
224
     * @param string $value
225
     *
226
     * @return BigInteger
227
     */
228
    private function convertBase64StringToBigInteger(string $value): BigInteger
229
    {
230
        return BigInteger::createFromBinaryString(Base64Url::decode($value));
231
    }
232
}
233