在使用LoadRunner时,当你录制完脚本后可能会发现在交互的数据中会存在密文,或者当拿到接口文档时就已经明确的描述出了交互数据的加解密方法,你该怎么办?
事实上这样的遭遇如今已经成为了一种常态,发送数据或接收到的数据中很难避免不出现密文,有些加密算法是自定义的方法,有些则是标准的对称或非对称加密算法。
很多时候对于像JMeter这样原生的Java程序测试工具来说,在高级语言特性、丰富的加解密算法库条件下,你可能一个简单的BeanShell处理器或自定义函数就可以轻松解决这些问题,但对于LoadRunner这种原生是C语言脚本的测试工具来说,为了能够发挥其自身在性能上巨大的优势,可能处理起来就没有那么轻松。
首先你要清楚选择LoadRunner作为性能测试工具的主要目的是什么?在JMeter、nGrinder等开源工具不断发展壮大,并且各自特点鲜明,不断蚕食着LoadRunner的优势地位,大有后来居上的情况下,你仍然甘愿花费重金或冒着侵权风险的条件下使用LoadRunner时,你必须清楚LoadRunner能够为你带来什么。
LoadRunner能成为行业标杆的前提是其优越的自身性能和丰富的协议支持。因此,你在使用它时也有必要将这两方面发挥到极致。一些建议是尽量避免使用LoadRunner的Java或C#.Net脚本,这样做会严重影响工具自身的性能,导致LoadRunner失去其巨大的性能优势,这也意味着在很大程度上就失去了使用它的价值。
下面我们按解决问题方案的优先级进行罗列:
方案一是使用C语言实现加解密过程,唯一麻烦的是LR在内存控制方面可能有一些小问题需要自己调试一下;
方案二是用Java这样可以支持long类型的语言编写一个外部的接口实现计算,可以在需要计算时使用一个HTTP请求这个接口;
方案三是使用参数,本地代码计算与用户对应形成加密数据的参数列表;
方案四才是使用Java脚本;
当然最后方案五是找开发改逻辑。
曾经在群里就遇到了这样一个发送端数据加密传输的需求:
9位数字,比如987654321,前缀123,后缀456,组合成一个13位的数字123987654321456,之后乘以数字30,再进行base64编码计算。
这个需求对于Java语言来说简直小儿科,只需要引入org.apache.commons.codec参考以下代码:
long number = 987654321L;
number = Long.valueOf("123" + String.valueOf(number) + "456") * 30;
String token = new String(Base64.encodeBase64(String.valueOf(number).getBytes()));
System.out.println(token);
但对于LoadRunner下的C语言脚本,就需要稍微转动一下脑筋来实现这样的需求:
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Action()
{
char *a = "123987654321456";
char base64[4096];
char bigint[16];
bigint_muti(a, 30, bigint);
base64_encode(bigint, base64, sizeof(char) * 16);
lr_output_message("%s", base64);
return 0;
}
char *
get_bigint(const int high_bit, const int low_bit){
//内存区域分配大于8,否则high和low的内存会连在一起导致无法补0
char high[16];
char low[16];
char bigint[16];
int i = 8;
sprintf(high, "%d\0", high_bit);
sprintf(low, "%d\0", low_bit);
memset(bigint, '\0', sizeof(char) * 16);
//低位补0
if(strlen(low) < 8){
strcat(bigint, high);
for(i; i > strlen(low); i--){
strcat(bigint, "0");
}
strcat(bigint, low);
} else {
strcat(bigint, high);
strcat(bigint, low);
}
//尾部置0
if(high_bit < 10000000){
bigint[15] = 0;
} else {
bigint[16] = 0;
}
return bigint;
}
char *
bigint_add(char *a, char *b){
int a_length = strlen(a);
int b_length = strlen(b);
int a_high_bit, b_high_bit, a_low_bit, b_low_bit, low_total, high_total;
int a_high_length = a_length - 8;
int b_high_length = b_length - 8;
char a_high[8];
char b_high[8];
char a_low[8];
char b_low[8];
char bigint[16];
memset(a_high, '\0', sizeof(char) * 8);
memset(b_high, '\0', sizeof(char) * 8);
memset(a_low, '\0', sizeof(char) * 8);
memset(b_low, '\0', sizeof(char) * 8);
memset(bigint, '\0', sizeof(char) * 16);
strncpy(a_high, a, a_high_length);
strncpy(b_high, b, b_high_length);
a_high_bit = atoi(a_high);
b_high_bit = atoi(b_high);
a_low_bit = atoi(a + a_high_length);
b_low_bit = atoi(b + b_high_length);
low_total = a_low_bit + b_low_bit;
if(low_total > 99999999){
low_total = low_total % 100000000;
high_total = a_high_bit + b_high_bit + 1;
} else {
high_total = a_high_bit + b_high_bit;
}
return get_bigint(high_total, low_total);
}
void
bigint_muti(char *a, int b, char *bigint){
int i = 0;
strcpy(bigint, a);
for(i; i < (b - 1); i++){
strcpy(bigint, bigint_add(a, bigint));
}
return;
}
char *
base64_encode(const unsigned char * bindata, char *base64, int binlength){
int i, j;
unsigned char current;
lr_output_message("%s", bindata);
for ( i = 0, j = 0 ; i < binlength ; i += 3 )
{
current = (bindata[i] >> 2) ;
current &= (unsigned char)0x3F;
base64[j++] = base64char[(int)current];
current = ( (unsigned char)(bindata[i] << 4 ) ) & ( (unsigned char)0x30 ) ;
if ( i + 1 >= binlength )
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
base64[j++] = '=';
break;
}
current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );
base64[j++] = base64char[(int)current];
current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;
if ( i + 2 >= binlength )
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
break;
}
current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );
base64[j++] = base64char[(int)current];
current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;
base64[j++] = base64char[(int)current];
}
base64[j] = '\0';
return base64;
}
需要自己编写一个简单的大数加法(而且可以不普适实现),base64的C算法网上到处是,唯一麻烦的是LR在内存控制方面可能有一些小问题需要自己调试一下;
构建一个平行的Mock服务,几乎没有太多编码基础的测试工程师也可以通过NodeJS构建出一个加密计算的HTTP服务,参考如下代码:
const express = require('express');
const app = express();
app.get('/', function (req, res) {
if(req.query.number){
var number = req.query.number;
number = parseInt('123' + number + '456') * 30;
var token = (new Buffer(number.toString())).toString('base64');
res.send(JSON.stringify({
"token" : token
}));
} else {
res.send(JSON.stringify({
"status" : 500
}));
}
})
app.listen(80);