When validating C-Sharp made JWT signature with PHP, or the other way around, you may run into some problems. What can happen is that the hash will be different but the data still won’t be tampered with. I noticed this when sending a JWT from a server using C# to a server using PHP. This code example will create the same signature with both PHP and C#.
C#:
public static string CreateToken(string sharedSecret) {
var encodedHeader = Base64Encode("{\"typ\":\"JWT\",\"alg\":\"HS256\"}");
var encodedClaims = Base64Encode({"score": "12","name": "Crille"});
var signingInput = encodedHeader + "." + encodedClaims;
var token = signingInput + "." + Base64Encode(SHA256Sum(signingInput, sharedSecret));
return token;
}
This is what the method SHA256Sum looks like:
private static string SHA256Sum(string signingInput, string sharedSecret) {
var encoding = new System.Text.UTF8Encoding();
var keyByte = encoding.GetBytes(sharedSecret);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
hmacsha256.ComputeHash(encoding.GetBytes(signingInput));
return ByteToString(hmacsha256.Hash);
}
}
Base64 encode method looks like this:
private static string Base64Decode(string encodedString) {
var textBytes = System.Convert.FromBase64String(encodedString);
return System.Text.Encoding.UTF8.GetString(textBytes);
}
Base64 decode method:
private static string Base64Encode(string plainText) {
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
And a method to create a byte array to a string
private static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
sbinary += buff[i].ToString("X2");
return sbinary;
}
PHP:
This is a class that will encode, decode and validate a JWT-token and will return the same signature as the C# methods. If the data has not be tampered with that is.
<?php
class JWT {
private $shared_secret = "thesharedsecret";
function encode($payload) {
$encoded_header = base64_encode('{"alg": "HS256","typ": "JWT"}');
$encoded_payload = base64_encode($payload);
$header_and_payload_combined = $encoded_header . '.' . $encoded_payload;
$hash = $this->create_signature($header_and_payload_combined);
$signature = base64_encode($hash);
$jwt_token = $header_and_payload_combined . '.' . $signature;
return $jwt_token;
}
function is_valid($token) {
$is_valid = false;
$token_parts = explode('.', $token);
$header_and_payload_combined = $token_parts[0] . '.' . $token_parts[1];
$recieved_signature = $token_parts[2];
$new_signature = base64_encode($this->create_signature($header_and_payload_combined));
if($new_signature == $recieved_signature) {
$is_valid = true;
}
return $is_valid;
}
private function create_signature($message) {
$hash = hash_hmac('sha256', $message, $this->shared_secret);
$hash = strtoupper($hash);
return $hash;
}
}