Completed
Push — master ( 8e9c64...20353b )
by Florent
04:07
created

KeyFactory::downloadContent()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
rs 8.439
cc 6
eloc 18
nc 6
nop 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2015 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 SpomkyLabs\JoseBundle\Factory;
13
14
15
use Jose\KeyConverter\KeyConverter;
16
use Jose\Object\JWK;
17
use Jose\Object\JWKSet;
18
19
final class KeyFactory
20
{
21
    /**
22
     * @param array  $values
23
     *
24
     * @return \Jose\Object\JWKInterface|\Jose\Object\JWKSetInterface
25
     */
26
    public function createFromValues(array $values)
27
    {
28
        if (array_key_exists('keys', $values)) {
29
            return new JWKSet($values);
30
        }
31
        return new JWK($values);
32
    }
33
34
    /**
35
     * @param string $filename
36
     * @param array  $additional_values
37
     *
38
     * @return \Jose\Object\JWKInterface
39
     */
40
    public function createFromCertificateFile($filename, array $additional_values = [])
41
    {
42
        $values = KeyConverter::loadKeyFromCertificateFile($filename);
43
        $values = array_merge($values, $additional_values);
44
45
        return new JWK($values);
46
    }
47
48
    /**
49
     * @param string $content
50
     * @param array  $additional_values
51
     *
52
     * @return \Jose\Object\JWKInterface
53
     */
54
    public function createFromCertificate($content, array $additional_values = [])
55
    {
56
        $values = KeyConverter::loadKeyFromCertificate($content);
57
        $values = array_merge($values, $additional_values);
58
59
        return new JWK($values);
60
    }
61
62
    /**
63
     * @param string $filename
64
     * @param bool   $is_der
65
     * @param string $password
66
     * @param array  $additional_values
67
     *
68
     * @return \Jose\Object\JWKInterface
69
     */
70
    public function createFromKeyFile($filename, $is_der, $password = null, array $additional_values = [])
71
    {
72
        $values = KeyConverter::loadKeyFromFile($filename, $password, $is_der);
73
        $values = array_merge($values, $additional_values);
74
75
        return new JWK($values);
76
    }
77
78
    /**
79
     * @param string $jku
80
     * @param bool   $allow_unsecured_connection
81
     *
82
     * @return \Jose\Object\JWKSet
83
     */
84
    public function createFromJKU($jku, $allow_unsecured_connection = false)
85
    {
86
        $content = $this->downloadContent($jku, $allow_unsecured_connection);
87
        $content = json_decode($content, true);
88
        if (!is_array($content) || !array_key_exists('keys', $content)) {
89
            throw new \InvalidArgumentException('Invalid content.');
90
        }
91
92
        return new JWKSet($content);
93
    }
94
95
    /**
96
     * @param string $x5u
97
     * @param bool   $allow_unsecured_connection
98
     *
99
     * @return \Jose\Object\JWKSetInterface
100
     */
101
    public function createFromX5U($x5u, $allow_unsecured_connection = false)
102
    {
103
        $content = $this->downloadContent($x5u, $allow_unsecured_connection);
104
        $content = json_decode($content, true);
105
        if (!is_array($content)) {
106
            throw new \InvalidArgumentException('Invalid content.');
107
        }
108
109
        $jwkset = new JWKSet();
110
        foreach ($content as $kid => $cert) {
111
            $jwk = KeyConverter::loadKeyFromCertificate($cert);
112
            if (empty($jwk)) {
113
                throw new \InvalidArgumentException('Invalid content.');
114
            }
115
            if (is_string($kid)) {
116
                $jwk['kid'] = $kid;
117
            }
118
            $jwkset->addKey(new JWK($jwk));
119
        }
120
121
        return $jwkset;
122
    }
123
124
    /**
125
     * @param array $x5c
126
     *
127
     * @return \Jose\Object\JWKInterface
128
     */
129
    public function createFromX5C(array $x5c)
130
    {
131
        $certificate = null;
132
        $last_issuer = null;
133
        $last_subject = null;
134
        foreach ($x5c as $cert) {
135
            $current_cert = "-----BEGIN CERTIFICATE-----\n$cert\n-----END CERTIFICATE-----";
136
            $x509 = openssl_x509_read($current_cert);
137
            if (false === $x509) {
138
                $last_issuer = null;
139
                $last_subject = null;
140
                break;
141
            }
142
            $parsed = openssl_x509_parse($x509);
143
            openssl_x509_free($x509);
144
            if (false === $parsed) {
145
                $last_issuer = null;
146
                $last_subject = null;
147
                break;
148
            }
149
            if (null === $last_subject) {
150
                $last_subject = $parsed['subject'];
151
                $last_issuer = $parsed['issuer'];
152
                $certificate = $current_cert;
153
            } else {
154
                if (json_encode($last_issuer) === json_encode($parsed['subject'])) {
155
                    $last_subject = $parsed['subject'];
156
                    $last_issuer = $parsed['issuer'];
157
                } else {
158
                    $last_issuer = null;
159
                    $last_subject = null;
160
                    break;
161
                }
162
            }
163
        }
164
        if (null === $last_issuer || json_encode($last_issuer) !== json_encode($last_subject)) {
165
            throw new \InvalidArgumentException('Invalid certificate chain.');
166
        }
167
        return $this->createFromCertificate($certificate);
168
    }
169
170
    /**
171
     * @param string $url
172
     * @param bool   $allow_unsecured_connection
173
     *
174
     * @throws \InvalidArgumentException
175
     *
176
     * @return string
177
     */
178
    private function downloadContent($url, $allow_unsecured_connection)
179
    {
180
        // The URL must be a valid URL and scheme must be https
181
        if (false === filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
182
            throw new \InvalidArgumentException('Invalid URL.');
183
        }
184
        if (false === $allow_unsecured_connection && 'https://' !== substr($url, 0, 8)) {
185
            throw new \InvalidArgumentException('Unsecured connection.');
186
        }
187
188
        $params = [
189
            CURLOPT_RETURNTRANSFER => true,
190
            CURLOPT_URL            => $url,
191
        ];
192
        if (true === $allow_unsecured_connection) {
193
            $params[CURLOPT_SSL_VERIFYPEER] = true;
194
            $params[CURLOPT_SSL_VERIFYHOST] = 2;
195
        }
196
197
        $ch = curl_init();
198
        curl_setopt_array($ch, $params);
199
        $content = curl_exec($ch);
200
        curl_close($ch);
201
202
        if (empty($content)) {
203
            throw new \InvalidArgumentException('Unable to get content.');
204
        }
205
206
        return $content;
207
    }
208
}
209