handle RSA key passphrase

This commit is contained in:
Julián Gutiérrez
2016-03-28 16:42:34 +02:00
parent 9533595394
commit 197657f2b9
17 changed files with 223 additions and 146 deletions

View File

@@ -43,7 +43,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
try {
// Attempt to parse and validate the JWT
$token = (new Parser())->parse($jwt);
if ($token->verify(new Sha256(), $this->publicKeyPath) === false) {
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
throw OAuthServerException::accessDenied('Access token could not be verified');
}

62
src/CryptKey.php Normal file
View File

@@ -0,0 +1,62 @@
<?php
/**
* Cryptography key holder.
*
* @author Julián Gutiérrez <juliangut@gmail.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
class CryptKey
{
/**
* @var string
*/
protected $keyPath;
/**
* @var string
*/
protected $passPhrase;
/**
* @param string $keyPath
* @param null|string $passPhrase
*/
public function __construct($keyPath, $passPhrase = null)
{
if (strpos($keyPath, 'file://') !== 0) {
$keyPath = 'file://' . $keyPath;
}
if (!file_exists($keyPath) || !is_readable($keyPath)) {
throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
}
$this->keyPath = $keyPath;
$this->passPhrase = $passPhrase;
}
/**
* Retrieve key path.
*
* @return string
*/
public function getKeyPath()
{
return $this->keyPath;
}
/**
* Retrieve key pass phrase.
*
* @return null|string
*/
public function getPassPhrase()
{
return $this->passPhrase;
}
}

View File

@@ -13,41 +13,33 @@ namespace League\OAuth2\Server;
trait CryptTrait
{
/**
* @var string
* @var \League\OAuth2\Server\CryptKey
*/
protected $privateKeyPath;
protected $privateKey;
/**
* @var string
* @var \League\OAuth2\Server\CryptKey
*/
protected $publicKeyPath;
protected $publicKey;
/**
* Set path to private key.
*
* @param string $privateKeyPath
* @param \League\OAuth2\Server\CryptKey $privateKey
*/
public function setPrivateKeyPath($privateKeyPath)
public function setPrivateKey(CryptKey $privateKey)
{
if (strpos($privateKeyPath, 'file://') !== 0) {
$privateKeyPath = 'file://' . $privateKeyPath;
}
$this->privateKeyPath = $privateKeyPath;
$this->privateKey = $privateKey;
}
/**
* Set path to public key.
*
* @param string $publicKeyPath
* @param \League\OAuth2\Server\CryptKey $publicKey
*/
public function setPublicKeyPath($publicKeyPath)
public function setPublicKey(CryptKey $publicKey)
{
if (strpos($publicKeyPath, 'file://') !== 0) {
$publicKeyPath = 'file://' . $publicKeyPath;
}
$this->publicKeyPath = $publicKeyPath;
$this->publicKey = $publicKey;
}
/**
@@ -59,10 +51,12 @@ trait CryptTrait
*/
protected function encrypt($unencryptedData)
{
$privateKey = openssl_pkey_get_private($this->privateKeyPath);
$privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase());
$privateKeyDetails = @openssl_pkey_get_details($privateKey);
if ($privateKeyDetails === null) {
throw new \LogicException(sprintf('Could not get details of private key: %s', $this->privateKeyPath));
throw new \LogicException(
sprintf('Could not get details of private key: %s', $this->privateKey->getKeyPath())
);
}
$chunkSize = ceil($privateKeyDetails['bits'] / 8) - 11;
@@ -78,7 +72,7 @@ trait CryptTrait
}
$output .= $encrypted;
}
openssl_free_key($privateKey);
openssl_pkey_free($privateKey);
return base64_encode($output);
}
@@ -94,10 +88,12 @@ trait CryptTrait
*/
protected function decrypt($encryptedData)
{
$publicKey = openssl_pkey_get_public($this->publicKeyPath);
$publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath());
$publicKeyDetails = @openssl_pkey_get_details($publicKey);
if ($publicKeyDetails === null) {
throw new \LogicException(sprintf('Could not get details of public key: %s', $this->publicKeyPath));
throw new \LogicException(
sprintf('Could not get details of public key: %s', $this->publicKey->getKeyPath())
);
}
$chunkSize = ceil($publicKeyDetails['bits'] / 8);
@@ -115,7 +111,7 @@ trait CryptTrait
}
$output .= $decrypted;
}
openssl_free_key($publicKey);
openssl_pkey_free($publicKey);
return $output;
}

View File

@@ -5,6 +5,7 @@ namespace League\OAuth2\Server\Entities;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
@@ -16,11 +17,11 @@ class AccessTokenEntity implements AccessTokenEntityInterface
/**
* Generate a JWT from the access token
*
* @param string $privateKeyPath
* @param \League\OAuth2\Server\CryptKey $privateKey
*
* @return string
*/
public function convertToJWT($privateKeyPath)
public function convertToJWT(CryptKey $privateKey)
{
return (new Builder())
->setAudience($this->getClient()->getIdentifier())
@@ -30,7 +31,7 @@ class AccessTokenEntity implements AccessTokenEntityInterface
->setExpiration($this->getExpiryDateTime()->getTimestamp())
->setSubject($this->getUserIdentifier())
->set('scopes', $this->getScopes())
->sign(new Sha256(), new Key($privateKeyPath))
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
->getToken();
}
}

View File

@@ -2,14 +2,16 @@
namespace League\OAuth2\Server\Entities\Interfaces;
use League\OAuth2\Server\CryptKey;
interface AccessTokenEntityInterface extends TokenInterface
{
/**
* Generate a JWT from the access token
*
* @param string $privateKeyPath
* @param \League\OAuth2\Server\CryptKey $privateKey
*
* @return string
*/
public function convertToJWT($privateKeyPath);
public function convertToJWT(CryptKey $privateKey);
}

View File

@@ -11,6 +11,7 @@
namespace League\OAuth2\Server\Grant;
use League\Event\EmitterAwareInterface;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
@@ -89,14 +90,14 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* Set the path to the private key.
*
* @param string $privateKeyPath
* @param \League\OAuth2\Server\CryptKey $privateKey
*/
public function setPrivateKeyPath($privateKeyPath);
public function setPrivateKey(CryptKey $privateKey);
/**
* Set the path to the public key.
*
* @param string $publicKeyPath
* @param \League\OAuth2\Server\CryptKey $publicKey
*/
public function setPublicKeyPath($publicKeyPath);
public function setPublicKey(CryptKey $publicKey);
}

View File

@@ -197,7 +197,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
$scopes
);
$redirectPayload['access_token'] = (string) $accessToken->convertToJWT($this->privateKeyPath);
$redirectPayload['access_token'] = (string) $accessToken->convertToJWT($this->privateKey);
$redirectPayload['token_type'] = 'bearer';
$redirectPayload['expires_in'] = time() - $accessToken->getExpiryDateTime()->getTimestamp();

View File

@@ -22,7 +22,7 @@ class BearerTokenResponse extends AbstractResponseType
{
$expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp();
$jwtAccessToken = $this->accessToken->convertToJWT($this->privateKeyPath);
$jwtAccessToken = $this->accessToken->convertToJWT($this->privateKey);
$responseParams = [
'token_type' => 'Bearer',

View File

@@ -32,20 +32,20 @@ class Server implements EmitterAwareInterface
protected $grantTypeAccessTokenTTL = [];
/**
* @var string
* @var \League\OAuth2\Server\CryptKey
*/
protected $privateKeyPath;
protected $privateKey;
/**
* @var \League\OAuth2\Server\CryptKey
*/
protected $publicKey;
/**
* @var ResponseTypeInterface
*/
protected $responseType;
/**
* @var string
*/
private $publicKeyPath;
/**
* @var \League\OAuth2\Server\Repositories\ClientRepositoryInterface
*/
@@ -72,8 +72,8 @@ class Server implements EmitterAwareInterface
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
* @param string $privateKeyPath
* @param string $publicKeyPath
* @param \League\OAuth2\Server\CryptKey|string $privateKey
* @param \League\OAuth2\Server\CryptKey|string $publicKey
* @param null|\League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param null|\League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface $authorizationValidator
*/
@@ -81,16 +81,25 @@ class Server implements EmitterAwareInterface
ClientRepositoryInterface $clientRepository,
AccessTokenRepositoryInterface $accessTokenRepository,
ScopeRepositoryInterface $scopeRepository,
$privateKeyPath,
$publicKeyPath,
$privateKey,
$publicKey,
ResponseTypeInterface $responseType = null,
AuthorizationValidatorInterface $authorizationValidator = null
) {
$this->clientRepository = $clientRepository;
$this->accessTokenRepository = $accessTokenRepository;
$this->scopeRepository = $scopeRepository;
$this->privateKeyPath = $privateKeyPath;
$this->publicKeyPath = $publicKeyPath;
if (!$privateKey instanceof CryptKey) {
$privateKey = new CryptKey($privateKey);
}
$this->privateKey = $privateKey;
if (!$publicKey instanceof CryptKey) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
$this->responseType = $responseType;
$this->authorizationValidator = $authorizationValidator;
}
@@ -106,8 +115,8 @@ class Server implements EmitterAwareInterface
$grantType->setAccessTokenRepository($this->accessTokenRepository);
$grantType->setClientRepository($this->clientRepository);
$grantType->setScopeRepository($this->scopeRepository);
$grantType->setPrivateKeyPath($this->privateKeyPath);
$grantType->setPublicKeyPath($this->publicKeyPath);
$grantType->setPrivateKey($this->privateKey);
$grantType->setPublicKey($this->publicKey);
$grantType->setEmitter($this->getEmitter());
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
@@ -118,8 +127,8 @@ class Server implements EmitterAwareInterface
/**
* Return an access token response.
*
* @param \Psr\Http\Message\ServerRequestInterface|null $request
* @param \Psr\Http\Message\ResponseInterface|null $response
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
*
@@ -171,8 +180,7 @@ class Server implements EmitterAwareInterface
$this->responseType = new BearerTokenResponse($this->accessTokenRepository);
}
$this->responseType->setPublicKeyPath($this->publicKeyPath);
$this->responseType->setPrivateKeyPath($this->privateKeyPath);
$this->responseType->setPrivateKey($this->privateKey);
return $this->responseType;
}
@@ -186,8 +194,7 @@ class Server implements EmitterAwareInterface
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
}
$this->authorizationValidator->setPublicKeyPath($this->publicKeyPath);
$this->authorizationValidator->setPrivateKeyPath($this->privateKeyPath);
$this->authorizationValidator->setPublicKey($this->publicKey);
return $this->authorizationValidator;
}