Completed
Push — master ( 1a59c2...c73113 )
by Florent
02:55
created

JWKFactory::createFromX5U()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 2
Metric Value
c 3
b 1
f 2
dl 0
loc 18
rs 9.4285
cc 3
eloc 12
nc 3
nop 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Factory;
13
14
use Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Jose\KeyConverter\KeyConverter;
17
use Jose\Object\JWK;
18
use Jose\Object\JWKSet;
19
use Mdanter\Ecc\Curves\CurveFactory;
20
use Mdanter\Ecc\Curves\NistCurve;
21
use Mdanter\Ecc\EccFactory;
22
23
final class JWKFactory
24
{
25
    /**
26
     * @param string $curve
27
     * @param array  $additional_values
28
     *
29
     * @return \Jose\Object\JWKInterface
30
     */
31
    public static function createRandomECPrivateKey($curve, array $additional_values = [])
32
    {
33
        $curve_name = self::getNistName($curve);
34
        $generator = CurveFactory::getGeneratorByName($curve_name);
35
        $private_key = $generator->createPrivateKey();
36
37
        $values = [
38
            'kty' => 'EC',
39
            'crv' => $curve,
40
            'x'   => self::encodeValue($private_key->getPublicKey()->getPoint()->getX()),
41
            'y'   => self::encodeValue($private_key->getPublicKey()->getPoint()->getY()),
42
            'd'   => self::encodeValue($private_key->getSecret()),
43
        ];
44
45
        $values = array_merge(
46
            $values,
47
            $additional_values
48
        );
49
50
        return new JWK($values);
51
    }
52
53
    /**
54
     * @param string $value
55
     *
56
     * @return string
57
     */
58
    private static function encodeValue($value)
59
    {
60
        return Base64Url::encode(self::convertDecToBin($value));
61
    }
62
63
    /**
64
     * @param string $value
65
     *
66
     * @return string
67
     */
68
    private static function convertDecToBin($value)
69
    {
70
        $adapter = EccFactory::getAdapter();
71
72
        return hex2bin($adapter->decHex($value));
73
    }
74
75
    /**
76
     * @param string $curve
77
     *
78
     * @throws \InvalidArgumentException
79
     *
80
     * @return string
81
     */
82
    private static function getNistName($curve)
83
    {
84
        switch ($curve) {
85
            case 'P-256':
86
                return NistCurve::NAME_P256;
87
            case 'P-384':
88
                return NistCurve::NAME_P384;
89
            case 'P-521':
90
                return NistCurve::NAME_P521;
91
            default:
92
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
93
        }
94
    }
95
96
    /**
97
     * @param array $values
98
     *
99
     * @return \Jose\Object\JWKInterface|\Jose\Object\JWKSetInterface
100
     */
101
    public static function createFromValues(array $values)
102
    {
103
        if (array_key_exists('keys', $values) && is_array($values['keys'])) {
104
            return new JWKSet($values);
105
        }
106
107
        return new JWK($values);
108
    }
109
110
    /**
111
     * @param string $file
112
     * @param array  $additional_values
113
     *
114
     * @return \Jose\Object\JWKInterface
115
     */
116
    public static function createFromCertificateFile($file, array $additional_values = [])
117
    {
118
        $values = KeyConverter::loadKeyFromCertificateFile($file);
119
        $values = array_merge($values, $additional_values);
120
121
        return new JWK($values);
122
    }
123
124
    /**
125
     * @param string $certificate
126
     * @param array  $additional_values
127
     *
128
     * @return \Jose\Object\JWKInterface
129
     */
130
    public static function createFromCertificate($certificate, array $additional_values = [])
131
    {
132
        $values = KeyConverter::loadKeyFromCertificate($certificate);
133
        $values = array_merge($values, $additional_values);
134
135
        return new JWK($values);
136
    }
137
138
    /**
139
     * @param resource $res
140
     * @param array    $additional_values
141
     *
142
     * @return \Jose\Object\JWKInterface
143
     */
144
    public static function createFromX509Resource($res, array $additional_values = [])
145
    {
146
        $values = KeyConverter::loadKeyFromX509Resource($res);
147
        $values = array_merge($values, $additional_values);
148
149
        return new JWK($values);
150
    }
151
152
    /**
153
     * @param string      $file
154
     * @param null|string $password
155
     * @param array       $additional_values
156
     *
157
     * @return \Jose\Object\JWKInterface
158
     */
159
    public static function createFromKeyFile($file, $password = null, array $additional_values = [])
160
    {
161
        $values = KeyConverter::loadFromKeyFile($file, $password);
162
        $values = array_merge($values, $additional_values);
163
164
        return new JWK($values);
165
    }
166
167
    /**
168
     * @param string      $key
169
     * @param null|string $password
170
     * @param array       $additional_values
171
     *
172
     * @return \Jose\Object\JWKInterface
173
     */
174
    public static function createFromKey($key, $password = null, array $additional_values = [])
175
    {
176
        $values = KeyConverter::loadFromKey($key, $password);
177
        $values = array_merge($values, $additional_values);
178
179
        return new JWK($values);
180
    }
181
182
    /**
183
     * @param string $jku
184
     * @param bool   $allow_unsecured_connection
185
     *
186
     * @return \Jose\Object\JWKSetInterface
187
     */
188
    public static function createFromJKU($jku, $allow_unsecured_connection = false)
189
    {
190
        $content = self::downloadContent($jku, $allow_unsecured_connection);
191
        $content = json_decode($content, true);
192
        Assertion::isArray($content, 'Invalid content.');
193
        Assertion::keyExists($content, 'keys', 'Invalid content.');
194
195
        return new JWKSet($content);
196
    }
197
198
    /**
199
     * @param string $x5u
200
     * @param bool   $allow_unsecured_connection
201
     *
202
     * @return \Jose\Object\JWKSetInterface
203
     */
204
    public static function createFromX5U($x5u, $allow_unsecured_connection = false)
205
    {
206
        $content = self::downloadContent($x5u, $allow_unsecured_connection);
207
        $content = json_decode($content, true);
208
        Assertion::isArray($content, 'Invalid content.');
209
210
        $jwkset = new JWKSet();
211
        foreach ($content as $kid => $cert) {
212
            $jwk = KeyConverter::loadKeyFromCertificate($cert);
213
            Assertion::notEmpty($jwk, 'Invalid content.');
214
            if (is_string($kid)) {
215
                $jwk['kid'] = $kid;
216
            }
217
            $jwkset = $jwkset->addKey(new JWK($jwk));
218
        }
219
220
        return $jwkset;
221
    }
222
223
    /**
224
     * @param array $x5c
225
     * @param array $additional_values
226
     *
227
     * @return \Jose\Object\JWKInterface
228
     */
229
    public static function createFromX5C(array $x5c, array $additional_values = [])
230
    {
231
        $values = KeyConverter::loadFromX5C($x5c);
232
        $values = array_merge($values, $additional_values);
233
234
        return new JWK($values);
235
    }
236
237
    /**
238
     * @param string $url
239
     * @param bool   $allow_unsecured_connection
240
     *
241
     * @throws \InvalidArgumentException
242
     *
243
     * @return string
244
     */
245
    private static function downloadContent($url, $allow_unsecured_connection)
246
    {
247
        // The URL must be a valid URL and scheme must be https
248
        Assertion::false(
249
            false === filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED),
250
            'Invalid URL.'
251
        );
252
        Assertion::false(
253
            false === $allow_unsecured_connection && 'https://' !==  mb_substr($url, 0, 8, '8bit'),
254
            'Unsecured connection.'
255
        );
256
257
        $params = [
258
            CURLOPT_RETURNTRANSFER => true,
259
            CURLOPT_URL            => $url,
260
        ];
261
        if (false === $allow_unsecured_connection) {
262
            $params[CURLOPT_SSL_VERIFYPEER] = true;
263
            $params[CURLOPT_SSL_VERIFYHOST] = 2;
264
        }
265
266
        $ch = curl_init();
267
        curl_setopt_array($ch, $params);
268
        $content = curl_exec($ch);
269
        curl_close($ch);
270
271
        Assertion::notEmpty($content, 'Unable to get content.');
272
273
        return $content;
274
    }
275
}
276