CVE-2021-32791 (Other, published 2021-07-26T17:15Z)
Description: value: mod_auth_openidc is an authentication/authorization module for the Apache 2.x HTTP server that functions as an OpenID Connect Relying Party, authenticating users against an OpenID Connect Provider. In mod_auth_openidc before version 2.4.9, the AES GCM encryption in mod_auth_openidc uses a static IV and AAD. It is important to fix because this creates a static nonce and since aes-gcm is a stream cipher, this can lead to known cryptographic issues, since the same key is being reused. From 2.4.9 onwards this has been patched to use dynamic values through usage of cjose AES encryption routines.
Severity: MEDIUM (CVSS v3 base score: 5.9)
Weakness: CWE-330 \u2014 Use of Insufficiently Random Values
The product uses insufficiently random numbers or values in a security context that depends on unpredictable numbers.
Repository: https://github.com/zmartzone/mod_auth_openidc
Files changed:
.gitignore
ChangeLog
Dockerfile
configure.ac
openidc.conf
src/cache/common.c
test/mod_auth_openidc.jmx
Commit message: use encrypted JWTs for storing encrypted cache contents

- avoid using static AAD/IV; thanks @niebardzo
- bump to 2.4.9-dev

Signed-off-by: Hans Zandbelt <hans.zandbelt@zmartzone.eu>

Vulnerable code:
#RUN apt-get update && apt-get install -y libcjose-dev

# docker run -p 443:443 -it 749d1204d189 /bin/bash -c "source /etc/apache2/envvars && valgrind --leak-check=full /usr/sbin/apache2 -X"
AC_INIT([mod_auth_openidc],[2.4.8.4],[hans.zandbelt@zmartzone.eu])
#define oidc_cache_crypto_openssl_error(r, fmt, ...) \
		oidc_error(r, "%s: %s", apr_psprintf(r->pool, fmt, ##__VA_ARGS__), ERR_error_string(ERR_get_error(), NULL))

#define OIDC_CACHE_CIPHER							EVP_aes_256_gcm()
#define OIDC_CACHE_TAG_LEN							16

#if (OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER))
#define OIDC_CACHE_CRYPTO_GET_TAG					EVP_CTRL_AEAD_GET_TAG
#define OIDC_CACHE_CRYPTO_SET_TAG					EVP_CTRL_AEAD_SET_TAG
#define OIDC_CACHE_CRYPTO_SET_IVLEN					EVP_CTRL_AEAD_SET_IVLEN
#else
#define OIDC_CACHE_CRYPTO_GET_TAG					EVP_CTRL_GCM_GET_TAG
#define OIDC_CACHE_CRYPTO_SET_TAG					EVP_CTRL_GCM_SET_TAG
#define OIDC_CACHE_CRYPTO_SET_IVLEN					EVP_CTRL_GCM_SET_IVLEN
#endif

/*
 * AES GCM encrypt
 */
static int oidc_cache_crypto_encrypt_impl(request_rec *r,
		unsigned char *plaintext, int plaintext_len, const unsigned char *aad,
		int aad_len, unsigned char *key, const unsigned char *iv, int iv_len,
		unsigned char *ciphertext, const unsigned char *tag, int tag_len) {
	EVP_CIPHER_CTX *ctx;

	int len;

	int ciphertext_len;

	/* create and initialize the context */
	if (!(ctx = EVP_CIPHER_CTX_new())) {
		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
		return -1;
	}

	/* initialize the encryption cipher */
	if (!EVP_EncryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
		oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
		return -1;
	}

	/* set IV length */
	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
		return -1;
	}

	/* initialize key and IV */
	if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
		oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
		return -1;
	}

	/* provide AAD data */
	if (!EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) {
		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
				aad_len);
		return -1;
	}

	/* provide the message to be encrypted and obtain the encrypted output */
	if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
		oidc_cache_crypto_openssl_error(r, "EVP_EncryptUpdate ciphertext");
		return -1;
	}
	ciphertext_len = len;

	/*
	 * finalize the encryption; normally ciphertext bytes may be written at
	 * this stage, but this does not occur in GCM mode
	 */
	if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
		oidc_cache_crypto_openssl_error(r, "EVP_EncryptFinal_ex");
		return -1;
	}
	ciphertext_len += len;

	/* get the tag */
	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_GET_TAG, tag_len,
			(void *) tag)) {
		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
		return -1;
	}

	/* clean up */
	EVP_CIPHER_CTX_free(ctx);

	return ciphertext_len;
}
 * AES GCM decrypt
static int oidc_cache_crypto_decrypt_impl(request_rec *r,
		unsigned char *ciphertext, int ciphertext_len, const unsigned char *aad,
		int aad_len, const unsigned char *tag, int tag_len, unsigned char *key,
		const unsigned char *iv, int iv_len, unsigned char *plaintext) {
	EVP_CIPHER_CTX *ctx;
	int len;
	int plaintext_len;
	int ret;

	/* create and initialize the context */
	if (!(ctx = EVP_CIPHER_CTX_new())) {
		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
		return -1;
	}
	/* initialize the decryption cipher */
	if (!EVP_DecryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
		oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
		return -1;
	}
	/* set IV length */
	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
		return -1;
	}

	/* initialize key and IV */
	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
		oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
		return -1;
	}

	/* provide AAD data */
	if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) {
		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
				aad_len);
		return -1;
	}

	/* provide the message to be decrypted and obtain the plaintext output */
	if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate ciphertext");
		return -1;
	}
	plaintext_len = len;

	/* set expected tag value; works in OpenSSL 1.0.1d and later */
	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_TAG, tag_len,
			(void *) tag)) {
		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
		return -1;
	}
	/*
	 * finalize the decryption; a positive return value indicates success,
	 * anything else is a failure - the plaintext is not trustworthy
	 */
	ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
	/* clean up */
	EVP_CIPHER_CTX_free(ctx);

	if (ret > 0) {
		/* success */
		plaintext_len += len;
		return plaintext_len;
	} else {
		/* verify failed */
		oidc_cache_crypto_openssl_error(r, "EVP_DecryptFinal_ex");
		return -1;
	}
}

/*
 * static AAD value for encryption/decryption
 */
static const unsigned char OIDC_CACHE_CRYPTO_GCM_AAD[] = { 0x4d, 0x23, 0xc3,
		0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78,
		0xde };

/*
 * static IV value for encryption/decryption
 */
static const unsigned char OIDC_CACHE_CRYPTO_GCM_IV[] = { 0x00, 0x01, 0x02,
		0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
		0x0f };

/*
 * AES GCM encrypt using the static AAD and IV
 */
static int oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext,
		unsigned char *key, char **result) {
	char *encoded = NULL, *p = NULL, *e_tag = NULL;
	unsigned char *ciphertext = NULL;
	int plaintext_len, ciphertext_len, encoded_len, e_tag_len;
	unsigned char tag[OIDC_CACHE_TAG_LEN];

	/* allocate space for the ciphertext */
	plaintext_len = strlen(plaintext) + 1;
	ciphertext = apr_pcalloc(r->pool,
			(plaintext_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER)));

	ciphertext_len = oidc_cache_crypto_encrypt_impl(r,
			(unsigned char *) plaintext, plaintext_len,
			OIDC_CACHE_CRYPTO_GCM_AAD, sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), key,
			OIDC_CACHE_CRYPTO_GCM_IV, sizeof(OIDC_CACHE_CRYPTO_GCM_IV),
			ciphertext, tag, sizeof(tag));

	/* base64url encode the resulting ciphertext */
	encoded_len = oidc_base64url_encode(r, &encoded, (const char *) ciphertext,
			ciphertext_len, 1);
	if (encoded_len > 0) {
		p = encoded;

		/* base64url encode the tag */
		e_tag_len = oidc_base64url_encode(r, &e_tag, (const char *) tag,
				OIDC_CACHE_TAG_LEN, 1);

		/* now allocated space for the concatenated base64url encoded ciphertext and tag */
		encoded = apr_pcalloc(r->pool, encoded_len + 1 + e_tag_len + 1);
		memcpy(encoded, p, encoded_len);
		p = encoded + encoded_len;
		*p = OIDC_CHAR_DOT;
		p++;

		/* append the tag in the buffer */
		memcpy(p, e_tag, e_tag_len);
		encoded_len += e_tag_len + 1;

		/* make sure the result is \0 terminated */
		encoded[encoded_len] = '\0';

		*result = encoded;
	}

	return encoded_len;
 * AES GCM decrypt using the static AAD and IV
static int oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
		unsigned char *key, unsigned char **plaintext) {

	int len = -1;
	/* grab the base64url-encoded tag after the "." */
	char *encoded_tag = strstr(cache_value, ".");
	if (encoded_tag == NULL) {
		oidc_error(r,
				"corrupted cache value: no tag separator found in encrypted value");
		return FALSE;
	}
	/* make sure we don't modify the original string since it may be just a pointer into the cache (shm) */
	cache_value = apr_pstrmemdup(r->pool, cache_value,
			strlen(cache_value) - strlen(encoded_tag));
	encoded_tag++;
	/* base64url decode the ciphertext */
	char *d_bytes = NULL;
	int d_len = oidc_base64url_decode(r->pool, &d_bytes, cache_value);
	/* base64url decode the tag */
	char *t_bytes = NULL;
	int t_len = oidc_base64url_decode(r->pool, &t_bytes, encoded_tag);
	/* see if we're still good to go */
	if ((d_len > 0) && (t_len > 0)) {
		/* allocated space for the plaintext */
		*plaintext = apr_pcalloc(r->pool,
				(d_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER) - 1));

		/* decrypt the ciphertext providing the tag value */

		len = oidc_cache_crypto_decrypt_impl(r, (unsigned char *) d_bytes,
				d_len, OIDC_CACHE_CRYPTO_GCM_AAD,
				sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), (unsigned char *) t_bytes,
				t_len, key, OIDC_CACHE_CRYPTO_GCM_IV,
				sizeof(OIDC_CACHE_CRYPTO_GCM_IV), *plaintext);

		/* check the result and make sure it is \0 terminated */
		if (len > -1) {
			(*plaintext)[len] = '\0';
		} else {
			*plaintext = NULL;
		}

	}

	return len;
}

/*
 * hash the crypto passhphrase so it has enough key length for AES GCM 256
 */
static unsigned char *oidc_cache_hash_passphrase(request_rec *r,
		const char *passphrase) {

	unsigned char *key = NULL;
	unsigned int key_len = 0;
	oidc_jose_error_t err;

	if (oidc_jose_hash_bytes(r->pool, OIDC_JOSE_ALG_SHA256,
			(const unsigned char *) passphrase, strlen(passphrase), &key,
			&key_len, &err) == FALSE) {
		oidc_error(r, "oidc_jose_hash_bytes returned an error: %s", err.text);
		return NULL;
	}

	return key;
static char *oidc_cache_get_hashed_key(request_rec *r, const char *passphrase,
		const char *key) {
	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256,
			input, &output) == FALSE) {
		oidc_error(r,
				"oidc_util_hash_string_and_base64url_encode returned an error");
	rc = (oidc_cache_crypto_decrypt(r, cache_value,
			oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
			(unsigned char **) value) > 0);
			if (oidc_cache_crypto_encrypt(r, value,
					oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
					&encoded) <= 0)
<jmeterTestPlan version="1.2" properties="3.1" jmeter="3.1.20170220">
          <elementProp name="X_APP_SERVER" elementType="Argument">
            <stringProp name="Argument.name">X_APP_SERVER</stringProp>
            <stringProp name="Argument.value">/protected/api/index.php</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="Hosts Data Set" enabled="true">
          <stringProp name="filename">hosts.txt</stringProp>
          <stringProp name="fileEncoding"></stringProp>
          <stringProp name="variableNames">APP_SERVER</stringProp>
          <stringProp name="delimiter">,</stringProp>
          <boolProp name="quotedData">false</boolProp>
          <boolProp name="recycle">true</boolProp>
          <boolProp name="stopThread">false</boolProp>
          <stringProp name="shareMode">shareMode.all</stringProp>
        </CSVDataSet>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
              <stringProp name="Assertion.test_field">Assertion.response_data_as_document</stringProp>
        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="Hosts Data Set" enabled="true">
          <stringProp name="filename">hosts.txt</stringProp>
          <stringProp name="fileEncoding"></stringProp>
          <stringProp name="variableNames">APP_SERVER</stringProp>
          <stringProp name="delimiter">,</stringProp>
          <boolProp name="quotedData">false</boolProp>
          <boolProp name="recycle">true</boolProp>
          <boolProp name="stopThread">false</boolProp>
          <stringProp name="shareMode">shareMode.all</stringProp>
        </CSVDataSet>
        <hashTree/>

Fixed code:
/config.guess~
/config.sub~
06/10/2021
- use encrypted JWTs for storing encrypted cache contents and avoid using static AAD/IV; thanks @niebardzo
- bump to 2.4.9-dev

ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && apt-get install -y libapache2-mod-php 
RUN mkdir -p /var/www/html/protected
RUN echo "<html><body><h1>Hello, <?php echo($_SERVER['REMOTE_USER']) ?></h1><pre><?php print_r(array_map(\"htmlentities\", apache_request_headers())); ?></pre><a href=\"/protected/?logout=https%3A%2F%2Flocalhost.zmartzone.eu%2Floggedout.html\">Logout</a></body></html>" >  /var/www/html/protected/index.php
RUN mkdir -p /var/www/html/api && cp /var/www/html/protected/index.php /var/www/html/api

# docker run -p 443:443 -it mod_auth_openidc /bin/bash -c "source /etc/apache2/envvars && valgrind --leak-check=full /usr/sbin/apache2 -X"
AC_INIT([mod_auth_openidc],[2.4.9-dev],[hans.zandbelt@zmartzone.eu])
OIDCScope "openid email profile"

#define OIDC_CACHE_CRYPTO_JSON_KEY "c"
 * AES GCM encrypt using the crypto passphrase as symmetric key
static apr_byte_t oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext, const char *key,
		char **result) {
	apr_byte_t rv = FALSE;
	json_t *json = NULL;
	json = json_object();
	json_object_set_new(json, OIDC_CACHE_CRYPTO_JSON_KEY, json_string(plaintext));
	rv = oidc_util_jwt_create(r, (const char*) key, json, result);
	if (json)
		json_decref(json);
	return rv;
 * AES GCM decrypt using the crypto passphrase as symmetric key
static apr_byte_t oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
		const char *key, char **plaintext) {
	apr_byte_t rv = FALSE;
	json_t *json = NULL;
	rv = oidc_util_jwt_verify(r, (const char*) key, cache_value, &json);
	if (rv == FALSE)
		goto end;
	rv = oidc_json_object_get_string(r->pool, json, OIDC_CACHE_CRYPTO_JSON_KEY, plaintext, NULL);
	end:
	if (json)
		json_decref(json);
	return rv;
static char* oidc_cache_get_hashed_key(request_rec *r, const char *passphrase, const char *key) {
	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256, input, &output)
			== FALSE) {
		oidc_error(r, "oidc_util_hash_string_and_base64url_encode returned an error");
	rc = oidc_cache_crypto_decrypt(r, cache_value, cfg->crypto_passphrase, value);
			if (oidc_cache_crypto_encrypt(r, value, cfg->crypto_passphrase, &encoded) == FALSE)
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1.20190725">
          <elementProp name="APP_SERVER" elementType="Argument">
            <stringProp name="Argument.name">APP_SERVER</stringProp>
            <stringProp name="Argument.value">/api/index.php</stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
              <stringProp name="Assertion.custom_message"></stringProp>
              <stringProp name="Assertion.custom_message"></stringProp>
          <boolProp name="ignoreFirstLine">false</boolProp>
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Accept</stringProp>
              <stringProp name="Header.value">*/*</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Accept</stringProp>
              <stringProp name="Header.value">application/json</stringProp>
            </elementProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
              <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
              <stringProp name="Assertion.custom_message"></stringProp>