JMeter入门9---Tcp sampler AES加密
Max.Bai
2018-06
压测tcp请求的时候需要对数据进行处理才发送,比如AES数据加密。实现方法可以有两种,一种自己写java请求,不适用默认tcp sampler, 第二种用默认的Tcp sampler,beanshell实现AES加密。
记录下beanshell实现过程。
1. 添加TCP Sampler
EOL 设置为10 表示回车符号
2. 在TCP sampler添加 Add->Pre Process -> BeanShell PreProcess
3. 在BeanShell PreProcess 脚本里面添加如下代码。
AES 加密
import org.apache.jmeter.protocol.tcp.sampler.*; // tcp sample lib
import org.apache.jmeter.samplers.*; // jmeter samplers lib
import org.apache.jmeter.config.*;
import com.alibaba.fastjson.JSON; //fastjson jar save in /lib/ext folder or load in testplan
import com.alibaba.fastjson.JSONObject;
source("./telutils/AESCryptUtils.java"); //path start with bin as default
String IV_PARAMETER = "941c2d70a830c950";
String key = "941c2d70a830c950";
String body = sampler.getRequestData(); // get tcp sample request data
log.info("PreProcessor===========================================" + body);
timestamp = System.currentTimeMillis(); // timestamp
String seq_num ="201806071746210003";
String s = String.format("abc %s %s", new Object[]{timestamp, seq_num}); //format string
log.info("formate test --->" + s);
log.info("dev id------->" + vars.get("dev_num")); // get vars, set vars.put("abc", "123");
//parse json string to object
JSONObject parseObject = JSON.parseObject(body);
// get data
// data to string
String data = parseObject.getString("data");
// string aes encode
String aesbody = AESCryptUtils.encode(data, key, IV_PARAMETER);
// String aesbody = "MTIzNDU2Nzg5MDA5ODc2NTQzMjExMjM0NTY3ODkwMDk";
// set data
parseObject.put("data", aesbody);
parseObject.put("deviceID", "DEVDD" + vars.get("dev_num"));
// add header to request
String postData = "CST(" + parseObject.toJSONString() + ")\r\n"; // object to json string
log.info("PreProcessor===========================================" + postData);
//postData = "{\"code\":1002,\"deviceId\":\"0000111122223333aaaabbbb\",\"data\":\"MTIzNDU2Nzg5MDA5ODc2NTQzMjExMjM0NTY3ODkwMDk=\"}";
sampler.setRequestData(postData); // set tcp sampole request data
Post process AES 解密过程
String IV_PARAMETER = "941c2d70a830c950";
String key = "PINd6af6c3134308";
// CTS({"code":"1003","data":"0FZqNrjLsKFZhB7iqKT1ZqpAh49i371GDBCen6/nFrgo7m5lXF1h0P95ru5eQ7UHgvau20ZHOJ/s9DoSBpi+7Kpgj1RzNKOy+Nc5E7W+mQQHiAKiyM6BcX8h9TmTfWHV8mSYxqij1zMB3+59+gC/wPRdhi9WkCEdWkKhkevA8BI/oEtsHUZ6kys9kcqnvT/p7hXjnZvpYcrv5q3GilY7JYS3sQnmP2WfgKu3xnzfWRTCUK3NkjmS6IiCYJIrDlGTenC3sYWikgZOnCoxJS5v/A=="})
String strbody = prev.getResponseDataAsString(); // get tcp response string
int x = strbody.indexOf("{");
int y = strbody.indexOf("}");
String codebody = strbody.substring(x,y+1);
log.info("codebody===========================================" + codebody);
JSONObject parseObject = JSON.parseObject(codebody);
String aesdata = parseObject.getString("data");
log.info("aesdata===========================================" + aesdata);
//String aesdata = "0FZqNrjLsKFZhB7iqKT1ZqpAh49i371GDBCen6/nFrgo7m5lXF1h0P95ru5eQ7UHgvau20ZHOJ/s9DoSBpi+7Kpgj1RzNKOy+Nc5E7W+mQQHiAKiyM6BcX8h9TmTfWHV8mSYxqij1zMB3+59+gC/wPRdhi9WkCEdWkKhkevA8BI/oEtsHUZ6kys9kcqnvT/p7hXjnZvpYcrv5q3GilY7JYS3sQnmP2WfgKu3xnzfWRTCUK3NkjmS6IiCYJIrDlGTenC3sYWikgZOnCoxJS5v/A==";
String body = AESCryptUtils.decode(aesdata, key, IV_PARAMETER); // decode string
log.info("decrypto===========================================" + body);
4. 代码中包含了fastjson, AES加密脚本,AES加密脚本内容如下:
fastjson 在testplan导入,或者直接放在jmeter lib/ext 目录先,source到如的文件开始目录默认是/bin
package com.maxbai.test.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
/**
* @date 17/11/21 下午2:30
* AES对称加密工具类
*/
public class AESCryptUtils {
private static final String IV_PARAMETER = "176d3805f01deeab";
//编码方式
private static final String CHARSET_NAME = "UTF-8";
private static final String KEY_ALGORITHM = "AES";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
/**
* 加密
*
* @param data 加密内容
* @param key 原始密钥
* @param iv 16bytes初始向量
* @return
*/
public static String encode(String data, String key, String iv) {
return encrypt(data, key, iv);
}
public static String encode(String data, String key) {
return encrypt(data, md5Hex16(key), IV_PARAMETER);
}
/**
* 加密
*
* @param data 加密内容
* @param key 原始密钥
* @param iv 16bytes初始向量
* @return
*/
public static String decode(String data, String key, String iv) {
return decrypt(data, key, iv);
}
public static String decode(String data, String key) {
return decrypt(data, md5Hex16(key), IV_PARAMETER);
}
/**
* 16位md5加密
*
* @param key 原始密钥
* @return
*/
public static String md5Hex16(String key) {
return DigestUtils.md5Hex(key.getBytes()).substring(8, 24);
}
/**
* 加密
*
* @param data 加密内容
* @param md5Key 16位md5后的密钥
* @param iv 初始向量
* @return
*/
private static String encrypt(String data, String md5Key, String iv) {
try {
IvParameterSpec zeroIv = new IvParameterSpec(iv.getBytes());
//两个参数,第一个为私钥字节数组, 第二个为加密方式 AES或者DES
SecretKeySpec key = new SecretKeySpec(md5Key.getBytes(), KEY_ALGORITHM);
//实例化加密类,参数为加密方式,要写全
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//初始化,此方法可以采用三种方式,按加密算法要求来添加。(1)无第三个参数(2)第三个参数为SecureRandom random = new SecureRandom();中random对象,随机数。(AES不可采用这种方法)(3)采用此代码中的IVParameterSpec
cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
//加密操作,返回加密后的字节数组,然后需要编码。主要编解码方式有Base64, HEX, UUE,7bit等等。此处看服务器需要什么编码方式
byte[] encryptedData = cipher.doFinal(data.getBytes(CHARSET_NAME));
return Base64.encodeBase64String(encryptedData);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
* 解密
*
* @param data 解密内容
* @param strKey 16位密钥
* @param iv 初始向量
* @return
*/
private static String decrypt(String data, String strKey, String iv) {
try {
byte[] byteMi = Base64.decodeBase64(data);
// byte[] byteMi = new BASE64Decoder().decodeBuffer(data);
IvParameterSpec zeroIv = new IvParameterSpec(iv.getBytes());
SecretKeySpec key = new SecretKeySpec(strKey.getBytes(), KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//与加密时不同MODE:Cipher.DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
byte[] decryptedData = cipher.doFinal(byteMi);
return new String(decryptedData, CHARSET_NAME);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
* 测试
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//初始向量,AES为16bytes
String IV_PARAMETER = "941c2d70a830c950";
String content = "{\"deviceId\":\"MC93c9c2d8ac435db6019ba619d6a77d\",\"sequence\":\"167783\",\"time\":1515582588066,\"version\":\"XXX.YYY.ZZZ.MMM\"}";
String key = "941c2d70a830c950";
//md5密钥
System.out.println("16位md5密钥:" + md5Hex16(key));
//偏移量
System.out.println("初始向量:" + IV_PARAMETER);
// 加密
System.out.println("加密前:" + content);
String encryptResult = encode(content, key, IV_PARAMETER);
System.out.println("加密后:" + new String(encryptResult));
// 解密
String decryptResult = decode(encryptResult, key, IV_PARAMETER);
System.out.println("解密后:" + new String(decryptResult));
}
}
5. timestamp 在benshell中的获得
timestamp = System.currentTimeMillis(); // timestamp
6. String.format() 在benshell中只有2个参数,一个是string,一个是Object[].
String s = String.format("abc %s %s", new Object[]{timestamp, seq_num}); //format string
附、Bean Shell常用内置变量
JMeter在它的BeanShell中内置了变量,用户可以通过这些变量与JMeter进行交互,其中主要的变量及其使用方法如下:
log:写入信息到jmeber.log文件,使用方法:log.info(“This is log info!”);
ctx:该变量引用了当前线程的上下文,使用方法可参考:org.apache.jmeter.threads.JMeterContext。
vars - (JMeterVariables):操作jmeter变量,这个变量实际引用了JMeter线程中的局部变量容器(本质上是Map),它是测试用例与BeanShell交互的桥梁,常用方法:
a) vars.get(String key):从jmeter中获得变量值
b) vars.put(String key,String value):数据存到jmeter变量中
更多方法可参考:org.apache.jmeter.threads.JMeterVariables
props - (JMeterProperties - class java.util.Properties):操作jmeter属性,该变量引用了JMeter的配置信息,可以获取Jmeter的属性,它的使用方法与vars类似,但是只能put进去String类型的值,而不能是一个对象。对应于java.util.Properties。
a) props.get("START.HMS"); 注:START.HMS为属性名,在文件jmeter.properties中定义
b) props.put("PROP1","1234");
prev - (SampleResult):获取前面的sample返回的信息,常用方法:
a) getResponseDataAsString():获取响应信息
b) getResponseCode() :获取响应code
更多方法可参考:org.apache.jmeter.samplers.SampleResult
sampler - (Sampler):gives access to the current sampler
参考:
https://www.blazemeter.com/blog/queen-jmeters-built-componentshow-use-beanshell
https://www.cnblogs.com/clairejing/p/7890144.html
https://blog.csdn.net/doctor_who2004/article/details/53108623