[Java] How to include PKCE Code_Verifier and Code_Challenge in JMeter request

2 minute read

Introduction

A specification called PKCE(RFC 7636) was established as a countermeasure against authorization code interception attacks in OAuth, and codes called Code_Verifier and Code_Challenge are generated on the client side, It is designed to be embedded in the request parameter.

Since the load test using JMeter will be carried out in the development of API service corresponding to PKCE, and the code generation process on the client side will be incorporated into JMeter, the method and procedure incorporated at that time will be explained.

Code_Verifier, Code_Challenge generation

In order to generate the code (Code_Verifier, Code_Challenge) to be embedded in the request with JMeter, generate a jar that contains the class for code generation. The code generation algorithm is defined in RFC 7636 as follows, so implement a Java program that generates code according to the specifications.

code_verifier = high-entropy cryptographic random STRING using the unreserved characters [A-Z] / [a-z] / [0-9] / “-“ / “.” / “_” / “~” from Section 2.3 of [RFC3986], with a minimum length of 43 characters and a maximum length of 128 characters.

Omitted

code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

Omitted

ABNF for “code_challenge” is as follows.

code-challenge = 43*128unreserved unreserved = ALPHA / DIGIT / “-“ / “.” / “_” / “~” ALPHA = %x41-5A / %x61-7A DIGIT = %x30-39

Use commons-codec to use SHA256 hashing algorithm in Code_Challenge generation and commons-lang3 for Base64 encoding.

package com.sample.pkce;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import java.util.Base64;

public class PKCECode {

    private final String CODE_VERFIER_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~";

    public String getCodeVerifier(final int codeVerifierLength) {
        return RandomStringUtils.random(codeVerifierLength, CODE_VERFIER_STRING);
    }

    public String getCodeChallenge(final String codeVerifier) {
        final byte[] hashedCodeVerifier = DigestUtils.sha256(codeVerifier);
        final String base64EncodedCodeVerifier = Base64.getEncoder().encodeToString(hashedCodeVerifier);
        final String base64URLEncodedCodeVerifier = base64EncodedCodeVerifier
                .replaceAll("=","")
                .replaceAll("\\+","-")
                .replaceAll("/","_");
        return base64URLEncodedCodeVerifier;
    }
}

To confirm whether the implemented program can generate Code_Challenge correctly, use the same CodeVerifier as the conversion example of RFC 7636 Appendix. I tested it and confirmed that the conversion was successful.

@Test
public void getCodeChallengeTest() {
    final String codeVerifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
    final PKCECode pkceCode = new PKCECode();
    Assert.assertEquals("E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", pkceCode.getCodeChallenge(codeVerifier));
}

Request with JMeter

Before starting JMeter, store the commons-codec and commons-lang3 of the used library and the generated jar file in JMeter’s external lib folder (<JMeter installation directory>\lib\ext) ..

After that, start JMeter, select BeanShell PreProcessor from “Add” and “Preprocess” in the test plan, and enter the following script.

import com.sample.pkce.PKCECode

PKCECode pkceCode = new PKCECode();
String codeVerifier = pkceCode.getCodeVerifier(43); //Specify the character length of Code_Verifier
String codeChallenge= pkceCode.getCodeChallenge(codeVerifier);

vars.put("codeVerifier",codeVerifier );
vars.put("codeChallenge",codeChallenge);

Then, set the value embedded in the script with ${codeVerifier} and ${codeChallenge} in the request parameters of the HTTP request.

Supplement

This time, I explained how to incorporate the code generation process with BeanShell PreProcessor. However, if code generation is performed every time, the processing load will be applied to the client and it may not be possible to apply the load as expected. Therefore, when performing a performance load test, it is recommended to select one of the following two ways.

  • How to output a large amount of code to a CSV file in advance and load it with JMeter’s ``
  • Set Code_Verifier and Code_Challenge as fixed values in all requests and set them directly in JMeter parameters.
    • If the fixed value is acceptable, there is also a method to use the conversion example of Appendix of RFC 7636 without specially generating new code.

Reference

I explained in detail here about why it is 43 to 128 characters. Please refer to it.

  • https://techblog.yahoo.co.jp/entry/20191219790463/