lcobucci / jwt

A simple library to work with JSON Web Token and JSON Web Signature
https://lcobucci-jwt.readthedocs.io/en/stable/
BSD 3-Clause "New" or "Revised" License
7.29k stars 600 forks source link

Enable integration with third-party signing #93

Closed udf2457 closed 8 years ago

udf2457 commented 8 years ago

For those people who may use cloud-based security infrastructure (e.g. Azure Keyvault HSM, Amazon AWS HSM), there is no way to integrate signing with lcobucci/jwt because, for example, your code presently requires people to supply the path to the private key on the local filesystem.

For avoidance of doubt, I am not saying that you need to code all the API communications ! I am just saying, give me an easy way to (a) get the "string to sign" and (b) supply the signed value back to lcobucci/jwt .... the "black-box" magic in the middle can be done by people's code.

Hope this makes sense.

geggleto commented 8 years ago

@udf2457 so all we need to do is change the Key class to accept a string that you can generate yourself ?

udf2457 commented 8 years ago

@geggleto Sounds about right.

(I'm guessing the function to retrieve payload/string to sign is already marked public, if not it will need to be).

lcobucci commented 8 years ago

As you can see on https://github.com/lcobucci/jwt/blob/3.1/src/Signer/Key.php#L45 we already allow strings on constructors, so you can do the "black-box" magic and create a new Key.

The file:// is not expected but was just added to don't break BC after the Key class was introduced.

geggleto commented 8 years ago

@udf2457

For branch 2.1

if you look at... https://github.com/lcobucci/jwt/blob/2.1/src/Builder.php#L213-L223 You can see the signature of sign accepts a (string) for the key...

Same thing for verifying... https://github.com/lcobucci/jwt/blob/2.1/src/Token.php#L136-L143

lcobucci commented 8 years ago

@geggleto v2 was discontinued and should not be used due to security reasons.

lcobucci commented 8 years ago

@udf2457 for now you can fetch your string while creating the Key object and after we have #32 (on v4) you'll be able to user external URLs to create tokens.

geggleto commented 8 years ago

@lcobucci ah okay, noted.

udf2457 commented 8 years ago

@lcobucci Am a little confused about your reference to https://github.com/lcobucci/jwt/blob/3.1/src/Signer/Key.php#L45, isn't that used for providing a Key not a signed string ?

geggleto commented 8 years ago

@udf2457 if your Content does not contain the string file:// it is treated as a string and will not load the file.

lcobucci commented 8 years ago

@udf2457 exactly as @geggleto explained 😉

// You can create:
$key = new Key('file:///test/private_key.pem');
// or:
$key = new Key(file_get_contents('/test/private_key.pem'));
// or: (with the right line endings)
$key = new Key('-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDdlatRjRjogo3Wojg...');

If you need more power you could even do:

final class KeyVaultObject
{
     public function __toString():string
     {
         // Your black magic here
         return 'black magic result';
     }
}

$key = new Key(new KeyVaultObject('identifier'));
udf2457 commented 8 years ago

@geggleto @lcobucci So, just to be sure, based on the example code on your homepage that this sort of thing will work as intended ?

$get_the_string_to_sign= $jwt_instance->getPayload(); // or whatever the function 
//
// send stuff to azure.....
//
$signer = $config->getSigner();
$azureResult = "the_result_signature_text_after_I_sent_stringtosign_to_azure_api" ;
$token = $config->createBuilder()
                ->setIssuer('http://example.com') // Configures the issuer (iss claim)
                // etc. etc.
                ->sign($signer,  $azureResult ) // creates a signature using your private key
                ->getToken(); // Retrieves the generated token
udf2457 commented 8 years ago

// You can create: $key = new Key('file:///test/private_key.pem'); // or: $key = new Key(file_get_contents('/test/private_key.pem')); // or: (with the right line endings) $key = new Key('-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQDdlatRjRjogo3Wojg...');

Yes, but surely this is exactly the problem I'm telling you ? ;-)

The cloudy stuff is HSM.... i.e. once in the HSM, the private key never emerges. You can only ever ask it to "sign,decrypt,verify,encrypt" .... the private key stays locked-up inside the HSM at all times, the only key you can view is the public key.

geggleto commented 8 years ago

I ended up writing my own wrapper around it... https://github.com/geggleto/securejwt required libsodium tho

udf2457 commented 8 years ago

I ended up writing my own wrapper around it... required libsodium tho

Alright, this is getting silly now.

I've got nothing against the great libsodium, but surely I don't need it to be able to concatenate a ready-made signature onto a JWT token (I'm no JWT guru but I'm guessing that's how it works !)

lcobucci commented 8 years ago

@udf2457 ok so your problem is not about with the Key creation at all. You actually need a customized Signer.

For that you just need to implement the Signer interface (https://github.com/lcobucci/jwt/blob/3.1/src/Signer.php) and do your black magic there. The BaseSigner (https://github.com/lcobucci/jwt/blob/3.1/src/Signer/BaseSigner.php) can be used to help you.

I have no intention on providing customized signers in this package but you can create one that has this lib as dependency and adds a HSM signer (which can be useful to others). I'll gladly put your package as suggestion of this lib.

udf2457 commented 8 years ago

@lcobucci ok, I'll take a look at the interfaces suggested.

lcobucci commented 8 years ago

@udf2457 don't forget to use the stable version as dependency as master is meant for the next major release and it's API WILL CHANGE.

lcobucci commented 8 years ago

Will mark this as wontfix since is out of scope of this package, but please create your signer on a new repository and mention me on a PR if need any help 😄

omitobi commented 12 months ago

Does anyone have a solution to this particular issue? I am currently in the same situation.

lcobucci commented 12 months ago

Does anyone have a solution to this particular issue? I am currently in the same situation.

@omitobi delegating the signing process to an external component can be achieved by implementing a custom algorithm (signer): https://lcobucci-jwt.readthedocs.io/en/stable/extending-the-library/#signer

If this doesn't clarify it enough, please open a discussion (not issue) asking a more detailed question 👍

fredericgboutin-yapla commented 7 months ago

I mean, @omitobi is kind of right. The original issue is still valid: Enable integration with third-party signing

Yes, there is a workaround so you can hack you way. But I'm in a situation were I would like to leverage AWS KMS. Obviously I would have preferred if someone else had already developed and tested that support.

The point is, lcobucci/jwt needs something more integrated or supported or "pluggable" so we can sign JWT with different services were we don't have access to the private key by design but only an API.

SvenRtbg commented 7 months ago

Reviving an issue created and closed in 2016, targeting a version that isn't valid today, is kinda "meh".

But anyways: We have the Signer interface:

interface Signer
{
    public function algorithmId(): string;
    public function sign(string $payload, Key $key): string;
    public function verify(string $expected, string $payload, Key $key): bool;
}

We have the Key interface:

interface Key
{
    public function contents(): string;
    public function passphrase(): string;
}

We have the InMemory implementation of Key as a provided way to send a key into the signing mechanism of your choice.

What exactly is missing that could be considered general-purpose (as to warrant implementing this here instead of a separate add-on library) that you would need for any cloud operation?

Have you considered implementing a bit of glue code to utilize the sign() method of the AWS client library?

Ocramius commented 7 months ago

What @SvenRtbg said: locking.