Completed
Push — master ( fe04e5...e565c7 )
by Florent
02:21
created

JWKFactory::createFromX5C()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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