welcome back to dyb-tech
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Base64;
|
||||
|
||||
class Base64Encoder implements Encoder
|
||||
{
|
||||
/**
|
||||
* @param string $data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode($data)
|
||||
{
|
||||
return base64_encode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decode($data)
|
||||
{
|
||||
return base64_decode($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Base64;
|
||||
|
||||
class Base64UrlSafeEncoder implements Encoder
|
||||
{
|
||||
public function encode($data)
|
||||
{
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
return base64_decode(strtr($data, '-_', '+/'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Base64;
|
||||
|
||||
interface Encoder
|
||||
{
|
||||
/**
|
||||
* @param string $data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode($data);
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decode($data);
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Namshi\JOSE\Base64\Base64Encoder;
|
||||
use Namshi\JOSE\Base64\Base64UrlSafeEncoder;
|
||||
use Namshi\JOSE\Base64\Encoder;
|
||||
use Namshi\JOSE\Signer\SignerInterface;
|
||||
|
||||
/**
|
||||
* Class representing a JSON Web Signature.
|
||||
*/
|
||||
class JWS extends JWT
|
||||
{
|
||||
protected $signature;
|
||||
protected $isSigned = false;
|
||||
protected $originalToken;
|
||||
protected $encodedSignature;
|
||||
protected $encryptionEngine;
|
||||
protected $supportedEncryptionEngines = array('OpenSSL', 'SecLib');
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $header An associative array of headers. The value can be any type accepted by json_encode or a JSON serializable object
|
||||
*
|
||||
* @see http://php.net/manual/en/function.json-encode.php
|
||||
* @see http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @see https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
|
||||
*
|
||||
* @param string $encryptionEngine
|
||||
* }
|
||||
*/
|
||||
public function __construct($header = array(), $encryptionEngine = 'OpenSSL')
|
||||
{
|
||||
if (!in_array($encryptionEngine, $this->supportedEncryptionEngines)) {
|
||||
throw new InvalidArgumentException(sprintf('Encryption engine %s is not supported', $encryptionEngine));
|
||||
}
|
||||
|
||||
if ('SecLib' === $encryptionEngine && version_compare(PHP_VERSION, '7.0.0-dev') >= 0) {
|
||||
throw new InvalidArgumentException("phpseclib 1.0.0(LTS), even the latest 2.0.0, doesn't support PHP7 yet");
|
||||
}
|
||||
|
||||
$this->encryptionEngine = $encryptionEngine;
|
||||
|
||||
parent::__construct(array(), $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the JWS signininput.
|
||||
*
|
||||
* @param resource|string $key
|
||||
* @param optional string $password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function sign($key, $password = null)
|
||||
{
|
||||
$this->signature = $this->getSigner()->sign($this->generateSigninInput(), $key, $password);
|
||||
$this->isSigned = true;
|
||||
|
||||
return $this->signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signature representation of the JWS.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSignature()
|
||||
{
|
||||
if ($this->isSigned()) {
|
||||
return $this->signature;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the JSW has already been signed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSigned()
|
||||
{
|
||||
return (bool) $this->isSigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representing the JWT.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTokenString()
|
||||
{
|
||||
$signinInput = $this->generateSigninInput();
|
||||
|
||||
return sprintf('%s.%s', $signinInput, $this->encoder->encode($this->getSignature()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a JWS from a JWT.
|
||||
*
|
||||
* @param string $jwsTokenString
|
||||
* @param bool $allowUnsecure
|
||||
* @param Encoder $encoder
|
||||
* @param string $encryptionEngine
|
||||
*
|
||||
* @return JWS
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function load($jwsTokenString, $allowUnsecure = false, Encoder $encoder = null, $encryptionEngine = 'OpenSSL')
|
||||
{
|
||||
if ($encoder === null) {
|
||||
$encoder = strpbrk($jwsTokenString, '+/=') ? new Base64Encoder() : new Base64UrlSafeEncoder();
|
||||
}
|
||||
|
||||
$parts = explode('.', $jwsTokenString);
|
||||
|
||||
if (count($parts) === 3) {
|
||||
$header = json_decode($encoder->decode($parts[0]), true);
|
||||
$payload = json_decode($encoder->decode($parts[1]), true);
|
||||
|
||||
if (is_array($header) && is_array($payload)) {
|
||||
if (strtolower($header['alg']) === 'none' && !$allowUnsecure) {
|
||||
throw new InvalidArgumentException(sprintf('The token "%s" cannot be validated in a secure context, as it uses the unallowed "none" algorithm', $jwsTokenString));
|
||||
}
|
||||
|
||||
$jws = new static($header, $encryptionEngine);
|
||||
|
||||
$jws->setEncoder($encoder)
|
||||
->setHeader($header)
|
||||
->setPayload($payload)
|
||||
->setOriginalToken($jwsTokenString)
|
||||
->setEncodedSignature($parts[2]);
|
||||
|
||||
return $jws;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('The token "%s" is an invalid JWS', $jwsTokenString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the internal signin input corresponds to the encoded
|
||||
* signature previously stored (@see JWS::load).
|
||||
*
|
||||
* @param resource|string $key
|
||||
* @param string $algo The algorithms this JWS should be signed with. Use it if you want to restrict which algorithms you want to allow to be validated.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verify($key, $algo = null)
|
||||
{
|
||||
if (empty($key) || ($algo && $this->header['alg'] !== $algo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$decodedSignature = $this->encoder->decode($this->getEncodedSignature());
|
||||
$signinInput = $this->getSigninInput();
|
||||
|
||||
return $this->getSigner()->verify($key, $decodedSignature, $signinInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original token signin input if it exists, otherwise generate the
|
||||
* signin input for the current JWS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSigninInput()
|
||||
{
|
||||
$parts = explode('.', $this->originalToken);
|
||||
|
||||
if (count($parts) >= 2) {
|
||||
return sprintf('%s.%s', $parts[0], $parts[1]);
|
||||
}
|
||||
|
||||
return $this->generateSigninInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the original base64 encoded token.
|
||||
*
|
||||
* @param string $originalToken
|
||||
*
|
||||
* @return JWS
|
||||
*/
|
||||
private function setOriginalToken($originalToken)
|
||||
{
|
||||
$this->originalToken = $originalToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base64 encoded signature.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncodedSignature()
|
||||
{
|
||||
return $this->encodedSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base64 encoded signature.
|
||||
*
|
||||
* @param string $encodedSignature
|
||||
*
|
||||
* @return JWS
|
||||
*/
|
||||
public function setEncodedSignature($encodedSignature)
|
||||
{
|
||||
$this->encodedSignature = $encodedSignature;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signer responsible to encrypting / decrypting this JWS.
|
||||
*
|
||||
* @return SignerInterface
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function getSigner()
|
||||
{
|
||||
$signerClass = sprintf('Namshi\\JOSE\\Signer\\%s\\%s', $this->encryptionEngine, $this->header['alg']);
|
||||
|
||||
if (class_exists($signerClass)) {
|
||||
return new $signerClass();
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
sprintf("The algorithm '%s' is not supported for %s", $this->header['alg'], $this->encryptionEngine));
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE;
|
||||
|
||||
use Namshi\JOSE\Base64\Base64UrlSafeEncoder;
|
||||
use Namshi\JOSE\Base64\Encoder;
|
||||
|
||||
/**
|
||||
* Class representing a JSON Web Token.
|
||||
*/
|
||||
class JWT
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $payload;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $header;
|
||||
|
||||
/**
|
||||
* @var Encoder
|
||||
*/
|
||||
protected $encoder;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $payload
|
||||
* @param array $header
|
||||
*/
|
||||
public function __construct(array $payload, array $header)
|
||||
{
|
||||
$this->setPayload($payload);
|
||||
$this->setHeader($header);
|
||||
$this->setEncoder(new Base64UrlSafeEncoder());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Encoder $encoder
|
||||
*/
|
||||
public function setEncoder(Encoder $encoder)
|
||||
{
|
||||
$this->encoder = $encoder;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the signininput for the current JWT.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateSigninInput()
|
||||
{
|
||||
$base64payload = $this->encoder->encode(json_encode($this->getPayload(), JSON_UNESCAPED_SLASHES));
|
||||
$base64header = $this->encoder->encode(json_encode($this->getHeader(), JSON_UNESCAPED_SLASHES));
|
||||
|
||||
return sprintf('%s.%s', $base64header, $base64payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the payload of the JWT.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPayload()
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the payload of the current JWT.
|
||||
*
|
||||
* @param array $payload
|
||||
*/
|
||||
public function setPayload(array $payload)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header of the JWT.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHeader()
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the header of this JWT.
|
||||
*
|
||||
* @param array $header
|
||||
*/
|
||||
public function setHeader(array $header)
|
||||
{
|
||||
$this->header = $header;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
use phpseclib\File\ASN1;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the a ECDSA algorithm, after hashing it.
|
||||
*/
|
||||
abstract class ECDSA extends PublicKey
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '7.0.0-dev') >= 0) {
|
||||
throw new \InvalidArgumentException("phpseclib 1.0.0(LTS), even the latest 2.0.0, doesn't support PHP7 yet");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function supportsKey($key)
|
||||
{
|
||||
if (false === parent::supportsKey($key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// openssl_sign with EC keys was introduced in this PHP release
|
||||
$minVersions = array(
|
||||
'5.4' => '5.4.26',
|
||||
'5.5' => '5.5.10',
|
||||
'5.6' => '5.6.0',
|
||||
);
|
||||
|
||||
if (isset($minVersions[PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION]) &&
|
||||
version_compare(PHP_VERSION, $minVersions[PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION], '<')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$keyDetails = openssl_pkey_get_details($key);
|
||||
|
||||
if (0 === preg_match('/-----BEGIN PUBLIC KEY-----([^-]+)-----END PUBLIC KEY-----/', $keyDetails['key'], $matches)) {
|
||||
return false;
|
||||
}
|
||||
$publicKey = trim($matches[1]);
|
||||
$asn1 = new ASN1();
|
||||
|
||||
/*
|
||||
* http://tools.ietf.org/html/rfc3279#section-2.2.3
|
||||
* AlgorithmIdentifier ::= SEQUENCE {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
* }
|
||||
* For ECDSA Signature Algorithm:
|
||||
* algorithm: ansi-X9-62 => 1.2.840.10045.2.1
|
||||
* parameters: id-ecSigType => 1.2.840.10045.x.y.z
|
||||
*
|
||||
*/
|
||||
$asnAlgorithmIdentifier = array(
|
||||
'type' => ASN1::TYPE_SEQUENCE,
|
||||
'children' => array(
|
||||
'ansi-X9-62' => array(
|
||||
'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
|
||||
),
|
||||
'id-ecSigType' => array(
|
||||
'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/*
|
||||
* http://tools.ietf.org/html/rfc5280#section-4.1
|
||||
* SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
* algorithm AlgorithmIdentifier,
|
||||
* subjectPublicKey BIT STRING
|
||||
* }
|
||||
*/
|
||||
$asnSubjectPublicKeyInfo = array(
|
||||
'type' => ASN1::TYPE_SEQUENCE,
|
||||
'children' => array(
|
||||
'algorithm' => $asnAlgorithmIdentifier,
|
||||
'subjectPublicKey' => array(
|
||||
'type' => ASN1::TYPE_BIT_STRING,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$decoded = $asn1->decodeBER(base64_decode($publicKey));
|
||||
$mappedDetails = $asn1->asn1map($decoded[0], $asnSubjectPublicKeyInfo);
|
||||
|
||||
return isset($mappedDetails['algorithm']['id-ecSigType']) ? $this->getSupportedECDSACurve() === $mappedDetails['algorithm']['id-ecSigType'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedPrivateKeyType()
|
||||
{
|
||||
return defined('OPENSSL_KEYTYPE_EC') ? OPENSSL_KEYTYPE_EC : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ECDSA curve supported in this signer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getSupportedECDSACurve();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the ECDSA algorithm, after hashing it.
|
||||
*/
|
||||
class ES256 extends ECDSA
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return version_compare(phpversion(), '5.4.8', '<') ? 'SHA256' : OPENSSL_ALGO_SHA256;
|
||||
}
|
||||
|
||||
protected function getSupportedECDSACurve()
|
||||
{
|
||||
return '1.2.840.10045.3.1.7';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the ECDSA algorithm, after hashing it.
|
||||
*/
|
||||
class ES384 extends ECDSA
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return version_compare(phpversion(), '5.4.8', '<') ? 'SHA384' : OPENSSL_ALGO_SHA384;
|
||||
}
|
||||
|
||||
protected function getSupportedECDSACurve()
|
||||
{
|
||||
return '1.3.132.0.34';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the ECDSA algorithm, after hashing it.
|
||||
*/
|
||||
class ES512 extends ECDSA
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return version_compare(phpversion(), '5.4.8', '<') ? 'SHA512' : OPENSSL_ALGO_SHA512;
|
||||
}
|
||||
|
||||
protected function getSupportedECDSACurve()
|
||||
{
|
||||
return '1.3.132.0.35';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
use Namshi\JOSE\Signer\SignerInterface;
|
||||
|
||||
/**
|
||||
* This class is the base of all HMAC Signers.
|
||||
*/
|
||||
abstract class HMAC implements SignerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sign($input, $key)
|
||||
{
|
||||
return hash_hmac($this->getHashingAlgorithm(), $input, (string) $key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* To prevent timing attacks we are using PHP 5.6 native function hash_equals,
|
||||
* in case of PHP < 5.6 a timing safe equals comparison function.
|
||||
*
|
||||
* more info here:
|
||||
* http://blog.ircmaxell.com/2014/11/its-all-about-time.html
|
||||
*
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function verify($key, $signature, $input)
|
||||
{
|
||||
$signedInput = $this->sign($input, $key);
|
||||
|
||||
return $this->timingSafeEquals($signedInput, $signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* A timing safe equals comparison.
|
||||
*
|
||||
* @param string $signature the internal signature to be checked
|
||||
* @param string $signedInput The signed input submitted value
|
||||
*
|
||||
* @return bool true if the two strings are identical.
|
||||
*/
|
||||
public function timingSafeEquals($known, $input)
|
||||
{
|
||||
return hash_equals($known, $input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashing algorithm used in this signer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getHashingAlgorithm();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* HMAC Signer using SHA-256.
|
||||
*/
|
||||
class HS256 extends HMAC
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return 'sha256';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* HMAC Signer using SHA-384.
|
||||
*/
|
||||
class HS384 extends HMAC
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return 'sha384';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* HMAC Signer using SHA-512.
|
||||
*/
|
||||
class HS512 extends HMAC
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return 'sha512';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
use Namshi\JOSE\Signer\SignerInterface;
|
||||
|
||||
/**
|
||||
* None Signer.
|
||||
*/
|
||||
class None implements SignerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sign($input, $key)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function verify($key, $signature, $input)
|
||||
{
|
||||
return $signature === '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Namshi\JOSE\Signer\SignerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the a public key algorithm, after hashing it.
|
||||
*/
|
||||
abstract class PublicKey implements SignerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sign($input, $key, $password = null)
|
||||
{
|
||||
$keyResource = $this->getKeyResource($key, $password);
|
||||
if (!$this->supportsKey($keyResource)) {
|
||||
throw new InvalidArgumentException('Invalid key supplied.');
|
||||
}
|
||||
|
||||
$signature = null;
|
||||
openssl_sign($input, $signature, $keyResource, $this->getHashingAlgorithm());
|
||||
|
||||
return $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function verify($key, $signature, $input)
|
||||
{
|
||||
$keyResource = $this->getKeyResource($key);
|
||||
if (!$this->supportsKey($keyResource)) {
|
||||
throw new InvalidArgumentException('Invalid key supplied.');
|
||||
}
|
||||
|
||||
$result = openssl_verify($input, $signature, $keyResource, $this->getHashingAlgorithm());
|
||||
|
||||
if ($result === -1) {
|
||||
throw new RuntimeException('Unknown error during verification.');
|
||||
}
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string representation of a key into an OpenSSL resource.
|
||||
*
|
||||
* @param string|resource $key
|
||||
* @param string $password
|
||||
*
|
||||
* @return resource OpenSSL key resource
|
||||
*/
|
||||
protected function getKeyResource($key, $password = null)
|
||||
{
|
||||
if (is_resource($key)) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$resource = openssl_pkey_get_public($key) ?: openssl_pkey_get_private($key, $password);
|
||||
if ($resource === false) {
|
||||
throw new RuntimeException('Could not read key resource: ' . openssl_error_string());
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the key is supported by this signer.
|
||||
*
|
||||
* @param resource $key Public or private key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function supportsKey($key)
|
||||
{
|
||||
// OpenSSL 0.9.8+
|
||||
$keyDetails = openssl_pkey_get_details($key);
|
||||
|
||||
return isset($keyDetails['type']) ? $this->getSupportedPrivateKeyType() === $keyDetails['type'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashing algorithm used in this signer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getHashingAlgorithm();
|
||||
|
||||
/**
|
||||
* Returns the private key type supported in this signer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getSupportedPrivateKeyType();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the RSA algorithm, after hashing it.
|
||||
*/
|
||||
class RS256 extends RSA
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return version_compare(phpversion(), '5.4.8', '<') ? 'SHA256' : OPENSSL_ALGO_SHA256;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the RSA algorithm, after hashing it.
|
||||
*/
|
||||
class RS384 extends RSA
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return version_compare(phpversion(), '5.4.8', '<') ? 'SHA384' : OPENSSL_ALGO_SHA384;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the RSA algorithm, after hashing it.
|
||||
*/
|
||||
class RS512 extends RSA
|
||||
{
|
||||
public function getHashingAlgorithm()
|
||||
{
|
||||
return version_compare(phpversion(), '5.4.8', '<') ? 'SHA512' : OPENSSL_ALGO_SHA512;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\OpenSSL;
|
||||
|
||||
/**
|
||||
* Class responsible to sign inputs with the a RSA algorithm, after hashing it.
|
||||
*/
|
||||
abstract class RSA extends PublicKey
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedPrivateKeyType()
|
||||
{
|
||||
return defined('OPENSSL_KEYTYPE_RSA') ? OPENSSL_KEYTYPE_RSA : false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\SecLib;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Namshi\JOSE\Signer\SignerInterface;
|
||||
|
||||
abstract class PublicKey implements SignerInterface
|
||||
{
|
||||
protected $encryptionAlgorithm;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sign($input, $key, $password = null)
|
||||
{
|
||||
if ($password) {
|
||||
$this->encryptionAlgorithm->setPassword($password);
|
||||
}
|
||||
|
||||
if (!$this->encryptionAlgorithm->loadKey($key)) {
|
||||
throw new InvalidArgumentException('Invalid key supplied.');
|
||||
}
|
||||
|
||||
return $this->encryptionAlgorithm->sign($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function verify($key, $signature, $input)
|
||||
{
|
||||
if (!$this->encryptionAlgorithm->loadKey($key)) {
|
||||
throw new InvalidArgumentException('Invalid key supplied.');
|
||||
}
|
||||
|
||||
return $this->encryptionAlgorithm->verify($input, $signature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\SecLib;
|
||||
|
||||
class RS256 extends RSA
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->encryptionAlgorithm->setHash('sha256');
|
||||
$this->encryptionAlgorithm->setMGFHash('sha256');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\SecLib;
|
||||
|
||||
class RS384 extends RSA
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->encryptionAlgorithm->setHash('sha384');
|
||||
$this->encryptionAlgorithm->setMGFHash('sha384');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\SecLib;
|
||||
|
||||
class RS512 extends RSA
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->encryptionAlgorithm->setHash('sha512');
|
||||
$this->encryptionAlgorithm->setMGFHash('sha512');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer\SecLib;
|
||||
|
||||
use phpseclib\Crypt\RSA as CryptRSA;
|
||||
|
||||
class RSA extends PublicKey
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->encryptionAlgorithm = new CryptRSA();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE\Signer;
|
||||
|
||||
interface SignerInterface
|
||||
{
|
||||
/**
|
||||
* Signs the $input with the $key, after hashing it.
|
||||
*
|
||||
* @param string $input
|
||||
* @param resource|string $key
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function sign($input, $key);
|
||||
|
||||
/**
|
||||
* Verifies that the input correspond to the $signature decrypted with the
|
||||
* given public $key.
|
||||
*
|
||||
* @param resource|string $key
|
||||
* @param string $signature
|
||||
* @param string $input
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verify($key, $signature, $input);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Namshi\JOSE;
|
||||
|
||||
/**
|
||||
* Class providing an easy to use JWS implementation.
|
||||
*/
|
||||
class SimpleJWS extends JWS
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $header An associative array of headers. The value can be any type accepted by json_encode or a JSON serializable object
|
||||
*
|
||||
* @see http://php.net/manual/en/function.json-encode.php
|
||||
* @see http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @see https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
|
||||
* }
|
||||
*/
|
||||
public function __construct($header = array(), $encryptionEngine = 'OpenSSL')
|
||||
{
|
||||
if (!isset($header['typ'])) {
|
||||
$header['typ'] = 'JWS';
|
||||
}
|
||||
parent::__construct($header, $encryptionEngine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the payload of the current JWS with an issued at value in the 'iat' property.
|
||||
*
|
||||
* @param array $payload
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPayload(array $payload)
|
||||
{
|
||||
if (!isset($payload['iat'])) {
|
||||
$payload['iat'] = time();
|
||||
}
|
||||
|
||||
return parent::setPayload($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the JWS has been signed with a valid private key by verifying it with a public $key
|
||||
* and the token is not expired.
|
||||
*
|
||||
* @param resource|string $key
|
||||
* @param string $algo The algorithms this JWS should be signed with. Use it if you want to restrict which algorithms you want to allow to be validated.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($key, $algo = null)
|
||||
{
|
||||
return $this->verify($key, $algo) && !$this->isExpired();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the token is expired based on the 'exp' value.
|
||||
*it.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
$payload = $this->getPayload();
|
||||
|
||||
if (isset($payload['exp'])) {
|
||||
$now = new \DateTime('now');
|
||||
|
||||
if (is_int($payload['exp'])) {
|
||||
return ($now->getTimestamp() - $payload['exp']) > 0;
|
||||
}
|
||||
|
||||
if (is_numeric($payload['exp'])) {
|
||||
return ($now->format('U') - $payload['exp']) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user