.htaccess
1 year ago
Common.php
1 year ago
JWK.php
1 year ago
MontgomeryPrivate.php
1 year ago
MontgomeryPublic.php
1 year ago
OpenSSH.php
1 year ago
PKCS1.php
1 year ago
PKCS8.php
1 year ago
PuTTY.php
1 year ago
XML.php
1 year ago
index.html
1 year ago
libsodium.php
1 year ago
web.config
1 year ago
PKCS8.php
227 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * PKCS#8 Formatted EC Key Handler |
| 5 | * |
| 6 | * PHP version 5 |
| 7 | * |
| 8 | * Processes keys with the following headers: |
| 9 | * |
| 10 | * -----BEGIN ENCRYPTED PRIVATE KEY----- |
| 11 | * -----BEGIN PRIVATE KEY----- |
| 12 | * -----BEGIN PUBLIC KEY----- |
| 13 | * |
| 14 | * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 |
| 15 | * is specific to private keys it's basically creating a DER-encoded wrapper |
| 16 | * for keys. This just extends that same concept to public keys (much like ssh-keygen) |
| 17 | * |
| 18 | * @author Jim Wigginton <terrafrost@php.net> |
| 19 | * @copyright 2015 Jim Wigginton |
| 20 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
| 21 | * @link http://phpseclib.sourceforge.net |
| 22 | */ |
| 23 | |
| 24 | declare(strict_types=1); |
| 25 | |
| 26 | namespace phpseclib3\Crypt\EC\Formats\Keys; |
| 27 | |
| 28 | use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; |
| 29 | use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; |
| 30 | use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; |
| 31 | use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; |
| 32 | use phpseclib3\Crypt\EC\Curves\Ed25519; |
| 33 | use phpseclib3\Crypt\EC\Curves\Ed448; |
| 34 | use phpseclib3\Exception\RuntimeException; |
| 35 | use phpseclib3\Exception\UnsupportedCurveException; |
| 36 | use phpseclib3\File\ASN1; |
| 37 | use phpseclib3\File\ASN1\Maps; |
| 38 | use phpseclib3\Math\BigInteger; |
| 39 | use phpseclib3\Math\Common\FiniteField\Integer; |
| 40 | |
| 41 | /** |
| 42 | * PKCS#8 Formatted EC Key Handler |
| 43 | * |
| 44 | * @author Jim Wigginton <terrafrost@php.net> |
| 45 | */ |
| 46 | abstract class PKCS8 extends Progenitor |
| 47 | { |
| 48 | use Common; |
| 49 | |
| 50 | /** |
| 51 | * OID Name |
| 52 | * |
| 53 | * @var array |
| 54 | */ |
| 55 | public const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448']; |
| 56 | |
| 57 | /** |
| 58 | * OID Value |
| 59 | * |
| 60 | * @var string |
| 61 | */ |
| 62 | public const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113']; |
| 63 | |
| 64 | /** |
| 65 | * Break a public or private key down into its constituent components |
| 66 | * |
| 67 | * @param string|array $key |
| 68 | */ |
| 69 | public static function load($key, ?string $password = null): array |
| 70 | { |
| 71 | // initialize_static_variables() is defined in both the trait and the parent class |
| 72 | // when it's defined in two places it's the traits one that's called |
| 73 | // the parent one is needed, as well, but the parent one is called by other methods |
| 74 | // in the parent class as needed and in the context of the parent it's the parent |
| 75 | // one that's called |
| 76 | self::initialize_static_variables(); |
| 77 | |
| 78 | $key = parent::load($key, $password); |
| 79 | |
| 80 | $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; |
| 81 | |
| 82 | switch ($key[$type . 'Algorithm']['algorithm']) { |
| 83 | case 'id-Ed25519': |
| 84 | case 'id-Ed448': |
| 85 | return self::loadEdDSA($key); |
| 86 | } |
| 87 | |
| 88 | $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); |
| 89 | if (!$decoded) { |
| 90 | throw new RuntimeException('Unable to decode BER'); |
| 91 | } |
| 92 | $params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP); |
| 93 | if (!$params) { |
| 94 | throw new RuntimeException('Unable to decode the parameters using Maps\ECParameters'); |
| 95 | } |
| 96 | |
| 97 | $components = []; |
| 98 | $components['curve'] = self::loadCurveByParam($params); |
| 99 | |
| 100 | if ($type == 'publicKey') { |
| 101 | $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']); |
| 102 | |
| 103 | return $components; |
| 104 | } |
| 105 | |
| 106 | $decoded = ASN1::decodeBER($key['privateKey']); |
| 107 | if (!$decoded) { |
| 108 | throw new RuntimeException('Unable to decode BER'); |
| 109 | } |
| 110 | $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP); |
| 111 | if (isset($key['parameters']) && $params != $key['parameters']) { |
| 112 | throw new RuntimeException('The PKCS8 parameter field does not match the private key parameter field'); |
| 113 | } |
| 114 | |
| 115 | $components['dA'] = new BigInteger($key['privateKey'], 256); |
| 116 | $components['curve']->rangeCheck($components['dA']); |
| 117 | $components['QA'] = isset($key['publicKey']) ? |
| 118 | self::extractPoint($key['publicKey'], $components['curve']) : |
| 119 | $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); |
| 120 | |
| 121 | return $components; |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Break a public or private EdDSA key down into its constituent components |
| 126 | */ |
| 127 | private static function loadEdDSA(array $key): array |
| 128 | { |
| 129 | $components = []; |
| 130 | |
| 131 | if (isset($key['privateKey'])) { |
| 132 | $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); |
| 133 | |
| 134 | // 0x04 == octet string |
| 135 | // 0x20 == length (32 bytes) |
| 136 | if (substr($key['privateKey'], 0, 2) != "\x04\x20") { |
| 137 | throw new RuntimeException('The first two bytes of the private key field should be 0x0420'); |
| 138 | } |
| 139 | $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2)); |
| 140 | $components['dA'] = $arr['dA']; |
| 141 | $components['secret'] = $arr['secret']; |
| 142 | } |
| 143 | |
| 144 | if (isset($key['publicKey'])) { |
| 145 | if (!isset($components['curve'])) { |
| 146 | $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); |
| 147 | } |
| 148 | |
| 149 | $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']); |
| 150 | } |
| 151 | |
| 152 | if (isset($key['privateKey']) && !isset($components['QA'])) { |
| 153 | $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); |
| 154 | } |
| 155 | |
| 156 | return $components; |
| 157 | } |
| 158 | |
| 159 | /** |
| 160 | * Convert an EC public key to the appropriate format |
| 161 | * |
| 162 | * @param Integer[] $publicKey |
| 163 | * @param array $options optional |
| 164 | */ |
| 165 | public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string |
| 166 | { |
| 167 | self::initialize_static_variables(); |
| 168 | |
| 169 | if ($curve instanceof MontgomeryCurve) { |
| 170 | throw new UnsupportedCurveException('Montgomery Curves are not supported'); |
| 171 | } |
| 172 | |
| 173 | if ($curve instanceof TwistedEdwardsCurve) { |
| 174 | return self::wrapPublicKey( |
| 175 | $curve->encodePoint($publicKey), |
| 176 | null, |
| 177 | $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' |
| 178 | ); |
| 179 | } |
| 180 | |
| 181 | $params = new ASN1\Element(self::encodeParameters($curve, false, $options)); |
| 182 | |
| 183 | $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); |
| 184 | |
| 185 | return self::wrapPublicKey($key, $params, 'id-ecPublicKey'); |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Convert a private key to the appropriate format. |
| 190 | * |
| 191 | * @param Integer[] $publicKey |
| 192 | */ |
| 193 | public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, ?string $secret = null, ?string $password = null, array $options = []): string |
| 194 | { |
| 195 | self::initialize_static_variables(); |
| 196 | |
| 197 | if ($curve instanceof MontgomeryCurve) { |
| 198 | throw new UnsupportedCurveException('Montgomery Curves are not supported'); |
| 199 | } |
| 200 | |
| 201 | if ($curve instanceof TwistedEdwardsCurve) { |
| 202 | return self::wrapPrivateKey( |
| 203 | "\x04\x20" . $secret, |
| 204 | [], |
| 205 | null, |
| 206 | $password, |
| 207 | $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' |
| 208 | ); |
| 209 | } |
| 210 | |
| 211 | $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); |
| 212 | |
| 213 | $params = new ASN1\Element(self::encodeParameters($curve, false, $options)); |
| 214 | |
| 215 | $key = [ |
| 216 | 'version' => 'ecPrivkeyVer1', |
| 217 | 'privateKey' => $privateKey->toBytes(), |
| 218 | //'parameters' => $params, |
| 219 | 'publicKey' => "\0" . $publicKey, |
| 220 | ]; |
| 221 | |
| 222 | $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP); |
| 223 | |
| 224 | return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options); |
| 225 | } |
| 226 | } |
| 227 |