How to create a valid JWT in PHP using RSA256 - php

I have been having similar difficulties to this question. I need to create a JWT in php. I have the following code:
define('RSA_PRIVATE', 'MIICXAIB......EWxOf9o=');
$payload = json_encode([
'sub' => 'username',
'email' => 'john#example.com',
'given_name' => 'John',
'family_name' => 'Example',
'iat' => time(),
'nonce' => '12345'
]);
$header = json_encode([
'typ' => 'JWT',
'alg' => 'RS256'
]);
$base64UrlHeader = base64UrlEncode($header);
$base64UrlPayload = base64UrlEncode($payload);
$signature = hash_hmac('sha256', "$base64UrlHeader.$base64UrlPayload", RSA_PRIVATE, true);
$base64UrlSignature = base64UrlEncode($signature);
$jwt = "$base64UrlHeader.$base64UrlPayload.$base64UrlSignature";
echo($jwt);
function base64UrlEncode($text)
{
return str_replace(
['+', '/', '='],
['-', '_', ''],
base64_encode($text)
);
}
Whenever I attempt to validate with the third party that needs my JWT it comes back with a message telling me
Signature validation failed: rsa signature did not match
When I attempt to validate using the JWT debugger, it too says it is invalid.
Am I missing something here? Like the previous question I have only ever seen examples where people are using small secrets and not private RSA keys.

Following the answer by #Michal Trojanowski I went on to use openssl_sign. However this did not work when I had the key as a variable in my code. Instead, following the example on the php manual I managed to get it to work using the following adjustment:
$private_key = openssl_pkey_get_private("file:///path.to/jwtRS256.key");
openssl_sign(
"$base64UrlHeader.$base64UrlPayload",
$signature,
$private_key,
"sha256WithRSAEncryption"
);
openssl_free_key($private_key);
$base64UrlSignature = zd_base64UrlEncode($signature);
$jwt = "$base64UrlHeader.$base64UrlPayload.$base64UrlSignature";

You chose RS256 as the signature algorithm, which is an asymmetrical signing algorithm and you create the signature with a HMAC function, which is a symmetrical signing algorithm.
If you want to stick to RS256 try to follow this answer: https://stackoverflow.com/a/43313973/1712294
If you want to stick with a symmetrical algorithm change the data in your header to
$header = json_encode([
'typ' => 'JWT',
'alg' => 'HS256'
]);
The recommended way is to go with an asymmetrical algorithm.

Related

JWT encode error Expected 3 arguments. Found 2

I try to create a restful api with JWT in CI4, when I create token with code below :
$key = getenv('TOKEN_SECRET');
$payload = array(
"iat" => 1356999524,
"nbf" => 1357000000,
"uid" => $user['id'],
"email" => $user['email']
);
$token = JWT::encode($payload, $key);
Why do I get the error:
Expected 3 arguments. Found 2
The third paramter should be the algorithm you are trying to use for encoding.
For Example, HS256 is one of the algorithms.
So in your example it will be :
$token = JWT::encode($payload, $key, 'HS256');
You can refer to the following link for reference:
https://packagist.org/packages/firebase/php-jwt

Docusign PHP getting "invalid_grant: unsupported_grant_type" when trying to get token (JWT auth)

I'm trying to get the access token using the Docusign JWT authentication, but I always get:
{"error":"invalid_grant","error_description":"unsupported_grant_type"}
I double checked all the data (integration key, api username, etc) and they are fine.
I followed all the steps in the Docusign guidelines.
The only part I'm not 100% sure is when I generate the signature of the JWT token.
The documentation says:
The first two parts of the JWT are signed with your application's private key (using the RSA SHA-256 digital signature algorithm) as shown in the diagram.
This is how I'm generating the signature:
$header = [
'typ' => 'JWT',
'alg' => 'RS256'
];
$body = [
'iss' => getenv('INTEGRATION_KEY'),
'sub' => getenv('API_USERNAME'),
'iat' => time(),
'exp' => time() + 3600,
'aud' => str_replace('https://', '', getenv('AUTH_URL')),
'scope' => 'signature impersonation'
];
$signature = JWT::encode($body, file_get_contents(env('PRIVATE_KEY')), 'RS256');
$header = $this->base64url_encode(json_encode($header));
$body = $this->base64url_encode(json_encode($body));
$jwt = $header . '.' . $body . '.' . $signature;
Is that correct?
If not, and since JWT::encode expects an array as first parameter, how should I do to make it work?
This is how I'm requesting the access token:
return Http::withHeaders(
[
'Content-Type' => 'application/x-www-form-urlencoded'
]
)->post(
getenv('AUTH_URL') . '/oauth/token',
[
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt
]
);
Thanks!
Apparently Firebase JWT encode method doesn't encode a string in the right way.
I used this:
$header = $this->base64url_encode(json_encode($header));
$body = $this->base64url_encode(json_encode($body));
openssl_sign(
$header.".".$body,
$signature,
file_get_contents(env('PRIVATE_KEY_PATH')),
"sha256WithRSAEncryption"
);
and it worked.
Make sure you're using the same scopes when requesting consent and requesting the jwt token.
Thanks everyone for the help.
Creating a correct JWT token is hard. I suggest you either use the requestJWTUserToken from the PHP SDK or review its source to see how it makes the OAuth request.
I was having the same problem in a application using Laravel 6 and I managed to solve it as follows:
// the Header will not be needed as it is automatically generated
$header = [
'typ' => 'JWT',
'alg' => 'RS256'
];
$body = [
'iss' => getenv('INTEGRATION_KEY'),
'sub' => getenv('API_USERNAME'),
'iat' => time(),
'exp' => time() + 3600,
'aud' => str_replace('https://', ​​'', getenv('AUTH_URL')),
'scope' =>'signature impersonation'
];
/**
* Note that when creating the JWT, only the $body is provided,
* as the function already performs the necessary concatenations.
* in your code you put it like this:
* $jwt = $header . '.' . $body . '.' . $signature;
* which generates a hash that cannot be validated,
*/
// create the JWT
$jwt = JWT::encode($body , $privateKey, 'RS256');
// make the request
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', getenv('AUTH_URL').'/oauth/token',['query' =>[
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt,
]
]);
echo '<pre>';
print_r($response);
echo '</pre>';

JWT token generation for apple api in php

I am trying to generate JWT token for apple connect API (to get sales report) in php.
i tried this format (using firebase/php-jwt)
$Private_key = file_get_contents('AuthKey_XYZ.p8');
$Issuer_ID = 'XYZ';
$Key_ID = 'ZDJ';
$data = [
'iss' => $Issuer_ID,
'iat' => \Carbon\Carbon::now()->timestamp,
'exp' => \Carbon\Carbon::now()->addMinutes(20)->timestamp,
'aud' => "appstoreconnect-v1"
];
$jwt = JWT::encode($data, $Private_key,'HS256', $Key_ID);
And receiving 401, did i missed something or the format is
I'm pretty sure the JWT encryption algorithm you are specifying is wrong. Try setting it to 'ES256', e.g:
$jwt = JWT::encode($data, $Private_key,'ES256', $Key_ID);
from the docs under "Create the JWT header"
https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests

JWT token decode in cakephp

I am facing a problem to decode token (JWT). Here it's encoded successfully and it provides me with a token. My code code is:
$tokenData = $this->set([
'data' => [
'token' => JWT::encode([
'sub' => $user['username'],
'exp' => time() + 202200
],
Security::salt())
],
'_serialize' => ['success', 'data']
]);
It returns "token":{"token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
To decode this I used:
$JWT_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$token = JWT::decode($JWT_KEY);
dd($token);
How can I get it? specially time. Thanks in advance
if you are using firebase JWT take a look to: https://github.com/firebase/php-jwt
for me that command worked, here's a piece of my code:
$jwt= $this->request->data['_token'];
$decoded = JWT::decode($jwt, $this->pepper, array('HS256'));
//$decoded is an object with your token decoded data

Create AWS S3 presigned URL with MD5 hash using PHP SDK

I've been unable to construct an S3 presigned URL containing a MD5 hash. Here is the code I'm using:
$s3 = new Aws\S3\S3Client( ... );
$md5Hash = "YmEwMWVmNzE5MjI3YzdjNmI0ZjYzNGMxZWI1MmY1Y2U=";
$cmd = $s3->getCommand('PutObject', [
'Bucket' => $bucket,
'Key' => $key,
'Content-MD5' => $md5Hash,
'ContentType' => $contentType,
'Body' => ''
]);
$request = $s3->createPresignedRequest($cmd, '+5 minutes');
$presignedUrl = (string)$request->getUri();
The resulting presignedURL looks something like:
http://s3-external-1.amazonaws.com/{bucket}/{key}?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWC7FFD2T3NCEUNQ%2F20170503%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20170503T215723Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3000&X-Amz-Signature=ca61d80dcfee351e7ee8440a47c5007f1bc905ddc8229077abddab9c39412dc7
When I add the Content-MD5 hash to my PUT request, I get back an error indicating the MD5 header wasn't signed:
AccessDenied - There were headers present in the request which were not signed
HeadersNotSigned - content-md5
Any help appreciated!

Categories