Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support JWK #32

Open
fproject opened this issue Sep 9, 2015 · 33 comments
Open

Support JWK #32

fproject opened this issue Sep 9, 2015 · 33 comments

Comments

@fproject
Copy link

fproject commented Sep 9, 2015

Could you add support for JWK?

http://tools.ietf.org/html/draft-ietf-jose-json-web-key-02

@nguyenbs
Copy link

nguyenbs commented Sep 9, 2015

+1

@lcobucci
Copy link
Owner

Sure! I'm just fixing the issue #30 and will plan the best way to support it.

@lcobucci lcobucci added this to the 4.0.0 milestone Sep 10, 2015
@lcobucci
Copy link
Owner

I'm starting this effort on lcobucci/jwk#1 and I'll change this lib to use JWK to sign this. Any help is welcome!

@oasisguy
Copy link

Can I use this library in PHP 5.3.3?

@lcobucci
Copy link
Owner

@EugeneHan no, this lib can just be used with PHP 5.5+ (since there's no support for earlier versions http://php.net/supported-versions.php).

Our next major release will only support PHP 7.

@scaytrase
Copy link

@lcobucci would you support previous version after migrating on php7

@lcobucci
Copy link
Owner

lcobucci commented Feb 7, 2016

@scaytrase the plan is that we'll keep the new features on v4.x+ and security/bug fixes on both v3.x and v4.x+.

@benjy
Copy link

benjy commented Aug 10, 2017

This would be handy, AWS Cognito provides a RSA JWK and to verify them you need to convert to PEM first.

@lukos
Copy link

lukos commented May 29, 2018

Is there any more news on this feature? Currently, this appears to best PHP library for features and small size but it is a pain to write my own code to convert JWKs to PEM. I'm happy to help if there are specific jobs to do, I don't believe it is hard to convert Base64 JWK into a raw public key.

@shadowhand
Copy link
Contributor

shadowhand commented Jul 2, 2019

https://github.com/acodercat/php-jwk-to-pem might be helpful until JWK support lands.

@Spomky
Copy link
Contributor

Spomky commented Jul 5, 2019

@shadowhand the problem with this library is that it only supports RSA keys, not EC ones.
Moreover it will add some dependencies to your project... not really fun right?

May I suggest you to have a look at PHP console app?

curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar.pubkey
chmod +x jose.phar

./jose.phar key:convert:pkcs1 '{"kty":"EC","crv":"P-256","d":"kiNCxSbRjlAbHrEbrwVKS8vIXUh6URChrmw","x":"-wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgk","y":"rt8XDTalLMCRB5Tu9WQc2d0TOVwXXHkVDbI7cIig6r4"}'

# Will return:
# -----BEGIN EC PRIVATE KEY-----
# MHcCAQEEIJIjQsUm0Y5QGx6xG68N4GrprVrFSkvLyF1IelEQoa5soAoGCCqGSM49
# AwEHoUQDQgAE+wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgmu3xcNNqUs
# wJEHlO71ZBzZ3RM5XBdceRUNsjtwiKDqvg==
# -----END EC PRIVATE KEY-----

It also offers a lot of useful commands (key creation, analyze, keyset features...).

@shadowhand
Copy link
Contributor

shadowhand commented Jul 5, 2019

@Spomky We only need support for PEM keys during a HTTP request lifecycle, so the package I linked is better fit for our needs.

As a side node about: jwt-framework (and jose.phar) is an impressive project. Unfortunately it seems more concerned with being academically correct than useful. The docs took a long time to digest, and in our case, the gmp requirement a show stopper, as our runtime (lambda + bref) doesn't have it available.

@dimacros
Copy link

dimacros commented Aug 2, 2019

Any details about the implementation, are you still waiting or is there already a branch to support?

@sc0ttdav3y
Copy link

Just wanted to register my +1 for this feature.

I've implemented acodercat/php-jwk-to-pem in the meantime and it works fine for AWS Cognito. Perhaps you could simply consume this library as an optional dependency to add this feature more quickly?

@lcobucci If you're interested I could put together a PR for you based on using this library? Let me know if this is an approach you'd be happy with, or if you'd rather go another way.

@tarunjangra
Copy link

+1 from my side as well. JSON web key document should be the part of this library.

@bradjones1
Copy link

@lcobucci Would this be eligible for a move to 4.0.0 milestone from 5.0.0 with sponsored development?

@lcobucci
Copy link
Owner

@bradjones1 thanks for the offer. My plan is to redesign the key so that we can implement jwk without breaking BC but wanted to make it part of v4.1.

v4 requires PHP 7.4+, does that meet your needs?

@bradjones1
Copy link

@lcobucci Modern PHP at 7.4+ is no blocker on our side. Thanks very much for your work on the module and I look forward to helping.

@lcobucci lcobucci removed the BC-break label Dec 16, 2020
@Slamdunk
Copy link
Collaborator

Not a solution to JWK-to-PEM problem nor any help for JWK support, but just a mention that #605 got merged and now EdDSA signature can be used too for asymmetric signing: EdDSA keys don't need PEM conversion so they can be a nice workaround, considering also they are more secure.

@lcobucci lcobucci removed this from the 4.1.0 milestone Jan 28, 2021
@drupol
Copy link

drupol commented Feb 8, 2021

Dear @lcobucci,

What's the status of this?

Thanks!

@abbluiz
Copy link

abbluiz commented Aug 5, 2021

+1

@hallboav
Copy link

hallboav commented Aug 28, 2021

If you want to verify that a JWT has been signed using someone's private key, you can do:

composer req web-token/jwt-core
use Jose\Component\Core\JWK;
use Jose\Component\Core\Util\RSAKey;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Validation;

$key = <<<'KEY'
{
  "kty": "RSA",
  "e": "AQAB",
  "kid": "rsa1",
  "alg": "RS256",
  "n": "yKqGRQyJtqxRm_Mo2YTCCAkPS..."
}
KEY;

// web-token/jwt-core
$jwk = JWK::createFromJson($key);
$pem = RSAKey::createFromJWK($jwk)->toPEM();

// lcobucci/jwt
$token = '...';
$constraints = [
    new Validation\Constraint\SignedWith(
        new Signer\Rsa\Sha256(), 
        Signer\Key\InMemory::plainText($pem)
    ),
];
$validator = new Validation\Validator();
$validator->assert($token, ...$constraints);

...

🤝

@patcon
Copy link

patcon commented May 16, 2022

@shadowhand in #32 (comment):

The docs took a long time to digest, and in our case, the gmp requirement a show stopper, as our runtime (lambda + bref) doesn't have it available.

As I understand @Spomky, gmp extension is now optional in the web-token/jwt-core library you suggested above, yes?

From https://web-token.spomky-labs.com/introduction/pre-requisite:

Please note that cypher operation may be really slow, especially RSA functions. It is highly recommended to enable GMP or BCMath.

But presumably this will be no slower than a library that avoids either of those deps, right?

@kynx
Copy link

kynx commented Aug 9, 2022

@lcobucci I've been having a look at this after the discussion on #mezzio slack.

From what I can see, the shortest path would be a Validation\Constraint\SignedWithJwk that consumes a JwkSet containing the parsed key(s). I think this can be done without any BC breaks. The assert() method would get the kid from the token, construct a "real" SignedWith using the PEM from the matching JWK, then call assert() on that.

Does that make sense to you? If there's a better approach let me know... I sort of feel like the existing SignedWith should be handling it, but I can't see a way without a custom Signer, which would require further changes to the code.

@sicet7
Copy link

sicet7 commented Jul 4, 2023

For those wondering how to achieve something like @hallboav showcased, but with phpseclib instead.
Here is some sample code that my colleague and I wrote to try this out:
https://gist.github.com/sicet7/115bfe433e9f6f68d892c5966ed33ebb

@coffe4u
Copy link

coffe4u commented Aug 24, 2023

@simonberger
Copy link

This feature seem not to be planned anymore?

I tested the solution @hallboav provided, it works good, thank you for that. Unfortunately it has yet two flaws which makes it not really an acceptable solution (to me).

  1. It adds another dependency for a relatively neat technology like JWT which could be unmaintained in the future.
  2. A lot worse: The RsaKey class is marked internal

I picked lcobucci/jwt because it is used in league/oauth2-server which is planned to be used for oauth as well, but for those like me needing a bigger feature set around jwt the webtoken library collection seem unfortunately the better fit.

@Ocramius
Copy link
Collaborator

for a relatively neat technology like JWT which could be unmaintained in the future.

???????????? What?

@simonberger
Copy link

for a relatively neat technology like JWT which could be unmaintained in the future.

???????????? What?

Where exactly is your big question mark? I was talking about the two libraries the solution is using which increases the likelihood one could be unmaintained. Surely not JWT if that was the issue here. 😆

@lcobucci
Copy link
Owner

lcobucci commented Aug 29, 2023

Hello @simonberger, thanks for sharing your thoughts.

It's very unlikely that this library will get unmaintained in the future.

You see, I've implemented this library to satisfy the needs I had. Opening it up as OSS was mainly to share that with the rest of the world.

All the features we ended up having were either centred around my needs or the community's (who valiantly pushed change requests over years).

I've never missed JWK support, therefore it was never logical to spend my time implementing it. Especially after I became a father and started having less and less time for myself.

I see four possibilities here:

  1. Someone who needs JWK, uses their time to implement it in a way that's compatible with the existing API - and keeps external deps at a minimum. I'm more than happy to provide guidence for them
  2. Someone's company sponsors the development of the feature. I'd gladly hop in a call and further explain this
  3. People follow the advice given here to integrate different tools and have the convertion between JWK and openssl/sodium keys
  4. People switch to a different solution that meet their requirements - after all, we never intended to be the one library to rule them all.

Feel free to send us a PR or an email if you decide to follow paths 1 or 2 👍

@simonberger
Copy link

@lcobucci Thank you very much for your answer. Understandable from your perspective and a good conclusion comment why it won't come for now without contribution in any way.

To contribute from my side I have unfortunately too low knowledge of the topic and underlying technology as well as lack of time to grow that.

@miken32
Copy link

miken32 commented Sep 12, 2024

Just a note to say I'm using this library to build PEM public keys for verification of signatures. It's quite small and has no dependencies. Sample code that extracts an EC-based JWK, converts it to PEM, and verifies the JWT was signed by it.

<?php

use FreeDSx\Asn1\Asn1;
use FreeDSx\Asn1\Encoders;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Eddsa;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Token\Parser;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Validator;
use RadiusOne\AcmeServer\Enums\ErrorType;
use RadiusOne\AcmeServer\Exceptions\AcmeException;
use Symfony\Component\HttpFoundation\Response;

$protected = 'eyJub25jZSI6ICI2d3JsT0owVDlJOTFLVUhXVzJNZTV5ZldnYWRuSXNSRnpZUHI5el9PYnY1WENiTU1ZVEEiLCAidXJsIjogImh0dHBzOi8vYWNtZS1zdGFnaW5nLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvbmV3LWFjY3QiLCAiYWxnIjogIkVTMjU2IiwgImp3ayI6IHsiY3J2IjogIlAtMjU2IiwgImt0eSI6ICJFQyIsICJ4IjogIllfamdiYUlzV1hRTkFtSzlMYVhJY3FuZ3Jsc2VrbFdGbkRWRFc1TF9kWEEiLCAieSI6ICJoV3RVczlpRnZEMm1iNHZ1X2VLYkxJazgycHZnQWVxbG1PVmk0VVRiUFljIn19';
$payload = 'eyJ0ZXJtc09mU2VydmljZUFncmVlZCI6IHRydWV9';
$signature = 'mywGCQ9jHEWMJWAQUrLQ_rbSpQMYsroy-donI3lfkBKYdzpNLo8wjOgGPOhWa-SGQoIuRD-6RdI1tRbN3olFKA';
$jwt = implode('.', [$protected, $payload, $signature]);

$parser = new Parser(new JoseEncoder());
$validator = new Validator();
$token = $parser->parse($jwt);
$jwk = $token->headers()->get('jwk');

// convert JWK to PEM
$key_oid = match($jwk['crv']) {
    'P-256' => '1.2.840.10045.3.1.7',
    'secp256k1' => '1.3.132.0.10',
    'P-384' => '1.3.132.0.34',
    'P-521' => '1.3.132.0.35',
};
$x = sodium_base642bin($jwk['x'], SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING);
$y = sodium_base642bin($jwk['y'], SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING);
$key = Asn1::sequence(
    Asn1::sequence(Asn1::oid('1.2.840.10045.2.1'), Asn1::oid($key_oid)),
    // 04 indicates an uncompressed public key follows
    Asn1::bitStringFromBinary(chr(4) . $x . $y),
);
$der = Encoders::der()->encode($key);

$pem = sprintf(
    '-----BEGIN PUBLIC KEY-----%s%s%s-----END PUBLIC KEY-----',
    PHP_EOL,
    trim(chunk_split(base64_encode($der), 64, PHP_EOL)),
    PHP_EOL
);

// validate signature using PEM cert
$alg = match($token->headers()->get('alg')) {
    'ES256' => new Ecdsa\Sha256(),
    'ES384' => new Ecdsa\Sha384(),
    'ES512' => new Ecdsa\Sha512(),
    'EdDSA' => new Eddsa(),
    'RS256' => new Rsa\Sha256(),
    'RS384' => new Rsa\Sha384(),
    'RS512' => new Rsa\Sha512(),
    default => throw new AcmeException(ErrorType::badSignatureAlgorithm, Response::HTTP_BAD_REQUEST),
};
$validator->assert(
    $token,
    new SignedWith($alg, InMemory::plainText($pem))
);

I'm in way over my head here, so not sure how robust or reliable the code is. But it's working well for me and parsing the JWK for conversion to PEM is only a handful of lines; RSA shouldn't add much.

@SvenRtbg
Copy link
Collaborator

SvenRtbg commented Sep 13, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests