I am creating a JWT Token using a private key in PHP. For this I am using the OpenSSL library. Before anything I will share my code :
PHP
$header = [ self::ALG => self::RS256, self::TYP => self::JWT ]; $payload = [ "iss" => $this->getClientID(), "iat" => time(), "exp" => time()+999 ]; $header = base64_encode(json_encode($header)); $payload = base64_encode(json_encode($payload)); $token = $header.".".$payload; $pkey = openssl_pkey_get_private($this->getPrivateKey(), $this->getPassPhrase()); openssl_sign($token, $signature, $pkey, 'sha256'); //no algorithm specifically for rs256 $sign = base64_encode($signature); return $token.".".$sign;
So, this JWT Token will be used for authentication in a server which I am trying to hit. But I get the response from the external server as a Bad Request
, problem happening in my JWT token creation.
With same credentials, I tried in javascript using the library jsrsasign
and it then gives me the correct response.
JAVASCRIPT
// Header var oHeader = { alg: 'RS256', typ: 'JWT' }; // Payload var oPayload = {}; var tNow = rs.KJUR.jws.IntDate.get('now'); var tEnd = tNow + 1000; oPayload.iss = "playground.pizza"; oPayload.iat = tNow; oPayload.exp = tEnd; var sHeader = JSON.stringify(oHeader); var sPayload = JSON.stringify(oPayload); var pkey = "my private key" //I replaced all the new line with n here and had in one line var prvKey = rs.KEYUTIL.getKey(pkey, "my_pass_phrase"); var sJWT = rs.KJUR.jws.JWS.sign("RS256", sHeader, sPayload, prvKey); ultraSecureToken = sJWT;
One difference which I can clearly see is that for the signature generation function
in the php end I am passing sha256
as the algorithm and in the JavaScript RS256
is passed.
I read about how sha256 is just a hashing algorithm and RS256 is used for encoding, but in all the libraries I found in php, they were internally passing sha256
only in the openssl_sign
function. And openssl_sign
doesn’t have RS256 as an algorithm parameter.
So, firstly is there anywhere where I went wrong.
Secondly, is there a way we can generate a signature using RS256 in php.
PS : I am looking for solution in php.
Advertisement
Answer
The return-value of
KJUR.jws.JWS.sign
consists of three portions separated by a dot. The first part is the Base64url-encoded JSON-stringsHeader
, the second part is the Base64url-encoded JSON-stringsPayload
and the third part is the Base64url-encoded signature. The data to be signed consist of the first two portions including the dot separating the two portions. RS256 means that SHA256 and RSA with RSASSA-PKCS1-v1_5 padding is used for the signature. This can also be easily verified online, e.g. here, wherebySHA256withRSA
is to be selected as the algorithm.openssl_sign
also uses RSA with RSASSA-PKCS1-v1_5 padding and therefore creates the same signature with SHA256, assuming the same key and the same data to be signed are applied.jsrsasign
uses Base64 url-encoding (RFC4648, sect. 5), while PHP (or more precisely thebase64_encode
-method) uses standard Base64-encoding (RFC4648, sect. 4), which most likely is one cause of the issue. This means that the encoding in the current PHP-code must be changed to Base64url, e.g. here.Of course, the underlying JSON-strings in the PHP-code (
$header
,$payload
and$token
) must also be identical to their counterparts in the JavaScript-code, otherwise the signature will differ. Since the PHP-code is incomplete, this cannot be checked and could be another cause of the problem.