Failed Conditions
Push — v7 ( 514019...c6ee27 )
by Florent
02:57
created

ECKey::p256PublicKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
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
/**
20
 * Class ECKey.
21
 */
22
final class ECKey
23
{
24
    /**
25
     * @param JWK $jwk
26
     *
27
     * @return string
28
     */
29
    public static function convertToPEM(JWK $jwk): string
30
    {
31
        if ($jwk->has('d')) {
32
            return self::convertPrivateKeyToPEM($jwk);
33
        } else {
34
            return self::convertPublicKeyToPEM($jwk);
35
        }
36
    }
37
    /**
38
     * @param JWK $jwk
39
     *
40
     * @return string
41
     */
42
    public static function convertPublicKeyToPEM(JWK $jwk): string
43
    {
44
        switch ($jwk->get('crv')) {
45
            case 'P-256':
46
                $der = self::p256PublicKey();
47
                break;
48
            case 'P-384':
49
                $der = self::p384PublicKey();
50
                break;
51
            case 'P-521':
52
                $der = self::p521PublicKey();
53
                break;
54
            default:
55
                throw new \InvalidArgumentException('Unsupported curve.');
56
        }
57
        $der .= self::getKey($jwk);
58
        $pem  = '-----BEGIN PUBLIC KEY-----'.PHP_EOL;
59
        $pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
60
        $pem .= '-----END PUBLIC KEY-----'.PHP_EOL;
61
62
        return $pem;
63
    }
64
65
    /**
66
     * @param JWK $jwk
67
     *
68
     * @return string
69
     */
70
    public static function convertPrivateKeyToPEM(JWK $jwk): string
71
    {
72
        switch ($jwk->get('crv')) {
73
            case 'P-256':
74
                $der = self::p256PrivateKey($jwk);
75
                break;
76
            case 'P-384':
77
                $der = self::p384PrivateKey($jwk);
78
                break;
79
            case 'P-521':
80
                $der = self::p521PrivateKey($jwk);
81
                break;
82
            default:
83
                throw new \InvalidArgumentException('Unsupported curve.');
84
        }
85
        $der .= self::getKey($jwk);
86
        $pem  = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL;
87
        $pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
88
        $pem .= '-----END EC PRIVATE KEY-----'.PHP_EOL;
89
90
        return $pem;
91
    }
92
93
    /**
94
     * @return string
95
     */
96
    private static function p256PublicKey(): string
97
    {
98
        return pack('H*',
99
            '3059' // SEQUENCE, length 89
100
                .'3013' // SEQUENCE, length 19
101
                    .'0607' // OID, length 7
102
                        .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
103
                    .'0608' // OID, length 8
104
                        .'2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve
105
                .'0342' // BIT STRING, length 66
106
                    .'00' // prepend with NUL - pubkey will follow
107
        );
108
    }
109
110
    /**
111
     * @return string
112
     */
113
    private static function p384PublicKey(): string
114
    {
115
        return pack('H*',
116
            '3076' // SEQUENCE, length 118
117
                .'3010' // SEQUENCE, length 16
118
                    .'0607' // OID, length 7
119
                        .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
120
                    .'0605' // OID, length 5
121
                        .'2b81040022' // 1.3.132.0.34 = P-384 Curve
122
                .'0362' // BIT STRING, length 98
123
                    .'00' // prepend with NUL - pubkey will follow
124
        );
125
    }
126
127
    /**
128
     * @return string
129
     */
130
    private static function p521PublicKey(): string
131
    {
132
        return pack('H*',
133
            '30819b' // SEQUENCE, length 154
134
                .'3010' // SEQUENCE, length 16
135
                    .'0607' // OID, length 7
136
                        .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
137
                    .'0605' // OID, length 5
138
                        .'2b81040023' // 1.3.132.0.35 = P-521 Curve
139
                .'038186' // BIT STRING, length 134
140
                    .'00' // prepend with NUL - pubkey will follow
141
        );
142
    }
143
144
    /**
145
     * @param JWK $jwk
146
     *
147
     * @return string
148
     */
149
    private static function p256PrivateKey(JWK $jwk): string
150
    {
151
        $d = unpack('H*', Base64Url::decode($jwk->get('d')))[1];
152
        $dl = mb_strlen($d, '8bit') / 2;
153
        return pack('H*',
154
            '30'.dechex(87+$dl) // SEQUENCE, length 87+length($d)
155
                .'020101' // INTEGER, 1
156
                .'04'.dechex($dl)   // OCTET STRING, length($d)
157
                    .$d
158
                .'a00a' // TAGGED OBJECT #0, length 10
159
                    .'0608' // OID, length 8
160
                        .'2a8648ce3d030107' // 1.3.132.0.34 = P-384 Curve
161
                .'a144' //  TAGGED OBJECT #1, length 68
162
                    .'0342' // BIT STRING, length 66
163
                    .'00' // prepend with NUL - pubkey will follow
164
        );
165
    }
166
167
    /**
168
     * @param JWK $jwk
169
     *
170
     * @return string
171
     */
172
    private static function p384PrivateKey(JWK $jwk): string
173
    {
174
        $d = unpack('H*', Base64Url::decode($jwk->get('d')))[1];
175
        $dl = mb_strlen($d, '8bit') / 2;
176
        return pack('H*',
177
            '3081'.dechex(116+$dl) // SEQUENCE, length 116 + length($d)
178
                .'020101' // INTEGER, 1
179
                .'04'.dechex($dl)   // OCTET STRING, length($d)
180
                    .$d
181
                .'a007' // TAGGED OBJECT #0, length 7
182
                    .'0605' // OID, length 5
183
                        .'2b81040022' // 1.3.132.0.34 = P-384 Curve
184
                .'a164' //  TAGGED OBJECT #1, length 100
185
                    .'0362' // BIT STRING, length 98
186
                    .'00' // prepend with NUL - pubkey will follow
187
        );
188
    }
189
190
    /**
191
     * @param JWK $jwk
192
     *
193
     * @return string
194
     */
195
    private static function p521PrivateKey(JWK $jwk): string
196
    {
197
        $d = unpack('H*', Base64Url::decode($jwk->get('d')))[1];
198
        $dl = mb_strlen($d, '8bit') / 2;
199
        return pack('H*',
200
            '3081'.dechex(154+$dl) // SEQUENCE, length 154+length(d)
201
                .'020101' // INTEGER, 1
202
                .'04'.dechex($dl)   // OCTET STRING, length(d)
203
                    .$d
204
                .'a007' // TAGGED OBJECT #0, length 7
205
                    .'0605' // OID, length 5
206
                        .'2b81040023' // 1.3.132.0.35 = P-521 Curve
207
                .'a18189' //  TAGGED OBJECT #1, length 137
208
                    .'038186' // BIT STRING, length 134
209
                    .'00' // prepend with NUL - pubkey will follow
210
        );
211
    }
212
213
    /**
214
     * @param JWK $jwk
215
     *
216
     * @return string
217
     */
218
    private static function getKey(JWK $jwk): string
219
    {
220
        return
221
            pack('H*', '04')
222
            .Base64Url::decode($jwk->get('x'))
223
            .Base64Url::decode($jwk->get('y'));
224
    }
225
}
226