How to convert C# code to Delphi to perform a SHA256 hash and then Base64Url encoding

170 Views Asked by At

I am integrating Xero accounting with my Delphi app. Xero have a VS C# example program, but I've never used C#.

The Xero docs say: The “code challenge” is created by performing a SHA256 hash on the code verifier and then Base64url encoding the hash

The C# code is:

private void btnGenerateLink_Click(object sender, EventArgs e)
{
    //construct the link that the end user will need to visit in order to authorize the app
    var clientId = txtBoxClientID.Text;
    var scopes = Uri.EscapeUriString(txtBoxScopes.Text);
    var redirectUri = txtBoxRedirectURI.Text;
    var state = txtBoxState.Text;

    //generate the code challenge based on the verifier
    string codeChallenge;
    using (var sha256 = SHA256.Create())
    {
        var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(txtBoxCodeVerifier.Text));
        codeChallenge = Convert.ToBase64String(challengeBytes)
            .TrimEnd('=')
            .Replace('+', '-')
            .Replace('/', '_');
    }
    
    var authLink = $"{AuthorisationUrl}?response_type=code&client_id={clientId}&redirect_uri={redirectUri}&scope={scopes}&state={state}&code_challenge={codeChallenge}&code_challenge_method=S256";
    txtBoxAuthLink.Text = authLink;
    btnAuthorise.Enabled = true;
}

How do I re-write this in Delphi 11? I have tried the following:

var
  inputKey, hash : string;
  b64Encoded : string;
  b64url : TBase64UrlEncoding;
begin
  //create the code challenge
  inputKey := eCodeVerifier.Text;

  hash := THashSHA2.GetHashString(inputKey, SHA256);
  b64url := TBase64UrlEncoding.Create;
  b64Encoded := b64url.Encode(hash);
1

There are 1 best solutions below

0
On BEST ANSWER

Not an exact translation of the presented C# code, but hopefully answering your underlying question: here is a function that, given a PKCE code verifier, returns the SHA256 code challenge:

uses
  System.SysUtils, System.Hash, System.NetEncoding;

function GetCodeChallenge(const ACodeVerifier: string): string;
var
  LSHA256: string;
  LSHA256Bytes: TBytes;
begin
  LSHA256Bytes := THashSHA2.GetHashBytes(ACodeVerifier, THashSHA2.TSHA2Version.SHA256);
  LSHA256 := TNetEncoding.Base64.EncodeBytesToString(LSHA256Bytes);
  // The following three lines are what makes
  // a "Base64 encoding" into a "Base 64 URL encoding" - no more magic than that
  Result := StringReplace(LSHA256, '=', '', [rfReplaceAll]);
  Result := StringReplace(Result, '+', '-', [rfReplaceAll]);
  Result := StringReplace(Result, '/', '_', [rfReplaceAll]);
end;

Also worth mentioning, perhaps, is that a PKCE code verifier must be a random string of length min 43 and max 128.