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