Calculating JWT signature Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
kyuuuri
Posts: 340
Joined: 09 Jan 2016, 19:20

Calculating JWT signature

02 Dec 2019, 06:34

Hello, I'm trying to manually calculate a JWT's signature.
I'm using: https://jwt.io/.
This is my code:

Code: Select all

Header =
(
{
  "alg": "HS256",
  "typ": "JWT"
}
)
Payload =
(
{
  "sub": "1234567890",
  "name": "JohnDoe",
  "iat": 1516239022
}
)
header:= b64Encode(RegExReplace(Header, "(\n)|(\s)"))
; eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
payload:=b64Encode(RegExReplace(Payload, "(\n)|(\s)"))
; eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG5Eb2UiLCJpYXQiOjE1MTYyMzkwMjJ9

; Those two are okay.

tokenHash := bcrypt_sha256_hmac(RegExReplace(header, "(\n)|(\s)") . "." . RegExReplace(payload, "(\n)|(\s)"), "test123")
; 3b59324118bcd59a5435194120c2cfcb7cf295f25a79149b79145696329ffb95
msgbox % b64Encode(tokenHash)
; Different from O1kyQRi81ZpUNRlBIMLPy3zylfJaeRSbeRRWljKf-5U

b64Encode(string)
{
    VarSetCapacity(bin, StrPut(string, "UTF-8")) && len := StrPut(string, &bin, "UTF-8") - 1 
    if !(DllCall("crypt32\CryptBinaryToString", "ptr", &bin, "uint", len, "uint", 0x40000001, "ptr", 0, "uint*", size))
        throw Exception("CryptBinaryToString failed", -1)
    VarSetCapacity(buf, size << 1, 0)
    if !(DllCall("crypt32\CryptBinaryToString", "ptr", &bin, "uint", len, "uint", 0x40000001, "ptr", &buf, "uint*", size))
        throw Exception("CryptBinaryToString failed", -1)
    return StrGet(&buf)
}
tokenHash is 3b59324118bcd59a5435194120c2cfcb7cf295f25a79149b79145696329ffb95 and according to this answer on stackOverflow:
"... 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."
So I tried using this code:

Code: Select all

tokenHash := "3b59324118bcd59a5435194120c2cfcb7cf295f25a79149b79145696329ffb95"
token := HexToString(tokenHash)
msgbox % b64Encode(token)

HexToString(String) 
{ 
local Length, CharStr, RetString 

If !String 
  Return 0 

Length := StrLen(String)//2 


Loop, %Length% 
  { 
   
	  StringMid, CharStr, String, A_Index*2 - 1, 2 
	  
	  CharStr = 0x%CharStr%

	  ;Build the return string 
	  RetString .= Chr(CharStr) 
  
  } 

;Return the string to the caller 
Return RetString 
}
But it doesn't output: O1kyQRi81ZpUNRlBIMLPy3zylfJaeRSbeRRWljKf-5U I don't know what I'm doing wrong.

Thanks in advance.
User avatar
Chunjee
Posts: 1499
Joined: 18 Apr 2014, 19:05
Contact:

Re: Calculating JWT signature

02 Dec 2019, 07:43

Best I can tell you need a Base64url function. I haven't found any in the last 10 mins on google. Still looking.

Proof: https://gchq.github.io/CyberChef/#recip ... MjlmZmI5NQ

Base 62 encoding with URL and filesafe output: https://tools.ietf.org/html/rfc4648#section-5
kyuuuri
Posts: 340
Joined: 09 Jan 2016, 19:20

Re: Calculating JWT signature

02 Dec 2019, 08:44

Chunjee wrote:
02 Dec 2019, 07:43
Best I can tell you need a Base64url function. I haven't found any in the last 10 mins on google. Still looking.

Proof: https://gchq.github.io/CyberChef/#recip ... MjlmZmI5NQ

Base 62 encoding with URL and filesafe output: https://tools.ietf.org/html/rfc4648#section-5

Hello, the differences between base64 and base64url is that "+" and "/" are converted to "-" and "_" and that there is no mandatory padding with "=" characters to make the string length a multiple of four.
But the difference I get is not even close.

There is a problem when converting the hex to string or when encoding the string.

Right now my code outputs:
O1kyQRjCvMOVwppUNRlBIMOCw4_Di3zDssKVw7JaeRTCm3kUVsKWMsKfw7vClQ (when converted to base64url)
If I go to a hex to string converter online to convert the hash to a string and then paste the string in a base64url encoder I get the same value.
So I don't know exactly what's wrong.
User avatar
Chunjee
Posts: 1499
Joined: 18 Apr 2014, 19:05
Contact:

Re: Calculating JWT signature

02 Dec 2019, 09:17

Yeah I got it backwards. Something is wrong with the hex step.

I also couldn't find a more appropriate function.
kyuuuri
Posts: 340
Joined: 09 Jan 2016, 19:20

Re: Calculating JWT signature

02 Dec 2019, 09:28

The weird part is that if (using online tools) I convert from hex to base64url then the output is okay, but if I convert from hex to string and then to base64url then the output is wrong.
teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: Calculating JWT signature  Topic is solved

02 Dec 2019, 10:58

Code: Select all

tokenHash := "3b59324118bcd59a5435194120c2cfcb7cf295f25a79149b79145696329ffb95"

len := CryptStringToBinary(tokenHash, outData, "CRYPT_STRING_HEXRAW")
MsgBox, % CryptBinaryToString(&outData, len)

CryptStringToBinary(string, ByRef outData, formatName := "CRYPT_STRING_BASE64")
{
   static formats := { CRYPT_STRING_BASE64: 0x1
                     , CRYPT_STRING_HEX:    0x4
                     , CRYPT_STRING_HEXRAW: 0xC }
   fmt := formats[formatName]
   DllCall( "Crypt32\CryptStringToBinary", "Ptr", &string, "UInt", StrLen(string), "UInt", fmt
                                         , "UInt", 0, "UIntP", bytes, "UIntP", 0, "UIntP", 0 )
   VarSetCapacity(outData, bytes) 
   DllCall( "Crypt32\CryptStringToBinary", "Ptr", &string, "UInt", StrLen(string), "UInt", fmt
                                         , "Str", outData, "UIntP", bytes, "UIntP", 0, "UIntP", 0 )
   Return bytes
}

CryptBinaryToString(pData, size, formatName := "CRYPT_STRING_BASE64", NOCRLF := true)
{
   static formats := { CRYPT_STRING_BASE64: 0x1
                     , CRYPT_STRING_HEX:    0x4
                     , CRYPT_STRING_HEXRAW: 0xC }
        , CRYPT_STRING_NOCRLF := 0x40000000
   fmt := formats[formatName] | (NOCRLF ? CRYPT_STRING_NOCRLF : 0)
   DllCall("Crypt32\CryptBinaryToString", "Ptr", pData, "UInt", size, "UInt", fmt, "Ptr", 0, "UIntP", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode)
   DllCall("Crypt32\CryptBinaryToString", "Ptr", pData, "UInt", size, "UInt", fmt, "Str", outData, "UIntP", chars)
   Return outData
}
guest3456
Posts: 3469
Joined: 09 Oct 2013, 10:31

Re: Calculating JWT signature

03 Dec 2019, 23:11

kyuuuri wrote:
02 Dec 2019, 06:34
Hello, I'm trying to manually calculate a JWT's signature.
I'm using: https://jwt.io/.
if you complete a JWT library please share in the Scripts&Functions forum

nadure
Posts: 22
Joined: 26 Mar 2021, 23:02
Location: Korea
Contact:

Re: Calculating JWT signature

31 Mar 2021, 10:00

guest3456 wrote:
03 Dec 2019, 23:11
kyuuuri wrote:
02 Dec 2019, 06:34
Hello, I'm trying to manually calculate a JWT's signature.
I'm using: https jwt.io /. Broken Link for safety
if you complete a JWT library please share in the Scripts&Functions forum

Code: Select all

#Include %A_ScriptDir%\Resource\JsonParser.ahk
#Include %A_ScriptDir%\Resource\libcrypt.ahk

JWT_Token(Header, Payload, SecretKey, mode="sha256", SecretKeyToBase64=false)
{
    if(SecretKeyToBase64)
    {
        SecretKey := Base64enc(SecretKey)
        SecretKey := Base64ToBase64URLenc(SecretKey)
    }
    return new JWT_Token(Header, Payload, SecretKey, mode)
}

class JWT_Token
{
    __New(Header, Payload, SecretKey, mode="sha256")
    {
        ; Header -> Array()  ; Actually dict object. ex){"aa":"bbb"}
        ; Payload -> Array() ; Actually dict object. ex){"aa":"bbb"}
        ; SecretKey -> String ; just string for SecretKey or base64urlencoded string
        this.TreatErrorthings(Header, Payload, SecretKey)
        Header_b64 := Base64enc(Json.Dump(Header))
        Payload_b64 := Base64enc(Json.Dump(Payload))
        Header_b64_url := Base64ToBase64URLenc(Header_b64)
        Payload_b64_url := Base64ToBase64URLenc(Payload_b64)

        message := Header_b64_url . "." . Payload_b64_url
        sign_origin := LC_HMAC(SecretKey, message, mode) ; ok good
        
        
        len:=CryptStringToBinary(sign_origin, outData, "CRYPT_STRING_HEXRAW")
        signature := CryptBinaryToString(&outData, len)
        signature := Base64ToBase64URLenc(signature)
        
        return Header_b64_url . "." . Payload_b64_url . "." . signature
    }

    TreatErrorthings(Header, Payload, SecretKey)
    {
        if(Header = "")
            throw, there is no Header String
        
        if(Payload = "")
            throw, there is no Payload String
        
        if(SecretKey="")
            throw, there is no SecretKey

        if(IsObject(Header) = false)
            throw, Header is not array

        if(IsObject(Payload) = false)
            throw, Header is not array
    }
}



Base64ToBase64URLenc(string)
{
    ; string -> base64

    ; string returnValue = System.Convert
    ;   .ToBase64String(toEncodeAsBytes)
    ;   .TrimEnd(padding).Replace('+', '-')
    ;   .Replace('/', '_');

    ; result := ""
    ; loop,% StrLen(string)
    ; {
    ;     sub_string := SubStr(string, -A_index, 1 )
    ;     if asc(sub_string) = 61
    ;     {   
    ;         result := SubStr(string, 1, StrLen(string)-A_Index-1)
    ;     }
    ;     else
    ;     {
    ;         if A_index = 1
    ;         {
    ;             result := string
    ;         }
    ;         break
    ;     }
    ; }

    result := StrReplace(string, "=", "")
    result := StrReplace(result, "+", "-")
    result := StrReplace(result, "/", "_")
    return result
}


Code(i) {   ; <== Chars[i & 63], 0-base index
   Static Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
   Return SubStr(Chars,(i&63)+1,1)
}



Base64enc(string)
{
   Loop Parse, string
   {
      If Mod(A_Index,3) = 1
         buffer := Asc(A_LoopField) << 16
      Else If Mod(A_Index,3) = 2
         buffer += Asc(A_LoopField) << 8
      Else {
         buffer += Asc(A_LoopField)
         out := out . Code(buffer>>18) . Code(buffer>>12) . Code(buffer>>6) . Code(buffer)
      }
   }
   If Mod(StrLen(string),3) = 0
      Return out
   If Mod(StrLen(string),3) = 1
      Return out . Code(buffer>>18) . Code(buffer>>12) "=="
   Return out . Code(buffer>>18) . Code(buffer>>12) . Code(buffer>>6) "="
}

CryptStringToBinary(string, ByRef outData, formatName := "CRYPT_STRING_BASE64")
{
    ; https://www.autohotkey.com/boards/viewtopic.php?t=70358#p303866
   static formats := { CRYPT_STRING_BASE64: 0x1
                     , CRYPT_STRING_HEX:    0x4
                     , CRYPT_STRING_HEXRAW: 0xC }
   fmt := formats[formatName]
   DllCall( "Crypt32\CryptStringToBinary", "Ptr", &string, "UInt", StrLen(string), "UInt", fmt
                                         , "UInt", 0, "UIntP", bytes, "UIntP", 0, "UIntP", 0 )
   VarSetCapacity(outData, bytes) 
   DllCall( "Crypt32\CryptStringToBinary", "Ptr", &string, "UInt", StrLen(string), "UInt", fmt
                                         , "Str", outData, "UIntP", bytes, "UIntP", 0, "UIntP", 0 )
   Return bytes
}

CryptBinaryToString(pData, size, formatName := "CRYPT_STRING_BASE64", NOCRLF := true)
{
    ; https://www.autohotkey.com/boards/viewtopic.php?t=70358#p303866
   static formats := { CRYPT_STRING_BASE64: 0x1
                     , CRYPT_STRING_HEX:    0x4
                     , CRYPT_STRING_HEXRAW: 0xC }
        , CRYPT_STRING_NOCRLF := 0x40000000
   fmt := formats[formatName] | (NOCRLF ? CRYPT_STRING_NOCRLF : 0)
   DllCall("Crypt32\CryptBinaryToString", "Ptr", pData, "UInt", size, "UInt", fmt, "Ptr", 0, "UIntP", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode)
   DllCall("Crypt32\CryptBinaryToString", "Ptr", pData, "UInt", size, "UInt", fmt, "Str", outData, "UIntP", chars)
   Return outData
}

https://github.com/ahkscript/libcrypt.ahk


it works!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot], Google [Bot], Spawnova and 291 guests