Viel neues

This commit is contained in:
Sven Steinert
2026-04-30 12:06:00 +02:00
parent 118809bfae
commit fce31ebcd7
1274 changed files with 181255 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
<?php
/**
* JSON Web Key (RFC7517) Formatted RSA Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
use phpseclib3\Math\BigInteger;
/**
* JWK Formatted RSA Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class JWK extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
$key = parent::loadHelper($key);
if ($key->kty != 'RSA') {
throw new \RuntimeException('Only RSA JWK keys are supported');
}
$count = $publicCount = 0;
$vars = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi'];
foreach ($vars as $var) {
if (!isset($key->$var) || !is_string($key->$var)) {
continue;
}
$count++;
$value = new BigInteger(Strings::base64url_decode($key->$var), 256);
switch ($var) {
case 'n':
$publicCount++;
$components['modulus'] = $value;
break;
case 'e':
$publicCount++;
$components['publicExponent'] = $value;
break;
case 'd':
$components['privateExponent'] = $value;
break;
case 'p':
$components['primes'][1] = $value;
break;
case 'q':
$components['primes'][2] = $value;
break;
case 'dp':
$components['exponents'][1] = $value;
break;
case 'dq':
$components['exponents'][2] = $value;
break;
case 'qi':
$components['coefficients'][2] = $value;
}
}
if ($count == count($vars)) {
return $components + ['isPublicKey' => false];
}
if ($count == 2 && $publicCount == 2) {
return $components + ['isPublicKey' => true];
}
throw new \UnexpectedValueException('Key does not have an appropriate number of RSA parameters');
}
/**
* Convert a private key to the appropriate format.
*
* @param string $password optional
* @param array $options optional
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
if (count($primes) != 2) {
throw new \InvalidArgumentException('JWK does not support multi-prime RSA keys');
}
$key = [
'kty' => 'RSA',
'n' => Strings::base64url_encode($n->toBytes()),
'e' => Strings::base64url_encode($e->toBytes()),
'd' => Strings::base64url_encode($d->toBytes()),
'p' => Strings::base64url_encode($primes[1]->toBytes()),
'q' => Strings::base64url_encode($primes[2]->toBytes()),
'dp' => Strings::base64url_encode($exponents[1]->toBytes()),
'dq' => Strings::base64url_encode($exponents[2]->toBytes()),
'qi' => Strings::base64url_encode($coefficients[2]->toBytes()),
];
return self::wrapKey($key, $options);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
{
$key = [
'kty' => 'RSA',
'n' => Strings::base64url_encode($n->toBytes()),
'e' => Strings::base64url_encode($e->toBytes()),
];
return self::wrapKey($key, $options);
}
}

View File

@@ -0,0 +1,210 @@
<?php
/**
* Miccrosoft BLOB Formatted RSA Key Handler
*
* More info:
*
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\InvalidArgumentException;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* Microsoft BLOB Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class MSBLOB
{
/**
* Public/Private Key Pair
*/
public const PRIVATEKEYBLOB = 0x7;
/**
* Public Key
*/
public const PUBLICKEYBLOB = 0x6;
/**
* Public Key
*/
public const PUBLICKEYBLOBEX = 0xA;
/**
* RSA public key exchange algorithm
*/
public const CALG_RSA_KEYX = 0x0000A400;
/**
* RSA public key exchange algorithm
*/
public const CALG_RSA_SIGN = 0x00002400;
/**
* Public Key
*/
public const RSA1 = 0x31415352;
/**
* Private Key
*/
public const RSA2 = 0x32415352;
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
if (!Strings::is_stringable($key)) {
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
}
$key = Strings::base64_decode($key);
if (!is_string($key)) {
throw new UnexpectedValueException('Base64 decoding produced an error');
}
if (strlen($key) < 20) {
throw new UnexpectedValueException('Key appears to be malformed');
}
// PUBLICKEYSTRUC publickeystruc
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx
extract(unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8)));
/**
* @var string $type
* @var string $version
* @var integer $reserved
* @var integer $algo
*/
switch (ord($type)) {
case self::PUBLICKEYBLOB:
case self::PUBLICKEYBLOBEX:
$publickey = true;
break;
case self::PRIVATEKEYBLOB:
$publickey = false;
break;
default:
throw new UnexpectedValueException('Key appears to be malformed');
}
$components = ['isPublicKey' => $publickey];
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
switch ($algo) {
case self::CALG_RSA_KEYX:
case self::CALG_RSA_SIGN:
break;
default:
throw new UnexpectedValueException('Key appears to be malformed');
}
// RSAPUBKEY rsapubkey
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx
// could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit
extract(unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12)));
/**
* @var integer $magic
* @var integer $bitlen
* @var string $pubexp
*/
switch ($magic) {
case self::RSA2:
$components['isPublicKey'] = false;
// fall-through
case self::RSA1:
break;
default:
throw new UnexpectedValueException('Key appears to be malformed');
}
$baseLength = $bitlen / 16;
if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) {
throw new UnexpectedValueException('Key appears to be malformed');
}
$components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256);
// BYTE modulus[rsapubkey.bitlen/8]
$components['modulus'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);
if ($publickey) {
return $components;
}
$components['isPublicKey'] = false;
// BYTE prime1[rsapubkey.bitlen/16]
$components['primes'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
// BYTE prime2[rsapubkey.bitlen/16]
$components['primes'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
// BYTE exponent1[rsapubkey.bitlen/16]
$components['exponents'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
// BYTE exponent2[rsapubkey.bitlen/16]
$components['exponents'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
// BYTE coefficient[rsapubkey.bitlen/16]
$components['coefficients'] = [2 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
if (isset($components['privateExponent'])) {
$components['publicExponent'] = $components['privateExponent'];
}
// BYTE privateExponent[rsapubkey.bitlen/8]
$components['privateExponent'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);
return $components;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null): string
{
if (count($primes) != 2) {
throw new InvalidArgumentException('MSBLOB does not support multi-prime RSA keys');
}
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('MSBLOB private keys do not support encryption');
}
$n = strrev($n->toBytes());
$e = str_pad(strrev($e->toBytes()), 4, "\0");
$key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
$key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e);
$key .= $n;
$key .= strrev($primes[1]->toBytes());
$key .= strrev($primes[2]->toBytes());
$key .= strrev($exponents[1]->toBytes());
$key .= strrev($exponents[2]->toBytes());
$key .= strrev($coefficients[2]->toBytes());
$key .= strrev($d->toBytes());
return Strings::base64_encode($key);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $n, BigInteger $e): string
{
$n = strrev($n->toBytes());
$e = str_pad(strrev($e->toBytes()), 4, "\0");
$key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
$key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e);
$key .= $n;
return Strings::base64_encode($key);
}
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* OpenSSH Formatted RSA Key Handler
*
* PHP version 5
*
* Place in $HOME/.ssh/authorized_keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib3\Exception\RuntimeException;
use phpseclib3\Math\BigInteger;
/**
* OpenSSH Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSH extends Progenitor
{
/**
* Supported Key Types
*
* @var array
*/
protected static $types = ['ssh-rsa'];
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
$parsed = parent::load($key, $password);
if (isset($parsed['paddedKey'])) {
[$type] = Strings::unpackSSH2('s', $parsed['paddedKey']);
if ($type != $parsed['type']) {
throw new RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
}
$primes = $coefficients = [];
[
$modulus,
$publicExponent,
$privateExponent,
$coefficients[2],
$primes[1],
$primes[2],
$comment,
] = Strings::unpackSSH2('i6s', $parsed['paddedKey']);
$temp = $primes[1]->subtract($one);
$exponents = [1 => $publicExponent->modInverse($temp)];
$temp = $primes[2]->subtract($one);
$exponents[] = $publicExponent->modInverse($temp);
$isPublicKey = false;
return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey');
}
[$publicExponent, $modulus] = Strings::unpackSSH2('ii', $parsed['publicKey']);
return [
'isPublicKey' => true,
'modulus' => $modulus,
'publicExponent' => $publicExponent,
'comment' => $parsed['comment'],
];
}
/**
* Convert a public key to the appropriate format
*
* @param array $options optional
*/
public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
{
$RSAPublicKey = Strings::packSSH2('sii', 'ssh-rsa', $e, $n);
if ($options['binary'] ?? self::$binary) {
return $RSAPublicKey;
}
$comment = $options['comment'] ?? self::$comment;
$RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $comment;
return $RSAPublicKey;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
$publicKey = self::savePublicKey($n, $e, ['binary' => true]);
$privateKey = Strings::packSSH2('si6', 'ssh-rsa', $n, $e, $d, $coefficients[2], $primes[1], $primes[2]);
return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* PKCS#1 Formatted RSA Key Handler
*
* PHP version 5
*
* Used by File/X509.php
*
* Processes keys with the following headers:
*
* -----BEGIN RSA PRIVATE KEY-----
* -----BEGIN RSA PUBLIC KEY-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib3\Exception\RuntimeException;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#1 Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
* @param string|false $password
*/
public static function load($key, ?string $password = null): array
{
if (!Strings::is_stringable($key)) {
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
}
if (str_contains($key, 'PUBLIC')) {
$components = ['isPublicKey' => true];
} elseif (str_contains($key, 'PRIVATE')) {
$components = ['isPublicKey' => false];
} else {
$components = [];
}
$key = parent::load($key, $password);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new RuntimeException('Unable to decode BER');
}
$key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP);
if (is_array($key)) {
$components += [
'modulus' => $key['modulus'],
'publicExponent' => $key['publicExponent'],
'privateExponent' => $key['privateExponent'],
'primes' => [1 => $key['prime1'], $key['prime2']],
'exponents' => [1 => $key['exponent1'], $key['exponent2']],
'coefficients' => [2 => $key['coefficient']],
];
if ($key['version'] == 'multi') {
foreach ($key['otherPrimeInfos'] as $primeInfo) {
$components['primes'][] = $primeInfo['prime'];
$components['exponents'][] = $primeInfo['exponent'];
$components['coefficients'][] = $primeInfo['coefficient'];
}
}
if (!isset($components['isPublicKey'])) {
$components['isPublicKey'] = false;
}
return $components;
}
$key = ASN1::asn1map($decoded[0], Maps\RSAPublicKey::MAP);
if (!is_array($key)) {
throw new RuntimeException('Unable to perform ASN1 mapping');
}
if (!isset($components['isPublicKey'])) {
$components['isPublicKey'] = true;
}
return $components + $key;
}
/**
* Convert a private key to the appropriate format.
*
* @param string|false $password
* @param array $options optional
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
$num_primes = count($primes);
$key = [
'version' => $num_primes == 2 ? 'two-prime' : 'multi',
'modulus' => $n,
'publicExponent' => $e,
'privateExponent' => $d,
'prime1' => $primes[1],
'prime2' => $primes[2],
'exponent1' => $exponents[1],
'exponent2' => $exponents[2],
'coefficient' => $coefficients[2],
];
for ($i = 3; $i <= $num_primes; $i++) {
$key['otherPrimeInfos'][] = [
'prime' => $primes[$i],
'exponent' => $exponents[$i],
'coefficient' => $coefficients[$i],
];
}
$key = ASN1::encodeDER($key, Maps\RSAPrivateKey::MAP);
return self::wrapPrivateKey($key, 'RSA', $password, $options);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $n, BigInteger $e): string
{
$key = [
'modulus' => $n,
'publicExponent' => $e,
];
$key = ASN1::encodeDER($key, Maps\RSAPublicKey::MAP);
return self::wrapPublicKey($key, 'RSA');
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* PKCS#8 Formatted RSA Key Handler
*
* PHP version 5
*
* Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----BEGIN PUBLIC KEY-----
*
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
* is specific to private keys it's basically creating a DER-encoded wrapper
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\File\ASN1;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends Progenitor
{
/**
* OID Name
*
* @var string
*/
public const OID_NAME = 'rsaEncryption';
/**
* OID Value
*
* @var string
*/
public const OID_VALUE = '1.2.840.113549.1.1.1';
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
$key = parent::load($key, $password);
if (isset($key['privateKey'])) {
$components['isPublicKey'] = false;
$type = 'private';
} else {
$components['isPublicKey'] = true;
$type = 'public';
}
$result = $components + PKCS1::load($key[$type . 'Key']);
if (isset($key['meta'])) {
$result['meta'] = $key['meta'];
}
return $result;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
$key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
$key = ASN1::extractBER($key);
return self::wrapPrivateKey($key, [], null, $password, null, '', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param array $options optional
*/
public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
{
$key = PKCS1::savePublicKey($n, $e);
$key = ASN1::extractBER($key);
return self::wrapPublicKey($key, null);
}
}

View File

@@ -0,0 +1,225 @@
<?php
/**
* PKCS#8 Formatted RSA-PSS Key Handler
*
* PHP version 5
*
* Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----BEGIN PUBLIC KEY-----
*
* Analogous to "openssl genpkey -algorithm rsa-pss".
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted RSA-PSS Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PSS extends Progenitor
{
/**
* OID Name
*
* @var string
*/
public const OID_NAME = 'id-RSASSA-PSS';
/**
* OID Value
*
* @var string
*/
public const OID_VALUE = '1.2.840.113549.1.1.10';
/**
* OIDs loaded
*
* @var bool
*/
private static $oidsLoaded = false;
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Initialize static variables
*/
private static function initialize_static_variables(): void
{
if (!self::$oidsLoaded) {
ASN1::loadOIDs([
'md2' => '1.2.840.113549.2.2',
'md4' => '1.2.840.113549.2.4',
'md5' => '1.2.840.113549.2.5',
'id-sha1' => '1.3.14.3.2.26',
'id-sha256' => '2.16.840.1.101.3.4.2.1',
'id-sha384' => '2.16.840.1.101.3.4.2.2',
'id-sha512' => '2.16.840.1.101.3.4.2.3',
'id-sha224' => '2.16.840.1.101.3.4.2.4',
'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
'id-mgf1' => '1.2.840.113549.1.1.8',
]);
self::$oidsLoaded = true;
}
}
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
self::initialize_static_variables();
if (!Strings::is_stringable($key)) {
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
}
$components = ['isPublicKey' => str_contains($key, 'PUBLIC')];
$key = parent::load($key, $password);
$type = isset($key['privateKey']) ? 'private' : 'public';
$result = $components + PKCS1::load($key[$type . 'Key']);
if (isset($key[$type . 'KeyAlgorithm']['parameters'])) {
$decoded = ASN1::decodeBER($key[$type . 'KeyAlgorithm']['parameters']);
if ($decoded === false) {
throw new UnexpectedValueException('Unable to decode parameters');
}
$params = ASN1::asn1map($decoded[0], Maps\RSASSA_PSS_params::MAP);
} else {
$params = [];
}
if (isset($params['maskGenAlgorithm']['parameters'])) {
$decoded = ASN1::decodeBER($params['maskGenAlgorithm']['parameters']);
if ($decoded === false) {
throw new UnexpectedValueException('Unable to decode parameters');
}
$params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP);
} else {
$params['maskGenAlgorithm'] = [
'algorithm' => 'id-mgf1',
'parameters' => ['algorithm' => 'id-sha1'],
];
}
if (!isset($params['hashAlgorithm']['algorithm'])) {
$params['hashAlgorithm']['algorithm'] = 'id-sha1';
}
$result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']);
$result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']);
if (isset($params['saltLength'])) {
$result['saltLength'] = (int) $params['saltLength']->toString();
}
if (isset($key['meta'])) {
$result['meta'] = $key['meta'];
}
return $result;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
self::initialize_static_variables();
$key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
$key = ASN1::extractBER($key);
$params = self::savePSSParams($options);
return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param array $options optional
*/
public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
{
self::initialize_static_variables();
$key = PKCS1::savePublicKey($n, $e);
$key = ASN1::extractBER($key);
$params = self::savePSSParams($options);
return self::wrapPublicKey($key, $params);
}
/**
* Encodes PSS parameters
*
* @return string
*/
public static function savePSSParams(array $options)
{
/*
The trailerField field is an integer. It provides
compatibility with IEEE Std 1363a-2004 [P1363A]. The value
MUST be 1, which represents the trailer field with hexadecimal
value 0xBC. Other trailer fields, including the trailer field
composed of HashID concatenated with 0xCC that is specified in
IEEE Std 1363a, are not supported. Implementations that
perform signature generation MUST omit the trailerField field,
indicating that the default trailer field value was used.
Implementations that perform signature validation MUST
recognize both a present trailerField field with value 1 and an
absent trailerField field.
source: https://tools.ietf.org/html/rfc4055#page-9
*/
$params = [
'trailerField' => new BigInteger(1),
];
if (isset($options['hash'])) {
$params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash'];
}
if (isset($options['MGFHash'])) {
$temp = ['algorithm' => 'id-' . $options['MGFHash']];
$temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
$params['maskGenAlgorithm'] = [
'algorithm' => 'id-mgf1',
'parameters' => new ASN1\Element($temp),
];
}
if (isset($options['saltLength'])) {
$params['saltLength'] = new BigInteger($options['saltLength']);
}
return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP));
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* PuTTY Formatted RSA Key Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib3\Exception\InvalidArgumentException;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\Math\BigInteger;
/**
* PuTTY Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PuTTY extends Progenitor
{
/**
* Public Handler
*
* @var string
*/
public const PUBLIC_HANDLER = 'phpseclib3\Crypt\RSA\Formats\Keys\OpenSSH';
/**
* Algorithm Identifier
*
* @var array
*/
protected static $types = ['ssh-rsa'];
/**
* Break a public or private key down into its constituent components
*
* @param array|string $key
* @param string|false $password
* @return array|false
*/
public static function load($key, $password)
{
static $one;
if (!isset($one)) {
$one = new BigInteger(1);
}
$components = parent::load($key, $password);
if (!isset($components['private'])) {
return $components;
}
extract($components);
unset($components['public'], $components['private']);
$isPublicKey = false;
$result = Strings::unpackSSH2('ii', $public);
if ($result === false) {
throw new UnexpectedValueException('Key appears to be malformed');
}
[$publicExponent, $modulus] = $result;
$result = Strings::unpackSSH2('iiii', $private);
if ($result === false) {
throw new UnexpectedValueException('Key appears to be malformed');
}
$primes = $coefficients = [];
[$privateExponent, $primes[1], $primes[2], $coefficients[2]] = $result;
$temp = $primes[1]->subtract($one);
$exponents = [1 => $publicExponent->modInverse($temp)];
$temp = $primes[2]->subtract($one);
$exponents[] = $publicExponent->modInverse($temp);
return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey');
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
if (count($primes) != 2) {
throw new InvalidArgumentException('PuTTY does not support multi-prime RSA keys');
}
$public = Strings::packSSH2('ii', $e, $n);
$private = Strings::packSSH2('iiii', $d, $primes[1], $primes[2], $coefficients[2]);
return self::wrapPrivateKey($public, $private, 'ssh-rsa', $password, $options);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $n, BigInteger $e): string
{
return self::wrapPublicKey(Strings::packSSH2('ii', $e, $n), 'ssh-rsa');
}
}

View File

@@ -0,0 +1,166 @@
<?php
/**
* Raw RSA Key Handler
*
* PHP version 5
*
* An array containing two \phpseclib3\Math\BigInteger objects.
*
* The exponent can be indexed with any of the following:
*
* 0, e, exponent, publicExponent
*
* The modulus can be indexed with any of the following:
*
* 1, n, modulo, modulus
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* Raw RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw
{
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
if (!is_array($key)) {
throw new UnexpectedValueException('Key should be a array - not a ' . gettype($key));
}
$key = array_change_key_case($key, CASE_LOWER);
$components = ['isPublicKey' => false];
foreach (['e', 'exponent', 'publicexponent', 0, 'privateexponent', 'd'] as $index) {
if (isset($key[$index])) {
$components['publicExponent'] = $key[$index];
break;
}
}
foreach (['n', 'modulo', 'modulus', 1] as $index) {
if (isset($key[$index])) {
$components['modulus'] = $key[$index];
break;
}
}
if (!isset($components['publicExponent']) || !isset($components['modulus'])) {
throw new UnexpectedValueException('Modulus / exponent not present');
}
if (isset($key['primes'])) {
$components['primes'] = $key['primes'];
} elseif (isset($key['p']) && isset($key['q'])) {
$indices = [
['p', 'q'],
['prime1', 'prime2'],
];
foreach ($indices as $index) {
[$i0, $i1] = $index;
if (isset($key[$i0]) && isset($key[$i1])) {
$components['primes'] = [1 => $key[$i0], $key[$i1]];
}
}
}
if (isset($key['exponents'])) {
$components['exponents'] = $key['exponents'];
} else {
$indices = [
['dp', 'dq'],
['exponent1', 'exponent2'],
];
foreach ($indices as $index) {
[$i0, $i1] = $index;
if (isset($key[$i0]) && isset($key[$i1])) {
$components['exponents'] = [1 => $key[$i0], $key[$i1]];
}
}
}
if (isset($key['coefficients'])) {
$components['coefficients'] = $key['coefficients'];
} else {
foreach (['inverseq', 'q\'', 'coefficient'] as $index) {
if (isset($key[$index])) {
$components['coefficients'] = [2 => $key[$index]];
}
}
}
if (!isset($components['primes'])) {
$components['isPublicKey'] = true;
return $components;
}
if (!isset($components['exponents'])) {
$one = new BigInteger(1);
$temp = $components['primes'][1]->subtract($one);
$exponents = [1 => $components['publicExponent']->modInverse($temp)];
$temp = $components['primes'][2]->subtract($one);
$exponents[] = $components['publicExponent']->modInverse($temp);
$components['exponents'] = $exponents;
}
if (!isset($components['coefficients'])) {
$components['coefficients'] = [2 => $components['primes'][2]->modInverse($components['primes'][1])];
}
foreach (['privateexponent', 'd'] as $index) {
if (isset($key[$index])) {
$components['privateExponent'] = $key[$index];
break;
}
}
return $components;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, ?string $password = null, array $options = []): string
{
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('Raw private keys do not support encryption');
}
return serialize([
'e' => clone $e,
'n' => clone $n,
'd' => clone $d,
'primes' => array_map(fn ($var) => clone $var, $primes),
'exponents' => array_map(fn ($var) => clone $var, $exponents),
'coefficients' => array_map(fn ($var) => clone $var, $coefficients),
]);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $n, BigInteger $e): array
{
return ['e' => clone $e, 'n' => clone $n];
}
}

View File

@@ -0,0 +1,162 @@
<?php
/**
* XML Formatted RSA Key Handler
*
* More info:
*
* http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
* http://www.w3.org/TR/xkms2/#XKMS_2_0_Paragraph_269
* http://en.wikipedia.org/wiki/XML_Signature
* http://en.wikipedia.org/wiki/XKMS
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\RSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\InvalidArgumentException;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\Exception\UnsupportedFormatException;
use phpseclib3\Math\BigInteger;
/**
* XML Formatted RSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class XML
{
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key): array
{
if (!Strings::is_stringable($key)) {
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
}
if (!class_exists('DOMDocument')) {
throw new BadConfigurationException('The dom extension is not setup correctly on this system');
}
$components = [
'isPublicKey' => false,
'primes' => [],
'exponents' => [],
'coefficients' => [],
];
$use_errors = libxml_use_internal_errors(true);
$dom = new \DOMDocument();
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key . '</xml>';
}
if (!$dom->loadXML($key)) {
libxml_use_internal_errors($use_errors);
throw new UnexpectedValueException('Key does not appear to contain XML');
}
$xpath = new \DOMXPath($dom);
$keys = ['modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd'];
foreach ($keys as $key) {
// $dom->getElementsByTagName($key) is case-sensitive
$temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
if (!$temp->length) {
continue;
}
$value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
switch ($key) {
case 'modulus':
$components['modulus'] = $value;
break;
case 'exponent':
$components['publicExponent'] = $value;
break;
case 'p':
$components['primes'][1] = $value;
break;
case 'q':
$components['primes'][2] = $value;
break;
case 'dp':
$components['exponents'][1] = $value;
break;
case 'dq':
$components['exponents'][2] = $value;
break;
case 'inverseq':
$components['coefficients'][2] = $value;
break;
case 'd':
$components['privateExponent'] = $value;
}
}
libxml_use_internal_errors($use_errors);
foreach ($components as $key => $value) {
if (is_array($value) && !count($value)) {
unset($components[$key]);
}
}
if (isset($components['modulus']) && isset($components['publicExponent'])) {
if (count($components) == 3) {
$components['isPublicKey'] = true;
}
return $components;
}
throw new UnexpectedValueException('Modulus / exponent not present');
}
/**
* Convert a private key to the appropriate format.
*
* @param string $password optional
*/
public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, string $password = ''): string
{
if (count($primes) != 2) {
throw new InvalidArgumentException('XML does not support multi-prime RSA keys');
}
if (!empty($password) && is_string($password)) {
throw new UnsupportedFormatException('XML private keys do not support encryption');
}
return "<RSAKeyPair>\r\n" .
' <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" .
' <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" .
' <P>' . Strings::base64_encode($primes[1]->toBytes()) . "</P>\r\n" .
' <Q>' . Strings::base64_encode($primes[2]->toBytes()) . "</Q>\r\n" .
' <DP>' . Strings::base64_encode($exponents[1]->toBytes()) . "</DP>\r\n" .
' <DQ>' . Strings::base64_encode($exponents[2]->toBytes()) . "</DQ>\r\n" .
' <InverseQ>' . Strings::base64_encode($coefficients[2]->toBytes()) . "</InverseQ>\r\n" .
' <D>' . Strings::base64_encode($d->toBytes()) . "</D>\r\n" .
'</RSAKeyPair>';
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $n, BigInteger $e): string
{
return "<RSAKeyValue>\r\n" .
' <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" .
' <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" .
'</RSAKeyValue>';
}
}