Completed
Branch develop (8ddc4a)
by Florent
02:46
created

JWKFactory::encodeValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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