SHA256, SHA1, SHA384 hash to JSON Web Token - jwt

Is it possible to generate a JSON Web Token from a SHA384 Hash. If it is possible please advice how it is possible.
Also please generate JSON Web Token from my SHA384 hash:
e648d207eb6a457cc3b415e5e6db38759de529051d808b5d34c679c43020a233e6b5161de2e85070127009d61e4c24c8

No, converting a hash into a JWT token is not possible and I also can't see why/for what anyone would want this.
As mentioned in the comment, that approach does't make any sense at all.
A hash doesn't contain any readable information that you could convert into anything.
You could create a token that contains a hash in the payload, if this is what you mean.
JWTs are usually signed. The SHA algorithm, e.g. HS256 (HMAC-SHA256), can be used to sign a token.
That means that a hash is calculated from the header and payload plus your secret and the hash value is then base64url encoded and attached to the token as a signature.
You can read this introduction to learn more about JWT.

Yes it is possible in Laravel version 7.
While converting jwt hash token to shal, go to EloquentUserProvider.php
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
if(sha1($plain) == $user->getAuthPassword()) {
return true;
}else {
return false;
}
// return $this->hasher->check($plain, $user->getAuthPassword());

Related

Which Key Do We Use When Verifying Google ID Tokens

We're verifying a Google ID Token on ColdFusion servers. We have everything working but one thing puzzles me:
In the instructions here Google says to use their public keys to verify the token. When we retrieve the keys, in the JSON object there are 2 of them. Whether we grab the PEM or the JWT there are 2 keys.
Example JWT:
Example PEM:
How do we know which key to use? Through testing we find that one works and we're able to decode the JWT to validate while the other doesn't. Right now we're having to try both of them to see which one works. Is there something we're missing that indicates which of these keys is the one to use?
The keys are identified by the key Id "kid":
The "kid" (key ID) parameter is used to match a specific key.
In case of the JWK, you see the kid value in the JSON and you can see the same kid values in the first column of the PEM example. Your token has a "kid" claim in the header part. Decode the header to extract the kid.
e.g.:
{
"typ":"JWT",
"alg":"RS256",
"kid":"3dd6ca2a81dc2fea8c3642431e7e296d2d75b446"
}

Why JWT Token signing key is required

JWT tokens required signing key to decode them, but in https://jwt.io/ it can be decoded without any signing key, how is this possible.
You do not need a key to open the encoding, you need a key to verify that nobody changed the contents of the JWT. In fact the string you see is just json's base64 with your information, metadata, and "signature" on all content.
The signature is the most important part of a JSON Web Token(JWT). A signature is calculated by encoding the header and payload using Base64url Encoding and concatenating them with a period separator. Which is then given to the cryptographic algorithm.
// signature algorithm
data = base64urlEncode( header ) + “.” + base64urlEncode( payload )
signature = HMAC-SHA256( data, secret_salt )
So when the header or payload changes, the signature has to calculated again. Only the Identity Provider(IdP) has the private key to calculate the signature which prevents the tampering of token.
read more:
https://medium.com/#sureshdsk/how-json-web-token-jwt-authentication-works-585c4f076033
https://jwt.io/introduction/
https://www.youtube.com/watch?v=7Q17ubqLfaM

Are CSRF tokens base64url encoded?

I'm working on an application that parses a CSRF token from a cookie header. I'd like to know whether CSRF tokens are base64 encoded with URL-safe characters (cf. https://simplycalc.com/base64url-encode.php) so that I can match them with the regular expression
[.a-zA-Z0-9_-]+
I was able to find documentation on JSON web tokens (JWTs) indicating that they consist of base64url-encoded portions separated by periods ('.'), but I wasn't able to find similar documentation on CSRF tokens.
Are CSRF tokens also generally limited to a certain character set, or can they contain any characters?
A CSRF token is an opaque "nonce" that doesn't contain any info -- the token in the form submission and the token in the cookie or header simply have to match is all. If you see them base64-encoded, it's just for convenience of transmission, but it won't decode to anything useful, just random bytes most of the time. Nothing like the JSON structure of JWT.
Looking at my current framework (Laravel), its CSRF tokens are just random strings (they're derived from base64, but not valid base64). Chances are that's the case for most other frameworks too.

How to manually validate a JWT signature using online tools

From what I can understand, it's a straight forward process to validate a JWT signature. But when I use some online tools to do this for me, it doesn't match. How can I manually validate a JWT signature without using a JWT library? I'm needing a quick method (using available online tools) to demo how this is done.
I created my JWT on https://jwt.io/#debugger-io with the below info:
Algorithm: HS256
Secret: hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Verify Signature (section):
Secret value changed to above
"Checked" secret base64 encoded (whether this is checked or not, still get a different value)
JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.wDQ2mU5n89f2HsHm1dluHGNebbXeNr748yJ9kUNDNCA
Manual JWT signature verification attempt:
Using a base64UrlEncode calculator (http://www.simplycalc.com/base64url-encode.php or https://www.base64encode.org/)
If I: (Not actual value on sites, modified to show what the tools would ultimately build for me)
base64UrlEncode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9") + "." + base64UrlEncode("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ")
I get:
ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5.ZXlKemRXSWlPaUl4TWpNME5UWTNPRGt3SWl3aWJtRnRaU0k2SWtwdmFHNGdSRzlsSWl3aWFXRjBJam94TlRFMk1qTTVNREl5ZlE=
NOTE: there's some confusion on my part if I should be encoding the already encoded values, or use the already encoded values as-is.
(i.e. using base64UrlEncode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9") + "." + base64UrlEncode("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ") vs "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ").
Regardless on which I should do, the end result still doesn't match the signature. I'm leaning towards that I should NOT re-encode the encoded value, whether that's true or not.
Then using a HMAC Generator calculator (https://codebeautify.org/hmac-generator or https://www.freeformatter.com/hmac-generator.html#ad-output)
(Not actual value on sites, modified to show what the tools would ultimately build for me)
HMACSHA256(
"ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5.ZXlKemRXSWlPaUl4TWpNME5UWTNPRGt3SWl3aWJtRnRaU0k2SWtwdmFHNGdSRzlsSWl3aWFXRjBJam94TlRFMk1qTTVNREl5ZlE=",
"hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6"
)
Which gets me:
a2de322575675ba19ec272e83634755d4c3c2cd74e9e23c8e4c45e1683536e01
And that doesn't match the signature portion of the JWT:
wDQ2mU5n89f2HsHm1dluHGNebbXeNr748yJ9kUNDNCAM != a2de322575675ba19ec272e83634755d4c3c2cd74e9e23c8e4c45e1683536e01
Purpose:
The reason I'm needing to confirm this is to prove the ability to validate that the JWT hasn't been tampered with, without decoding the JWT.
My clients web interface doesn't need to decode the JWT, so there's no need for them to install a jwt package for doing that. They just need to do a simple validation to confirm the JWT hasn't been tampered with (however unlikely that may be) before they store the JWT for future API calls.
It's all a matter of formats and encoding.
On https://jwt.io you get this token based on your input values and secret:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
We want to prove that the signature:
3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
is correct.
The signature is a HMAC-SHA256 hash that is Base64url encoded.
(as described in RFC7515)
When you use the online HMAC generator to calculate a hash for
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
with the secret
hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6
you get
de921a2a4b225fd66ff0983e8566eb0f6e1584bdfa84120568da40e1f571dbd3
as result, which is a HMAC-SHA256 value, but not Base64url encoded. This hash is a hexadecimal string representation of a large number.
To compare it with the value from https://jwt.io you need to convert the value from it's hexadecimal string representation back to a number and Base64url encode it.
The following script is doing that and also uses crypto-js to calculate it's own hash. This can also be a way for you to verify without JWT libraries.
var CryptoJS = require("crypto-js");
// the input values
var base64Header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
var base64Payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
var secret = "hONPMX3tHWIp9jwLDtoCUwFAtH0RwSK6";
// two hashes from different online tools
var signatureJWTIO = "3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M";
var onlineCaluclatedHS256 = "de921a2a4b225fd66ff0983e8566eb0f6e1584bdfa84120568da40e1f571dbd3";
// hash calculation with Crypto-JS.
// The two replace expressions convert Base64 to Base64url format by replacing
// '+' with '-', '/' with '_' and stripping the '=' padding
var base64Signature = CryptoJS.HmacSHA256(base64Header + "." + base64Payload , secret).toString(CryptoJS.enc.Base64).replace(/\+/g,'-').replace(/\//g,'_').replace(/\=+$/m,'');
// converting the online calculated value to Base64 representation
var base64hash = new Buffer.from(onlineCaluclatedHS256, 'hex').toString('base64').replace(/\//g,'_').replace(/\+/g,'-').replace(/\=+$/m,'')
// the results:
console.log("Signature from JWT.IO : " + signatureJWTIO);
console.log("NodeJS calculated hash : " + base64Signature);
console.log("online calulated hash (converted) : " + base64hash);
The results are:
Signature from JWT.IO : 3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
NodeJS calculated hash : 3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
online calulated hash (converted) : 3pIaKksiX9Zv8Jg-hWbrD24VhL36hBIFaNpA4fVx29M
identical!
Conclusion:
The values calculated by the different online tools are all correct but not directly comparable due to different formats and encodings.
A little script as shown above might be a better solution.
I had the same problem until I figured out that I was using plain base64 encoding instead of base64url.
There are also some minor details in between.
Here is the step-by-step manual that will, hopefully, make the whole process much more clear.
Notes
Note 1: You must remove all spaces and newlines from your JSON strings (header and payload).
It is implicitly done on jwt.io when you generate a JWT token.
Note 2: To convert JSON string to base64url string on cryptii.com create the following configuration:
First view: Text
Second view: Encode
Encoding: Base64
Variant: Standard 'base64url' (RFC 4648 §5)
Third view: Text
Note 3: To convert HMAC HEX code (signature) to base64url string on cryptii.com create the following configuration:
First view: Bytes
Format: Hexadecimal
Group by: None
Second view: Encode
Encoding: Base64
Variant: Standard 'base64url' (RFC 4648 §5)
Third view: Text
Manual
You are going to need only two online tools:
[Tool 1]: cryptii.com - for base64url encoding,
[Tool 2]: codebeautify.org - for HMAC calculation.
On cryptii.com you can do both base64url encoding/decoding and also HMAC calculation, but for HMAC you need to provide a HEX key which is different from the input on jwt.io, so I used a separate service for HMAC calculation.
Input data
In this manual I used the following data:
Header:
{"alg":"HS256","typ":"JWT"}
Payload:
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Secret (key):
The Earth is flat!
The secret is not base64 encoded.
Step 1: Convert header [Tool 1]
Header (plain text):
{"alg":"HS256","typ":"JWT"}
Header (base64url encoded):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Step 2: Convert payload [Tool 1]
Payload (plain text):
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Payload (base64url encoded):
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Step 3: Calculate HMAC code (signature) [Tool 2]
Calculate HMAC using SHA256 algorithm.
Input string (base64url encoded header and payload, concatenated with a dot):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Calculated code (HEX number):
c8a9ae59f3d64564364a864d22490cc666c74c66a3822be04a9a9287a707b352
The calculated HMAC code is a HEX representation of the signature.
Note: it should not be encoded to base64url as a plain text string but as a sequence of bytes.
Step 4: Encode calculated HMAC code to base64url [Tool 1]:
Signature (Bytes):
c8a9ae59f3d64564364a864d22490cc666c74c66a3822be04a9a9287a707b352
Signature (base64url encoded):
yKmuWfPWRWQ2SoZNIkkMxmbHTGajgivgSpqSh6cHs1I
Summary
Here are our results (all base64url encoded):
Header:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature:
yKmuWfPWRWQ2SoZNIkkMxmbHTGajgivgSpqSh6cHs1I
The results from jwt.io:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.yKmuWfPWRWQ2SoZNIkkMxmbHTGajgivgSpqSh6cHs1I
As you can see, all three parts are identical.

Perl code to generate secret key for HMAC SHA256 signing?

I'm planning to use code similar to Amazon AWS samples to authenticate signed API requests. So users will have something like:
use Digest::SHA qw(hmac_sha256_base64);
my $digest = hmac_sha256_base64 ($request, $self->{SecretKey});
and attach $digest as a parameter to their request URI. The server-side will use the same algorithm to create a digest from the client URI and compare that to the value sent by the client.
What I can't find is Perl support for generating the SecretKey of the correct length to use when generating HMAC SHA256 digest.
For my Amazon AWS account I'm being given a 40 ASCII character base64 encoded string.
How do I generate a proper secret-key for my clients?
I suggest you use a PBKDF2 algorithm. PBKDF2 = "Password-based Key Derivation Function (#2)". It is defined in PKCS #5 (RFC 2898). This is the recommended way to derive a key from a password. You will need a salt, as well. A typical iteration count is 1000.
This page says it has a perl implementation of PBKDF2. I haven't tried it.
Apparently there is also a Crypto::PBKDF2, but it is saddled with dependencies you may not want.
EDIT
I just tried Anthony Thyssen's perl program for pbkdf2 - it works great. Simple, easy.