79190649

Date: 2024-11-14 22:58:15
Score: 1
Natty:
Report link

Many thanks to Topaco for pointing me in the right direction. Using their code, I was able to make some more progress. But, when I tried their solution, I still ran into this weird could not find file specified error. However, all I needed to do was adjust some IIS settings to resolve that issue. I set up a new app pool, enabled 32-bit applications, and set "Load user profile" to True.

Unfortunately, I still couldn't generate valid tokens. One issue was getting the right date format, so after some more Googling I modified some code I found to get dates generating in the correct format.

Later, after meeting with a developer from the payment processor, I realized I was trying to validate my tokens incorrectly. I didn't have the public key to go with my private key, so I was doomed to get "Invalid Token" on jwt.io. I then checked the tokens generated with Topaco's first solution using CngKey's, and my token was indeed valid. Once I knew there wasn't an issue with the tokens, I also fixed the API link I was using and voila, my requests were going through!

All in all, here's a simplified version of the code I have ended up with. All this requires is installing jose-jwt and RestSharp NuGet packages.

Imports Jose
Imports RestSharp
Imports System.Net
Imports System.Net.Http
Imports System.Security.Cryptography
Imports System.Web.Http

Module MyModule

    Public Const KEY_ID = "...."

    Public Const PRIVATE_KEY =
"-----BEGIN PRIVATE KEY-----
.....
-----END PRIVATE KEY-----"

    'Gets proper format for JWT time
    Public Function GetSecondsSinceEpoch(utcTime As DateTime) As Int64
        Return (utcTime - New DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds
    End Function

    'Removes the enclosure and any whitespace
    Public Shared Function ConvertPkcs8PemToDer(ByVal pkcs8Pem As String) As Byte()
        Dim body As String = pkcs8Pem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace(Environment.NewLine, "")
        Dim reg = New Regex("\s")
        body = reg.Replace(body, "")
        Return Convert.FromBase64String(body)
    End Function

    'Creates a UUID-formatted string.
    Public Shared Function CreateJti() As String
        Return Guid.NewGuid().ToString()
    End Function

    Public Function MakeMyRequest(uri As String, json_body As String) As String

        'Fixes a weird .NET bug
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls12 Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls
        
        'Create RestRequest things 
        Dim rest_uri As New Uri(uri) 'Uri of the API to call
        Dim rest_client As New RestClient With {.BaseUrl = rest_uri}
        Dim rest_request As New RestRequest With {.Method = Method.POST} 'Or whatever Method your API requires
        Dim rest_response As New RestResponse

        'Add JSON body and headers
        rest_request.AddJsonBody(json_body) 'Make sure this matches the API specifications
        rest_request.AddHeader("accept", "application/json")
        rest_request.AddHeader("Content-Type", "application/json")

        'Conver the PEM string to Pkcs8 format, acceptable to CngKey.Import()
        Dim privatePkcs8Der As Byte() = ConvertPkcs8PemToDer(PRIVATE_KEY)
        Dim privateEcKey As CngKey = CngKey.Import(privatePkcs8Der, CngKeyBlobFormat.Pkcs8PrivateBlob)

        'Claims and headers
        Dim iss = "MyIssuer"
        Dim aud = "MyAudience"
        Dim iat = GetSecondsSinceEpoch(Now().ToUniversalTime()) '.ToUniversalTime ensures your local UTC offset doesn't affect the timestamp
        Dim exp = GetSecondsSinceEpoch(Now().AddMinutes(5).ToUniversalTime())
        Dim kid = KEY_ID

        Dim headers = New Dictionary(Of String, Object)() From {
            {"alg", "ES512"},
            {"typ", "JWT"},
            {"kid", KEY_ID} 'Key ID may not be necessary for your scenario, but put whatever extra parameters are required here like username, password, etc
        }

        Dim payload = New Dictionary(Of String, Object)() From {
            {"iss", iss},
            {"aud", aud},
            {"iat", iat},
            {"exp", exp},
            {"jti", CreateJti()}
        }

        'Generate the token using the payload, CngKey, ES512 algorithm, and extra headers
        Dim token = Jose.JWT.Encode(payload, privateEcKey, JwsAlgorithm.ES512, headers)

        'Excecute the request
        rest_request.AddHeader("Authorization", "Bearer " & token)
        rest_response = rest_client.Execute(rest_request)

        'Check for your API's designated response code
        If rest_response.StatusCode <> 201 Then Throw New Exception("An error occurred processing the request.")
        Return rest_response.Content

    End Function

End Module
Reasons:
  • Blacklisted phrase (0.5): thanks
  • Blacklisted phrase (0.5): I need
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (1):
Posted by: h31mdallr