Using Groovy in JMeter to Sign Request Parameters with RSA
This article demonstrates how to use Groovy scripts within JMeter's JSR223 pre‑processor to generate RSA signatures for request parameters, covering key handling, encryption, decryption, signing, verification, map conversion, and how to attach the signature to the HTTP sampler for automated performance testing.
The post continues a series on applying Groovy in JMeter, focusing on signing request parameters that depend on other values such as tokens, a common scenario in API testing for PHP back‑ends.
First, a simple Thread Group and HTTP Request are created, then a JSR223 PreProcessor is added. The Groovy script defines constants for RSA block sizes, placeholders for the private and public keys, and a list of utility methods:
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
int MAX_ENCRYPT_BLOCK = 117;
int MAX_DECRYPT_BLOCK = 128;
String RSA_PRIVATE_KEY = "<private_key>";
String RSA_PUBLIC_KEY = "<public_key>";
String excludeKey = "sign";
public PrivateKey getPrivateKey(String privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
return keyFactory.generatePrivate(keySpec);
}
public PublicKey getPublicKey(String publicKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
return keyFactory.generatePublic(keySpec);
}
String encrypt(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = data.getBytes().length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0, i = 0;
byte[] cache;
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return new String(Base64.encodeBase64String(encryptedData));
}
public String decrypt(String data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] dataBytes = Base64.decodeBase64(data);
int inputLen = dataBytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0, i = 0;
byte[] cache;
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData, "UTF-8");
}
public String sign(String data, PrivateKey privateKey) throws Exception {
byte[] keyBytes = privateKey.getEncoded();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(key);
signature.update(data.getBytes());
return new String(Base64.encodeBase64(signature.sign()));
}
public boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
byte[] keyBytes = publicKey.getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(key);
signature.update(srcData.getBytes());
return signature.verify(Base64.decodeBase64(sign.getBytes()));
}
public String sign(Map<String, String> mapData, PrivateKey privateKey) throws Exception {
return sign(getStr(mapData), privateKey);
}
public static boolean verify(Map<String, String> mapData, PublicKey publicKey) throws Exception {
String sign = mapData.remove("sign");
if (sign == null || sign.length() < 1) {
throw new RuntimeException("参数缺少签名");
}
return verify(getStr(mapData), publicKey, sign);
}
public String getStr(Map<String, String> parms) {
parms.remove("sign");
TreeMap<String, String> sortParms = new TreeMap<>();
sortParms.putAll(parms);
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : parms.entrySet()) {
builder.append(entry.getKey());
builder.append("=");
builder.append(String.valueOf(entry.getValue()));
builder.append("&");
}
if (builder.length() > 1) {
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
vars.put("MY1", "flow");
props.put("MY", "ewewewerr");
log.info(props.get("MY"));
sampler.addArgument("name", "funteddster");
sampler.addArgument("pwd", "funtddester");
def args = sampler.getArguments();
def ss = [:];
log.info(sampler.getArguments().toString());
args.getArgumentCount().times {
def a = args.getArgument(it);
ss.put(a.ARG_NAME, a.VALUE);
}
def my_var = sign(ss, getPrivateKey(RSA_PRIVATE_KEY)) as String;
log.warn "输出参数-------- ${vars} console";
log.info "222222 " + my_var;
sampler.addArgument("sign", my_var);The script extracts variables, builds a map of request parameters, generates a deterministic string representation, signs it with the RSA private key, logs the signature, and finally adds the sign parameter to the HTTP sampler.
Running the test produces console logs showing the JMeter engine start, the execution of the Groovy script, and the generated signature value. The View Results Tree listener clearly displays the sign field in the request payload, confirming that the signature has been attached successfully.
At the end of the article, a disclaimer notes that the content is original to “FunTester” and should not be republished without permission, followed by a curated list of other technical articles.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
