JWT Authentication in Box

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

JWT Authentication in Box

29 Nov 2021, 14:45

Hi everyone,

I'm trying to use JWT authentication in Box using a key pair to get an access token, but am stuck creating the JWT assertion

Documentation I'm trying to follow:
https://developer.box.com/guides/authentication/jwt/without-sdk/

Any help is appreciated.


This works using a temporary developer token:

Code: Select all

EndPointBox = https://api.box.com/2.0/folders/0/items
Payload =
access_token = ######(Temp Dev Token)######

Reg:=API_Call_Box("GET",EndpointBox,access_token,Payload)
FolderCount := Reg.total_count

API_Call_Box(Type,EndpointBox,access_token,Payload){
HTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
HTTP.Open(Type,EndPointBox)
HTTP.SetRequestHeader("Authorization","Bearer " access_token)
HTTP.SetRequestHeader("Content-Type","application/json")
HTTP.Send()

;HTTPstatus := HTTP.Status
;HTTPresponse := HTTP.ResponseText
;msgbox % HTTPstatus
;msgbox % HTTPresponse

ParsedResponse:=json(HTTP.ResponseText)
Return ParsedResponse
}

msgbox % FolderCount

ExitApp

;;;;;;;;json;;;;;;;;

	json(json, rec := false) {
   static doc := ComObjCreate("htmlfile")
         , __ := doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         , JS := doc.parentWindow
   if !rec
      obj := %A_ThisFunc%(JS.eval("(" . json . ")"), true)
   else if !IsObject(json)
      obj := json
   else if JS.Object.prototype.toString.call(json) == "[object Array]" {
      obj := []
      Loop % json.length
         obj.Push( %A_ThisFunc%(json[A_Index - 1], true) )
   }
   else {
      obj := {}
      keys := JS.Object.keys(json)
      Loop % keys.length {
         k := keys[A_Index - 1]
         obj[k] := %A_ThisFunc%(json[k], true)
      }
   }
   Return obj
}


My attempt at generating an access token:

Code: Select all

#NoEnv
#SingleInstance,Force
SetBatchLines, -1

;;;;;;;;;;;;;;;;;;;;;;;;;;;Get-Token;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;https://developer.box.com/guides/authentication/jwt/without-sdk/


clientID := "#############"
clientSecret := "#####################"
enterpriseID := "##############"
publicKeyID := "###############"
;privateKey := "-----BEGIN ENCRYPTED PRIVATE KEY-----\n######################\n-----END ENCRYPTED PRIVATE KEY-----\n"
privateKey := "############################"
passphrase := "#######################"

time := A_now
time += 45, s

random, RandomNum, 1000000000000000000.0, 9999999999999999999.0
RandomNum := floor(RandomNum)

authenticationUrl := "https://api.box.com/oauth2/token"

key := AES.Decrypt(privateKey, passphrase, "256")

claims := QueryString_Builder({"iss":clientID,"sub":enterpriseID,"box_sub_type":"enterprise","aud":authenticationUrl,"jti":RandomNum,"exp":time,"kid":publicKeyID}) 

;Create JWT Assertion?
assertion := ???????(claims, key, "RS512")

params := QueryString_Builder({"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":assertion,"client_id":clientID,"client_secret":clientSecret})

HTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
HTTP.Open("POST",authenticationUrl . params)
HTTP.Send()
response := HTTP.ResponseText

access_token := json(response)
msgbox % access_token


;;;;;;;;;;;;;;;;;;;;;;;;;;;Get-Data;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EndPointBox = https://api.box.com/2.0/folders/0/items
Payload =

Reg:=API_Call_Box("GET",EndpointBox,access_token,Payload)
FolderCount := Reg.total_count

API_Call_Box(Type,EndpointBox,access_token,Payload){
HTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
HTTP.Open(Type,EndPointBox)
HTTP.SetRequestHeader("Authorization","Bearer " access_token)
HTTP.SetRequestHeader("Content-Type","application/json")
HTTP.Send()

;HTTPstatus := HTTP.Status
;HTTPresponse := HTTP.ResponseText
;msgbox % HTTPstatus
;msgbox % HTTPresponse

ParsedResponse:=json(HTTP.ResponseText)
Return ParsedResponse
}

msgbox % FolderCount

ExitApp



;;;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\REFERENCE///////////////////////////////////////////////////////////////////////
;;;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

;;;;;;;;;;;;;;;;;;;;;;;;;;;;QueryString_Builder;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

QueryString_Builder(kvp){
for key, value in kvp
queryString.=((A_Index="1")?(url "?"):("&")) key "=" value
return queryString
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AES;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;https://gist.github.com/jNizM/79aa6a4b8ec428bf780f

Class AES
{
    Encrypt(string, password, alg)
    {
        len := this.StrPutVar(string, str_buf, 0, "UTF-16")
        this.Crypt(str_buf, len, password, alg, 1)
        return this.b64Encode(str_buf, len)
    }
    Decrypt(string, password, alg)
    {
        len := this.b64Decode(string, encr_Buf)
        sLen := this.Crypt(encr_Buf, len, password, alg, 0)
        sLen /= 2
        return StrGet(&encr_Buf, sLen, "UTF-16")
    }

    Crypt(ByRef encr_Buf, ByRef Buf_Len, password, ALG_ID, CryptMode := 1)
    {
        ; WinCrypt.h
        static MS_ENH_RSA_AES_PROV := "Microsoft Enhanced RSA and AES Cryptographic Provider"
        static PROV_RSA_AES        := 24
        static CRYPT_VERIFYCONTEXT := 0xF0000000
        static CALG_SHA1           := 0x00008004
        static CALG_SHA_256        := 0x0000800c
        static CALG_SHA_384        := 0x0000800d
        static CALG_SHA_512        := 0x0000800e
        static CALG_AES_128        := 0x0000660e ; KEY_LENGHT := 0x80  ; (128)
        static CALG_AES_192        := 0x0000660f ; KEY_LENGHT := 0xC0  ; (192)
        static CALG_AES_256        := 0x00006610 ; KEY_LENGHT := 0x100 ; (256)
        static KP_BLOCKLEN         := 8

        if !(DllCall("advapi32.dll\CryptAcquireContext", "Ptr*", hProv, "Ptr", 0, "Ptr", 0, "Uint", PROV_RSA_AES, "UInt", CRYPT_VERIFYCONTEXT))
            MsgBox % "*CryptAcquireContext (" DllCall("kernel32.dll\GetLastError") ")"

        if !(DllCall("advapi32.dll\CryptCreateHash", "Ptr", hProv, "Uint", CALG_SHA1, "Ptr", 0, "Uint", 0, "Ptr*", hHash))
            MsgBox % "*CryptCreateHash (" DllCall("kernel32.dll\GetLastError") ")"

        passLen := this.StrPutVar(password, passBuf, 0, "UTF-16")
        if !(DllCall("advapi32.dll\CryptHashData", "Ptr", hHash, "Ptr", &passBuf, "Uint", passLen, "Uint", 0))
            MsgBox % "*CryptHashData (" DllCall("kernel32.dll\GetLastError") ")"
        
        if !(DllCall("advapi32.dll\CryptDeriveKey", "Ptr", hProv, "Uint", CALG_AES_%ALG_ID%, "Ptr", hHash, "Uint", (ALG_ID << 0x10), "Ptr*", hKey)) ; KEY_LENGHT << 0x10
            MsgBox % "*CryptDeriveKey (" DllCall("kernel32.dll\GetLastError") ")"

        if !(DllCall("advapi32.dll\CryptGetKeyParam", "Ptr", hKey, "Uint", KP_BLOCKLEN, "Uint*", BlockLen, "Uint*", 4, "Uint", 0))
            MsgBox % "*CryptGetKeyParam (" DllCall("kernel32.dll\GetLastError") ")"
        BlockLen /= 8

        if (CryptMode)
            DllCall("advapi32.dll\CryptEncrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len, "Uint", Buf_Len + BlockLen)
        else
            DllCall("advapi32.dll\CryptDecrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len)

        DllCall("advapi32.dll\CryptDestroyKey", "Ptr", hKey)
        DllCall("advapi32.dll\CryptDestroyHash", "Ptr", hHash)
        DllCall("advapi32.dll\CryptReleaseContext", "Ptr", hProv, "UInt", 0)
        return Buf_Len
    }

    StrPutVar(string, ByRef var, addBufLen := 0, encoding := "UTF-16")
    {
        tlen := ((encoding = "UTF-16" || encoding = "CP1200") ? 2 : 1)
        str_len := StrPut(string, encoding) * tlen
        VarSetCapacity(var, str_len + addBufLen, 0)
        StrPut(string, &var, encoding)
        return str_len - tlen
    }

    b64Encode(ByRef VarIn, SizeIn)
    {
        static CRYPT_STRING_BASE64 := 0x00000001
        static CRYPT_STRING_NOCRLF := 0x40000000
        DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", 0, "UInt*", SizeOut)
        VarSetCapacity(VarOut, SizeOut, 0)
        DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", &VarOut, "UInt*", SizeOut)
        return StrGet(&VarOut, SizeOut, "CP0")
    }
    b64Decode(ByRef VarIn, ByRef VarOut)
    {
        static CRYPT_STRING_BASE64 := 0x00000001
        static CryptStringToBinary := "CryptStringToBinary" (A_IsUnicode ? "W" : "A")
        DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", 0, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
        VarSetCapacity(VarOut, SizeOut, 0)
        DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", &VarOut, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
        return SizeOut
    }
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;json;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	json(json, rec := false) {
   static doc := ComObjCreate("htmlfile")
         , __ := doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         , JS := doc.parentWindow
   if !rec
      obj := %A_ThisFunc%(JS.eval("(" . json . ")"), true)
   else if !IsObject(json)
      obj := json
   else if JS.Object.prototype.toString.call(json) == "[object Array]" {
      obj := []
      Loop % json.length
         obj.Push( %A_ThisFunc%(json[A_Index - 1], true) )
   }
   else {
      obj := {}
      keys := JS.Object.keys(json)
      Loop % keys.length {
         k := keys[A_Index - 1]
         obj[k] := %A_ThisFunc%(json[k], true)
      }
   }
   Return obj
}

TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

Re: JWT Authentication in Box

02 Dec 2021, 14:11

I think I've made progress. I'm now getting an "Invalid credentials" response.
The credentials are correct so I'm thinking I'm doing something wrong with the encrypt/decrypt?

Code: Select all

#NoEnv
#SingleInstance,Force
SetBatchLines, -1

;;;;;;;;;;;;;;;;;;;;;;;;;;;Get-Token;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Documentation:
;https://developer.box.com/guides/authentication/jwt/without-sdk/


clientID := "#########"
clientSecret := "#############"
enterpriseID := "############"
publicKeyID := "#########"
privateKey := "-----BEGIN ENCRYPTED PRIVATE KEY-----\n##############\n-----END ENCRYPTED PRIVATE KEY-----\n"
passphrase := "##############"


time := A_now
time += 45, s

random, RandomNum, 1000000000000000.0, 9999999999999999.0
RandomNum := floor(RandomNum)

authenticationUrl := "https://api.box.com/oauth2/token"

key := AES.Decrypt(privateKey, passphrase, "256")

claims := {"iss":clientID,"sub":enterpriseID,"box_sub_type":"enterprise","aud":authenticationUrl,"jti":RandomNum,"exp":time,"kid":publicKeyID}

assertion := AES.Encrypt(claims, key, "256")

params := QueryString_Builder({"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":assertion,"client_id":clientID,"client_secret":clientSecret})

HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
HTTP.Open("POST", authenticationUrl . params, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.Send("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" assertion)
HTTP.WaitForResponse()
response := HTTP.ResponseText
msgbox % response

access_token := json(response)

;;;;;;;;;;;;;;;;;;;;;;;;;;;Get-Data;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EndPointBox = https://api.box.com/2.0/folders/0/items
Payload =

Reg:=API_Call_Box("GET",EndpointBox,access_token,Payload)
FolderCount := Reg.total_count

API_Call_Box(Type,EndpointBox,access_token,Payload){
HTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
HTTP.Open(Type,EndPointBox)
HTTP.SetRequestHeader("Authorization","Bearer " access_token)
HTTP.SetRequestHeader("Content-Type","application/json")
HTTP.Send()

ParsedResponse:=json(HTTP.ResponseText)
Return ParsedResponse
}

msgbox % FolderCount

ExitApp



;;;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;;;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\REFERENCE///////////////////////////////////////////////////////////////////////
;;;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

;;;;;;;;;;;;;;;;;;;;;;;;;;;;QueryString_Builder;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

QueryString_Builder(kvp){
for key, value in kvp
queryString.=((A_Index="1")?(url "?"):("&")) key "=" value
return queryString
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AES;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;https://gist.github.com/jNizM/79aa6a4b8ec428bf780f

Class AES
{
    Encrypt(string, password, alg)
    {
        len := this.StrPutVar(string, str_buf, 0, "UTF-16")
        this.Crypt(str_buf, len, password, alg, 1)
        return this.b64Encode(str_buf, len)
    }
    Decrypt(string, password, alg)
    {
        len := this.b64Decode(string, encr_Buf)
        sLen := this.Crypt(encr_Buf, len, password, alg, 0)
        sLen /= 2
        return StrGet(&encr_Buf, sLen, "UTF-16")
    }

    Crypt(ByRef encr_Buf, ByRef Buf_Len, password, ALG_ID, CryptMode := 1)
    {
        ; WinCrypt.h
        static MS_ENH_RSA_AES_PROV := "Microsoft Enhanced RSA and AES Cryptographic Provider"
        static PROV_RSA_AES        := 24
        static CRYPT_VERIFYCONTEXT := 0xF0000000
        static CALG_SHA1           := 0x00008004
        static CALG_SHA_256        := 0x0000800c
        static CALG_SHA_384        := 0x0000800d
        static CALG_SHA_512        := 0x0000800e
        static CALG_AES_128        := 0x0000660e ; KEY_LENGHT := 0x80  ; (128)
        static CALG_AES_192        := 0x0000660f ; KEY_LENGHT := 0xC0  ; (192)
        static CALG_AES_256        := 0x00006610 ; KEY_LENGHT := 0x100 ; (256)
        static KP_BLOCKLEN         := 8

        if !(DllCall("advapi32.dll\CryptAcquireContext", "Ptr*", hProv, "Ptr", 0, "Ptr", 0, "Uint", PROV_RSA_AES, "UInt", CRYPT_VERIFYCONTEXT))
            MsgBox % "*CryptAcquireContext (" DllCall("kernel32.dll\GetLastError") ")"

        if !(DllCall("advapi32.dll\CryptCreateHash", "Ptr", hProv, "Uint", CALG_SHA1, "Ptr", 0, "Uint", 0, "Ptr*", hHash))
            MsgBox % "*CryptCreateHash (" DllCall("kernel32.dll\GetLastError") ")"

        passLen := this.StrPutVar(password, passBuf, 0, "UTF-16")
        if !(DllCall("advapi32.dll\CryptHashData", "Ptr", hHash, "Ptr", &passBuf, "Uint", passLen, "Uint", 0))
            MsgBox % "*CryptHashData (" DllCall("kernel32.dll\GetLastError") ")"
        
        if !(DllCall("advapi32.dll\CryptDeriveKey", "Ptr", hProv, "Uint", CALG_AES_%ALG_ID%, "Ptr", hHash, "Uint", (ALG_ID << 0x10), "Ptr*", hKey)) ; KEY_LENGHT << 0x10
            MsgBox % "*CryptDeriveKey (" DllCall("kernel32.dll\GetLastError") ")"

        if !(DllCall("advapi32.dll\CryptGetKeyParam", "Ptr", hKey, "Uint", KP_BLOCKLEN, "Uint*", BlockLen, "Uint*", 4, "Uint", 0))
            MsgBox % "*CryptGetKeyParam (" DllCall("kernel32.dll\GetLastError") ")"
        BlockLen /= 8

        if (CryptMode)
            DllCall("advapi32.dll\CryptEncrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len, "Uint", Buf_Len + BlockLen)
        else
            DllCall("advapi32.dll\CryptDecrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len)

        DllCall("advapi32.dll\CryptDestroyKey", "Ptr", hKey)
        DllCall("advapi32.dll\CryptDestroyHash", "Ptr", hHash)
        DllCall("advapi32.dll\CryptReleaseContext", "Ptr", hProv, "UInt", 0)
        return Buf_Len
    }

    StrPutVar(string, ByRef var, addBufLen := 0, encoding := "UTF-16")
    {
        tlen := ((encoding = "UTF-16" || encoding = "CP1200") ? 2 : 1)
        str_len := StrPut(string, encoding) * tlen
        VarSetCapacity(var, str_len + addBufLen, 0)
        StrPut(string, &var, encoding)
        return str_len - tlen
    }

    b64Encode(ByRef VarIn, SizeIn)
    {
        static CRYPT_STRING_BASE64 := 0x00000001
        static CRYPT_STRING_NOCRLF := 0x40000000
        DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", 0, "UInt*", SizeOut)
        VarSetCapacity(VarOut, SizeOut, 0)
        DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", &VarOut, "UInt*", SizeOut)
        return StrGet(&VarOut, SizeOut, "CP0")
    }
    b64Decode(ByRef VarIn, ByRef VarOut)
    {
        static CRYPT_STRING_BASE64 := 0x00000001
        static CryptStringToBinary := "CryptStringToBinary" (A_IsUnicode ? "W" : "A")
        DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", 0, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
        VarSetCapacity(VarOut, SizeOut, 0)
        DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", &VarOut, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
        return SizeOut
    }
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;json;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	json(json, rec := false) {
   static doc := ComObjCreate("htmlfile")
         , __ := doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
         , JS := doc.parentWindow
   if !rec
      obj := %A_ThisFunc%(JS.eval("(" . json . ")"), true)
   else if !IsObject(json)
      obj := json
   else if JS.Object.prototype.toString.call(json) == "[object Array]" {
      obj := []
      Loop % json.length
         obj.Push( %A_ThisFunc%(json[A_Index - 1], true) )
   }
   else {
      obj := {}
      keys := JS.Object.keys(json)
      Loop % keys.length {
         k := keys[A_Index - 1]
         obj[k] := %A_ThisFunc%(json[k], true)
      }
   }
   Return obj
}
TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

Re: JWT Authentication in Box

02 Dec 2021, 17:33

Thanks for the reply.
I've discovered that I was setting my expiring time incorrectly and that syntax of variables in the payload was wrong.
I've adapted the example you linked but am still getting an invalid credentials response.

Code: Select all

#NoEnv
#SingleInstance,Force
SetBatchLines, -1

clientID := "#########"
clientSecret := "###########"
enterpriseID := "###########"
publicKeyID := "###########"
privateKey := "-----BEGIN ENCRYPTED PRIVATE KEY-----\n###########\n-----END ENCRYPTED PRIVATE KEY-----\n"
passphrase := "################"

random, RandomNum, 1000000000000000.0, 9999999999999999.0
RandomNum := floor(RandomNum)

authenticationUrl := "https://api.box.com/oauth2/token"

DllCall("LoadLibrary", "str", "bcrypt.dll")
DllCall("LoadLibrary", "str", "crypt32.dll")

time := A_NowUTC
EnvSub, time, 19700101000000, seconds
expTime := time+45

header = {"alg":"RS256","typ":"JWT"}
payload = {"iss":%clientID%,"sub":%enterpriseID%,"box_sub_type":"enterprise","aud":"%authenticationUrl%","jti":%RandomNum%,"exp":%expTime%,"kid":%publicKeyID%}

header := Base64URLenc(header)
payload := Base64URLenc(payload)
privateKey := StrReplace(privateKey, "\n")
len := CryptStringToBinary(privateKey, outData)

DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", PKCS_PRIVATE_KEY_INFO := 44, "ptr", &outData, "uint", len, "uint", (CRYPT_DECODE_ALLOC_FLAG := 0x8000)|(CRYPT_DECODE_NOCOPY_FLAG := 0x1), "ptr", 0, "ptr*", PrivateKeyInfo, "uint*", cb)
PrivateKeyInfo_PrivateKey_cbData := numget(PrivateKeyInfo+0, 4*A_PtrSize, "uint")
PrivateKeyInfo_PrivateKey_pbData := numget(PrivateKeyInfo+0, 5*A_PtrSize, "uint")
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", CNG_RSA_PRIVATE_KEY_BLOB := 83, "ptr", PrivateKeyInfo_PrivateKey_pbData, "uint", PrivateKeyInfo_PrivateKey_cbData, "uint", CRYPT_DECODE_ALLOC_FLAG := 0x8000, "ptr", 0, "ptr*", prkb, "uint*", cb)
DllCall("LocalFree", "ptr", PrivateKeyInfo)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hSignAlg, "ptr", &(BCRYPT_RSA_ALGORITHM := "RSA"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptImportKeyPair", "ptr", hSignAlg, "ptr", 0, "ptr", &(BCRYPT_RSAPRIVATE_BLOB := "RSAPRIVATEBLOB"), "ptr*", phKey, "ptr", prkb, "uint", cb, "uint", 0)
DllCall("LocalFree", "ptr", prkb)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hHashAlg, "ptr", &(BCRYPT_SHA256_ALGORITHM := "SHA256"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptCreateHash", "ptr", hHashAlg, "ptr*", hHash, "ptr", 0, "uint", 0, "ptr", 0, "uint", 0 , "uint", 0)
size := StrPut(header "." payload, "UTF-8")
VarSetCapacity(pbInput, size, 0)
StrPut(header "." payload, &pbInput, "UTF-8")
size--
DllCall("bcrypt\BCryptHashData", "ptr", hHash, "ptr", &pbInput, "uint", size, "uint", 0)
DllCall("bcrypt\BCryptGetProperty", "ptr", hHashAlg, "ptr", &(BCRYPT_HASH_LENGTH := "HashDigestLength"), "uint*", cbHash, "uint", 4, "uint*", cbResult, "uint", 0)
VarSetCapacity(pbHash, cbHash, 0)
DllCall("bcrypt\BCryptFinishHash", "ptr", hHash, "ptr", &pbHash, "uint", cbHash, "uint", 0)
VarSetCapacity(BCRYPT_PKCS1_PADDING_INFO, A_PtrSize, 0)
NumPut(&BCRYPT_SHA256_ALGORITHM, BCRYPT_PKCS1_PADDING_INFO)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", 0, "uint", 0, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)
VarSetCapacity(pbSignature, cbSignature, 0)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", &pbSignature, "uint", cbSignature, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)
signature := Base64URLenc(&pbSignature, cbSignature)
DllCall("bcrypt\BCryptDestroyHash", "ptr", hHash)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hSignAlg, "uint", 0)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hHashAlg, "uint", 0)
DllCall("bcrypt\BCryptDestroyKey", "ptr", phKey)
jwt := header "." payload "." signature

HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
HTTP.Open("POST", authenticationUrl, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.Send("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" jwt)
HTTP.WaitForResponse()
msgbox % HTTP.ResponseText
return


Base64URLenc(pData, size := "")
{
   if (size = "")
   {
      VarSetCapacity(bin, StrPut(pData, "UTF-8"))
      size := StrPut(pData, &bin, "UTF-8") - 1
      pData := &bin
   }
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "ptr", 0, "uint*", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode, 0)
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "str", outData, "uint*", chars)
   outData := StrReplace(outData, "=")
   outData := StrReplace(outData, "+", "-")
   outData := StrReplace(outData, "/", "_")
   return outData
}

CryptStringToBinary(string, ByRef outData)
{
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "ptr", 0, "uint*", bytes, "uint*", 0, "uint*", 0)
   VarSetCapacity(outData, bytes) 
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "str", outData, "uint*", bytes, "uint*", 0, "uint*", 0)
   Return bytes
}
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: JWT Authentication in Box

02 Dec 2021, 22:18

You need to read carefully about jwt standard.
For example "kid" should be in header.
https://www.rfc-editor.org/rfc/rfc7515#section-4
TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

Re: JWT Authentication in Box

03 Dec 2021, 16:55

Ok, I've been studying that documentation for a couple hours and trying different things, but haven't been successful.

I understand that the jwt assertion is just the encrypted header, payload, and signature.
I don't understand what's happening with the private key, or where I would apply the passphrase to decrypt it.

Code: Select all

#NoEnv
#SingleInstance,Force
SetBatchLines, -1

clientID := "#########"
clientSecret := "###########"
enterpriseID := "###########"
publicKeyID := "###########"
privateKey := "-----BEGIN ENCRYPTED PRIVATE KEY-----\n###########\n-----END ENCRYPTED PRIVATE KEY-----\n"
passphrase := "################"

time := A_NowUTC
EnvSub, time, 19700101000000, seconds
expTime := time+45

random, RandomNum, 1000000000000000.0, 9999999999999999.0
RandomNum := floor(RandomNum)

authenticationUrl := "https://api.box.com/oauth2/token"

payload := {"iss":%clientID%,"sub":%enterpriseID%,"box_sub_type":"enterprise","aud":"%authenticationUrl%","jti":%RandomNum%,"exp":%expTime%}

header := {"alg":"RS256","kid":%publicKeyID%}

header := Base64URLenc(header)
payload := Base64URLenc(payload)

private_key := StrReplace(private_key, "\n")
len := CryptStringToBinary(private_key, outData)
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", PKCS_PRIVATE_KEY_INFO := 44, "ptr", &outData, "uint", len, "uint", (CRYPT_DECODE_ALLOC_FLAG := 0x8000)|(CRYPT_DECODE_NOCOPY_FLAG := 0x1), "ptr", 0, "ptr*", PrivateKeyInfo, "uint*", cb)
PrivateKeyInfo_PrivateKey_cbData := numget(PrivateKeyInfo+0, 4*A_PtrSize, "uint")
PrivateKeyInfo_PrivateKey_pbData := numget(PrivateKeyInfo+0, 5*A_PtrSize, "uint")
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", CNG_RSA_PRIVATE_KEY_BLOB := 83, "ptr", PrivateKeyInfo_PrivateKey_pbData, "uint", PrivateKeyInfo_PrivateKey_cbData, "uint", CRYPT_DECODE_ALLOC_FLAG := 0x8000, "ptr", 0, "ptr*", prkb, "uint*", cb)
DllCall("LocalFree", "ptr", PrivateKeyInfo)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hSignAlg, "ptr", &(BCRYPT_RSA_ALGORITHM := "RSA"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptImportKeyPair", "ptr", hSignAlg, "ptr", 0, "ptr", &(BCRYPT_RSAPRIVATE_BLOB := "RSAPRIVATEBLOB"), "ptr*", phKey, "ptr", prkb, "uint", cb, "uint", 0)
DllCall("LocalFree", "ptr", prkb)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hHashAlg, "ptr", &(BCRYPT_SHA256_ALGORITHM := "SHA256"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptCreateHash", "ptr", hHashAlg, "ptr*", hHash, "ptr", 0, "uint", 0, "ptr", 0, "uint", 0 , "uint", 0)

size := StrPut(header "." payload, "UTF-8")
VarSetCapacity(pbInput, size, 0)
StrPut(header "." payload, &pbInput, "UTF-8")
size--
DllCall("bcrypt\BCryptHashData", "ptr", hHash, "ptr", &pbInput, "uint", size, "uint", 0)
DllCall("bcrypt\BCryptGetProperty", "ptr", hHashAlg, "ptr", &(BCRYPT_HASH_LENGTH := "HashDigestLength"), "uint*", cbHash, "uint", 4, "uint*", cbResult, "uint", 0)
VarSetCapacity(pbHash, cbHash, 0)
DllCall("bcrypt\BCryptFinishHash", "ptr", hHash, "ptr", &pbHash, "uint", cbHash, "uint", 0)
VarSetCapacity(BCRYPT_PKCS1_PADDING_INFO, A_PtrSize, 0)
NumPut(&BCRYPT_SHA256_ALGORITHM, BCRYPT_PKCS1_PADDING_INFO)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", 0, "uint", 0, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)
VarSetCapacity(pbSignature, cbSignature, 0)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", &pbSignature, "uint", cbSignature, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)

signature := Base64URLenc(&pbSignature, cbSignature)

DllCall("bcrypt\BCryptDestroyHash", "ptr", hHash)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hSignAlg, "uint", 0)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hHashAlg, "uint", 0)
DllCall("bcrypt\BCryptDestroyKey", "ptr", phKey)

jwt := header "." payload "." signature

HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
HTTP.Open("POST", authenticationUrl, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.Send("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" jwt)
HTTP.WaitForResponse()
response := HTTP.ResponseText
msgbox % response

ESC::
ExitApp


Base64URLenc(pData, size := "")
{
   if (size = "")
   {
      VarSetCapacity(bin, StrPut(pData, "UTF-8"))
      size := StrPut(pData, &bin, "UTF-8") - 1
      pData := &bin
   }
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "ptr", 0, "uint*", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode, 0)
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "str", outData, "uint*", chars)
   outData := StrReplace(outData, "=")
   outData := StrReplace(outData, "+", "-")
   outData := StrReplace(outData, "/", "_")
   return outData
}

CryptStringToBinary(string, ByRef outData)
{
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "ptr", 0, "uint*", bytes, "uint*", 0, "uint*", 0)
   VarSetCapacity(outData, bytes) 
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "str", outData, "uint*", bytes, "uint*", 0, "uint*", 0)
   Return bytes
}
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: JWT Authentication in Box

04 Dec 2021, 06:29

All I see that You removed for some reason "typ":"JWT" from header and for some reason decided to create header and payload objects instead of plain text.
If it is too difficult for You to generate jwt token, try to use different methods of authentication.
TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

Re: JWT Authentication in Box

04 Dec 2021, 16:14

Thanks for your responses, I am learning a lot since I have never done anything like this before.
From what I read the "typ" is optional and the Box documentation said only the "alg" and "kid" are required.
Setting the header/payload as objects was my mistake - thank you for catching that.

Should I be putting any of the variables in the header/payload in quotes besides the authenticationUrl if they don't contain any special characters?

Code: Select all

#NoEnv
#SingleInstance,Force
SetBatchLines, -1

clientID := "#########"
clientSecret := "###########"
enterpriseID := "###########"
publicKeyID := "###########"
privateKey := "-----BEGIN ENCRYPTED PRIVATE KEY-----\n###########\n-----END ENCRYPTED PRIVATE KEY-----\n"
passphrase := "################"

time := A_NowUTC
EnvSub, time, 19700101000000, seconds
expTime := time+45

random, RandomNum, 1000000000000000.0, 9999999999999999.0
RandomNum := floor(RandomNum)

authenticationUrl := "https://api.box.com/oauth2/token"

payload = {"iss":%clientID%,"sub":%enterpriseID%,"box_sub_type":"enterprise","aud":"%authenticationUrl%","jti":%RandomNum%,"exp":%expTime%}

header = {"alg":"RS256","typ":"JWT","kid":%publicKeyID%}

header := Base64URLenc(header)
payload := Base64URLenc(payload)

private_key := StrReplace(private_key, "\n")
len := CryptStringToBinary(private_key, outData)
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", PKCS_PRIVATE_KEY_INFO := 44, "ptr", &outData, "uint", len, "uint", (CRYPT_DECODE_ALLOC_FLAG := 0x8000)|(CRYPT_DECODE_NOCOPY_FLAG := 0x1), "ptr", 0, "ptr*", PrivateKeyInfo, "uint*", cb)
PrivateKeyInfo_PrivateKey_cbData := numget(PrivateKeyInfo+0, 4*A_PtrSize, "uint")
PrivateKeyInfo_PrivateKey_pbData := numget(PrivateKeyInfo+0, 5*A_PtrSize, "uint")
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", CNG_RSA_PRIVATE_KEY_BLOB := 83, "ptr", PrivateKeyInfo_PrivateKey_pbData, "uint", PrivateKeyInfo_PrivateKey_cbData, "uint", CRYPT_DECODE_ALLOC_FLAG := 0x8000, "ptr", 0, "ptr*", prkb, "uint*", cb)
DllCall("LocalFree", "ptr", PrivateKeyInfo)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hSignAlg, "ptr", &(BCRYPT_RSA_ALGORITHM := "RSA"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptImportKeyPair", "ptr", hSignAlg, "ptr", 0, "ptr", &(BCRYPT_RSAPRIVATE_BLOB := "RSAPRIVATEBLOB"), "ptr*", phKey, "ptr", prkb, "uint", cb, "uint", 0)
DllCall("LocalFree", "ptr", prkb)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hHashAlg, "ptr", &(BCRYPT_SHA256_ALGORITHM := "SHA256"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptCreateHash", "ptr", hHashAlg, "ptr*", hHash, "ptr", 0, "uint", 0, "ptr", 0, "uint", 0 , "uint", 0)

size := StrPut(header "." payload, "UTF-8")
VarSetCapacity(pbInput, size, 0)
StrPut(header "." payload, &pbInput, "UTF-8")
size--
DllCall("bcrypt\BCryptHashData", "ptr", hHash, "ptr", &pbInput, "uint", size, "uint", 0)
DllCall("bcrypt\BCryptGetProperty", "ptr", hHashAlg, "ptr", &(BCRYPT_HASH_LENGTH := "HashDigestLength"), "uint*", cbHash, "uint", 4, "uint*", cbResult, "uint", 0)
VarSetCapacity(pbHash, cbHash, 0)
DllCall("bcrypt\BCryptFinishHash", "ptr", hHash, "ptr", &pbHash, "uint", cbHash, "uint", 0)
VarSetCapacity(BCRYPT_PKCS1_PADDING_INFO, A_PtrSize, 0)
NumPut(&BCRYPT_SHA256_ALGORITHM, BCRYPT_PKCS1_PADDING_INFO)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", 0, "uint", 0, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)
VarSetCapacity(pbSignature, cbSignature, 0)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", &pbSignature, "uint", cbSignature, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)

signature := Base64URLenc(&pbSignature, cbSignature)

DllCall("bcrypt\BCryptDestroyHash", "ptr", hHash)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hSignAlg, "uint", 0)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hHashAlg, "uint", 0)
DllCall("bcrypt\BCryptDestroyKey", "ptr", phKey)

jwt := header "." payload "." signature

HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
HTTP.Open("POST", authenticationUrl, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.Send("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" jwt)
HTTP.WaitForResponse()
response := HTTP.ResponseText
msgbox % response

ExitApp


Base64URLenc(pData, size := "")
{
   if (size = "")
   {
      VarSetCapacity(bin, StrPut(pData, "UTF-8"))
      size := StrPut(pData, &bin, "UTF-8") - 1
      pData := &bin
   }
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "ptr", 0, "uint*", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode, 0)
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "str", outData, "uint*", chars)
   outData := StrReplace(outData, "=")
   outData := StrReplace(outData, "+", "-")
   outData := StrReplace(outData, "/", "_")
   return outData
}

CryptStringToBinary(string, ByRef outData)
{
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "ptr", 0, "uint*", bytes, "uint*", 0, "uint*", 0)
   VarSetCapacity(outData, bytes) 
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "str", outData, "uint*", bytes, "uint*", 0, "uint*", 0)
   Return bytes
}
TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

Re: JWT Authentication in Box

04 Dec 2021, 19:13

Thank you, the header/payload should be formatted correctly now.

It appears the signature variable is blank though.
How would I use the passphrase/PrivateKeyPassword on the key taken directly from json below as shown in your p12 example: viewtopic.php?f=6&t=89236

Code: Select all

#NoEnv
#SingleInstance,Force
SetBatchLines, -1

clientID := "#########"
clientSecret := "###########"
enterpriseID := "###########"
publicKeyID := "###########"
privateKey := "-----BEGIN ENCRYPTED PRIVATE KEY-----\n###########\n-----END ENCRYPTED PRIVATE KEY-----\n"
passphrase := "################"

time := A_NowUTC
EnvSub, time, 19700101000000, seconds
expTime := time+45

random, RandomNum, 1000000000000000.0, 9999999999999999.0
RandomNum := floor(RandomNum)
RandomNum := Base64URLenc(RandomNum)

authenticationUrl := "https://api.box.com/oauth2/token"

payload = {"iss":"%clientID%","sub":"%enterpriseID%","box_sub_type":"enterprise","aud":"%authenticationUrl%","jti":"%RandomNum%","exp":%expTime%}

header = {"alg":"RS256","typ":"JWT","kid":"%publicKeyID%"}

header := Base64URLenc(header)
payload := Base64URLenc(payload)

private_key := StrReplace(private_key, "\n")
len := CryptStringToBinary(private_key, outData)
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", PKCS_PRIVATE_KEY_INFO := 44, "ptr", &outData, "uint", len, "uint", (CRYPT_DECODE_ALLOC_FLAG := 0x8000)|(CRYPT_DECODE_NOCOPY_FLAG := 0x1), "ptr", 0, "ptr*", PrivateKeyInfo, "uint*", cb)
PrivateKeyInfo_PrivateKey_cbData := numget(PrivateKeyInfo+0, 4*A_PtrSize, "uint")
PrivateKeyInfo_PrivateKey_pbData := numget(PrivateKeyInfo+0, 5*A_PtrSize, "uint")
DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", CNG_RSA_PRIVATE_KEY_BLOB := 83, "ptr", PrivateKeyInfo_PrivateKey_pbData, "uint", PrivateKeyInfo_PrivateKey_cbData, "uint", CRYPT_DECODE_ALLOC_FLAG := 0x8000, "ptr", 0, "ptr*", prkb, "uint*", cb)
DllCall("LocalFree", "ptr", PrivateKeyInfo)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hSignAlg, "ptr", &(BCRYPT_RSA_ALGORITHM := "RSA"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptImportKeyPair", "ptr", hSignAlg, "ptr", 0, "ptr", &(BCRYPT_RSAPRIVATE_BLOB := "RSAPRIVATEBLOB"), "ptr*", phKey, "ptr", prkb, "uint", cb, "uint", 0)
DllCall("LocalFree", "ptr", prkb)
DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", hHashAlg, "ptr", &(BCRYPT_SHA256_ALGORITHM := "SHA256"), "ptr", 0, "uint", 0)
DllCall("bcrypt\BCryptCreateHash", "ptr", hHashAlg, "ptr*", hHash, "ptr", 0, "uint", 0, "ptr", 0, "uint", 0 , "uint", 0)

size := StrPut(header "." payload, "UTF-8")
VarSetCapacity(pbInput, size, 0)
StrPut(header "." payload, &pbInput, "UTF-8")
size--
DllCall("bcrypt\BCryptHashData", "ptr", hHash, "ptr", &pbInput, "uint", size, "uint", 0)
DllCall("bcrypt\BCryptGetProperty", "ptr", hHashAlg, "ptr", &(BCRYPT_HASH_LENGTH := "HashDigestLength"), "uint*", cbHash, "uint", 4, "uint*", cbResult, "uint", 0)
VarSetCapacity(pbHash, cbHash, 0)
DllCall("bcrypt\BCryptFinishHash", "ptr", hHash, "ptr", &pbHash, "uint", cbHash, "uint", 0)
VarSetCapacity(BCRYPT_PKCS1_PADDING_INFO, A_PtrSize, 0)
NumPut(&BCRYPT_SHA256_ALGORITHM, BCRYPT_PKCS1_PADDING_INFO)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", 0, "uint", 0, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)
VarSetCapacity(pbSignature, cbSignature, 0)
DllCall("bcrypt\BCryptSignHash", "ptr", phKey, "ptr", &BCRYPT_PKCS1_PADDING_INFO, "ptr", &pbHash, "uint", cbHash, "ptr", &pbSignature, "uint", cbSignature, "uint*", cbSignature, "uint", BCRYPT_PAD_PKCS1 := 2)

signature := Base64URLenc(&pbSignature, cbSignature)

DllCall("bcrypt\BCryptDestroyHash", "ptr", hHash)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hSignAlg, "uint", 0)
DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hHashAlg, "uint", 0)
DllCall("bcrypt\BCryptDestroyKey", "ptr", phKey)

jwt := header "." payload "." signature

HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
HTTP.Open("POST", authenticationUrl, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.Send("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" jwt "&client_id=" clientId "&client_secret=" clientSecret)
HTTP.WaitForResponse()

response := HTTP.ResponseText
responsestatus := HTTP.Status

msgbox % response
msgbox % responsestatus


ExitApp


Base64URLenc(pData, size := "")
{
   if (size = "")
   {
      VarSetCapacity(bin, StrPut(pData, "UTF-8"))
      size := StrPut(pData, &bin, "UTF-8") - 1
      pData := &bin
   }
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "ptr", 0, "uint*", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode, 0)
   DllCall("crypt32\CryptBinaryToString", "ptr", pData, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "str", outData, "uint*", chars)
   outData := StrReplace(outData, "=")
   outData := StrReplace(outData, "+", "-")
   outData := StrReplace(outData, "/", "_")
   return outData
}

CryptStringToBinary(string, ByRef outData)
{
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "ptr", 0, "uint*", bytes, "uint*", 0, "uint*", 0)
   VarSetCapacity(outData, bytes) 
   DllCall("Crypt32\CryptStringToBinary", "ptr", &string, "uint", StrLen(string), "uint", CRYPT_STRING_BASE64HEADER := 0, "str", outData, "uint*", bytes, "uint*", 0, "uint*", 0)
   Return bytes
}

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: JWT Authentication in Box

04 Dec 2021, 19:51

I think You need to decode it using CryptDecodeObjectEx with PKCS_ENCRYPTED_PRIVATE_KEY_INFO.
TablePerson22
Posts: 40
Joined: 04 Mar 2021, 20:27

Re: JWT Authentication in Box

04 Dec 2021, 20:20

To clarify - would I need to set "PrivateKeyInfo" to the private_key passphrase?

Code: Select all

DllCall("crypt32\CryptDecodeObjectEx", "uint", (X509_ASN_ENCODING := 1)|(PKCS_7_ASN_ENCODING := 65536), "int", PKCS_PRIVATE_KEY_INFO := 44, "ptr", &outData, "uint", len, "uint", (CRYPT_DECODE_ALLOC_FLAG := 0x8000)|(CRYPT_DECODE_NOCOPY_FLAG := 0x1), "ptr", 0, "ptr*", PrivateKeyInfo, "uint*", cb)
If that's the case, are there any other variables that I need to define in the other DLL calls?

Thank you
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: JWT Authentication in Box

05 Dec 2021, 03:04

PrivateKeyInfo it is pointer to output structure.
are there any other variables that I need to define in the other DLL calls?
I dont know, I have never worked with encrypted private keys.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bubo_Bubo, mikeyww, OrangeCat, RussF and 125 guests