pixelfed/app/Util/HTTPSignatures/Key.php
2018-11-18 20:33:10 -07:00

260 lines
6.9 KiB
PHP
Executable file

<?php
namespace App\Util\HttpSignatures;
class Key
{
/** @var string */
private $id;
/** @var string */
private $secret;
/** @var resource */
private $certificate;
/** @var resource */
private $publicKey;
/** @var resource */
private $privateKey;
/** @var string */
private $type;
/**
* @param string $id
* @param string|array $secret
*/
public function __construct($id, $item)
{
$this->id = $id;
if (Key::hasX509Certificate($item) || Key::hasPublicKey($item)) {
$publicKey = Key::getPublicKey($item);
} else {
$publicKey = null;
}
if (Key::hasPrivateKey($item)) {
$privateKey = Key::getPrivateKey($item);
} else {
$privateKey = null;
}
if (($publicKey || $privateKey)) {
$this->type = 'asymmetric';
if ($publicKey && $privateKey) {
$publicKeyPEM = openssl_pkey_get_details($publicKey)['key'];
$privateKeyPublicPEM = openssl_pkey_get_details($privateKey)['key'];
if ($privateKeyPublicPEM != $publicKeyPEM) {
throw new KeyException('Supplied Certificate and Key are not related');
}
}
$this->privateKey = $privateKey;
$this->publicKey = $publicKey;
$this->secret = null;
} else {
$this->type = 'secret';
$this->secret = $item;
$this->publicKey = null;
$this->privateKey = null;
}
}
/**
* Retrieves private key resource from a input string or
* array of strings.
*
* @param string|array $object PEM-format Private Key or file path to same
*
* @return resource|false
*/
public static function getPrivateKey($object)
{
if (is_array($object)) {
foreach ($object as $candidateKey) {
$privateKey = Key::getPrivateKey($candidateKey);
if ($privateKey) {
return $privateKey;
}
}
} else {
// OpenSSL libraries don't have detection methods, so try..catch
try {
$privateKey = openssl_get_privatekey($object);
return $privateKey;
} catch (\Exception $e) {
return null;
}
}
}
/**
* Retrieves public key resource from a input string or
* array of strings.
*
* @param string|array $object PEM-format Public Key or file path to same
*
* @return resource|false
*/
public static function getPublicKey($object)
{
if (is_array($object)) {
// If we implement key rotation in future, this should add to a collection
foreach ($object as $candidateKey) {
$publicKey = Key::getPublicKey($candidateKey);
if ($publicKey) {
return $publicKey;
}
}
} else {
// OpenSSL libraries don't have detection methods, so try..catch
try {
$publicKey = openssl_get_publickey($object);
return $publicKey;
} catch (\Exception $e) {
return null;
}
}
}
/**
* Signing HTTP Messages 'keyId' field.
*
* @return string
*
* @throws KeyException
*/
public function getId()
{
return $this->id;
}
/**
* Retrieve Verifying Key - Public Key for Asymmetric/PKI, or shared secret for HMAC.
*
* @return string Shared Secret or PEM-format Public Key
*
* @throws KeyException
*/
public function getVerifyingKey()
{
switch ($this->type) {
case 'asymmetric':
if ($this->publicKey) {
return openssl_pkey_get_details($this->publicKey)['key'];
} else {
return null;
}
break;
case 'secret':
return $this->secret;
default:
throw new KeyException("Unknown key type $this->type");
}
}
/**
* Retrieve Signing Key - Private Key for Asymmetric/PKI, or shared secret for HMAC.
*
* @return string Shared Secret or PEM-format Private Key
*
* @throws KeyException
*/
public function getSigningKey()
{
switch ($this->type) {
case 'asymmetric':
if ($this->privateKey) {
openssl_pkey_export($this->privateKey, $pem);
return $pem;
} else {
return null;
}
break;
case 'secret':
return $this->secret;
default:
throw new KeyException("Unknown key type $this->type");
}
}
/**
* @return string 'secret' for HMAC or 'asymmetric'
*/
public function getType()
{
return $this->type;
}
/**
* Test if $object is, points to or contains, X.509 PEM-format certificate.
*
* @param string|array $object PEM Format X.509 Certificate or file path to one
*
* @return bool
*/
public static function hasX509Certificate($object)
{
if (is_array($object)) {
foreach ($object as $candidateCertificate) {
$result = Key::hasX509Certificate($candidateCertificate);
if ($result) {
return $result;
}
}
} else {
// OpenSSL libraries don't have detection methods, so try..catch
try {
openssl_x509_export($object, $null);
return true;
} catch (\Exception $e) {
return false;
}
}
}
/**
* Test if $object is, points to or contains, PEM-format Public Key.
*
* @param string|array $object PEM-format Public Key or file path to one
*
* @return bool
*/
public static function hasPublicKey($object)
{
if (is_array($object)) {
foreach ($object as $candidatePublicKey) {
$result = Key::hasPublicKey($candidatePublicKey);
if ($result) {
return $result;
}
}
} else {
return false == !openssl_pkey_get_public($object);
}
}
/**
* Test if $object is, points to or contains, PEM-format Private Key.
*
* @param string|array $object PEM-format Private Key or file path to one
*
* @return bool
*/
public static function hasPrivateKey($object)
{
if (is_array($object)) {
foreach ($object as $candidatePrivateKey) {
$result = Key::hasPrivateKey($candidatePrivateKey);
if ($result) {
return $result;
}
}
} else {
return false != openssl_pkey_get_private($object);
}
}
}