最近在使用JMeter批量造数据时,登录过程中的密码是加密的,因此需要在登录时调用加密的方法对密码加密……

一、OpenSSL

1、简介

OpenSSL是一个功能强大的商用级全功能工具包,适用于传输层安全性(TLS)和安全套接字层(SSL)协议。

2、下载与安装

Windows下载地址:Win32/Win64 OpenSSL Installer

Linux下载地址:Downloads

3、生成私钥与公钥

以Linux为例:
openssl
  • 生成私钥:
genrsa -out rsa_1024_priv.pem 1024
  • 生成公钥
rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem

运行命令后会在当前文件夹下生成公/私钥文件:

  • 查看私/公钥
cat rsa_1024_priv.pem
cat rsa_1024_pub.pem

文件内容如下:

二、JSEncrypt

1、简介

JSEncrypt是一个用于执行OpenSSL RSA加密、解密和密钥生成的Javascript库。

2、下载与安装

  • 使用npm安装
npm i jsencrypt
  • 在官网下载

jsencrypt-master

解压后,在bin目录中可以看到:jsencrypt.jsjsencrypt.min.js

3、使用

//私钥
var privkey = 'MIICXQIBAAKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQABAoGAfY9LpnuWK5Bs50UVep5c93SJdUi82u7yMx4iHFMc/Z2hfenfYEzu+57fI4fvxTQ//5DbzRR/XKb8ulNv6+CHyPF31xk7YOBfkGI8qjLoq06V+FyBfDSwL8KbLyeHm7KUZnLNQbk8yGLzB3iYKkRHlmUanQGaNMIJziWOkN+N9dECQQD0ONYRNZeuM8zd8XJTSdcIX4a3gy3GGCJxOzv16XHxD03GW6UNLmfPwenKu+cdrQeaqEixrCejXdAFz/7+BSMpAkEA8EaSOeP5Xr3ZrbiKzi6TGMwHMvC7HdJxaBJbVRfApFrE0/mPwmP5rN7QwjrMY+0+AbXcm8mRQyQ1+IGEembsdwJBAN6az8Rv7QnD/YBvi52POIlRSSIMV7SwWvSK4WSMnGb1ZBbhgdg57DXaspcwHsFV7hByQ5BvMtIduHcT14ECfcECQATeaTgjFnqE/lQ22Rk0eGaYO80cc643BXVGafNfd9fcvwBMnk0iGX0XRsOozVt5AzilpsLBYuApa66NcVHJpCECQQDTjI2AQhFc1yRnCU/YgDnSpJVm1nASoRUnU8Jfm3Ozuku7JUXcVpt08DFSceCEX9unCuMcT72rAQlLpdZir876';

//公钥
var pubkey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB';

//传输的内容
var text = 'Hello World!';

//使用公钥加密
var encrypt = new JSEncrypt();
encrypt.setPublicKey(pubkey);
var encrypted = encrypt.encrypt(text);

//使用私钥解密
var decrypt = new JSEncrypt();		
decrypt.setPrivateKey(privkey);
var uncrypted = decrypt.decrypt(encrypted);

//检查
if (uncrypted == text) {
	console.log('It works!!!');
}
else {
	console.log('Something went wrong....');
}

三、JMeter JSR223

1、简介

JSR223是一种Java标准,它为JVM上的非Java语言(主要是脚本语言)提供了一组标准接口,目前几乎所有JVM脚本语言都会提供一个JSR223的实现。

2、使用JSR223 PreProcessor

  • 选中一个HTTP请求,添加JSR223前处理器

在发送这个HTTP请求之前会先执行此处理器中配置的脚本

  • 编写Script脚本

使用load()方法加载Javascript脚本,文件路径为相对JMeter安装路径的bin目录下。

加载jsencrypt.js会报错:

javax.script.ScriptException: ReferenceError: "navigator" is not defined in javascripts/jsencrypt.js at line number 2624

由于这并不是在浏览器环境中运行,因此需要修改jsencrypt.js文件,将其中的navigatorwindow等变量注释掉。

正则表达式提取器提取Get PublicKey请求中返回的公钥:

在编写脚本使用变量获取正则表达式提取的结果:

//加载Js
load("javascripts/jsencrypt.js");

var password = "123456";
//${publicKey}为正则表达式提取器提取到的变量
var publicKey = "${publicKey}";
var encrypt = new JSEncrypt.JSEncrypt();
encrypt.setPublicKey(publicKey);
//使用公钥加密
var encrypted = encrypt.encrypt(password);
//将加密结果存到变量中,供Login请求中使用
vars.put("password", encrypted);

//打印日志
log.info("encrypted password: " + encrypted);

在Login的请求中使用在脚本加密后的password:

3、其他说明

在编写样例的过程中遇到以下问题:

  • JSEncrypt.encrypt()加密后返回false

脚本如下所示:

var password = "123456";
var publicKey = "${publicKey}";
log.info("key: " + publicKey);
var encrypt = new JSEncrypt.JSEncrypt();
encrypt.setPublicKey(publicKey);
var encrypted = encrypt.encrypt(password);
log.info("encrypted password: " + encrypted);

对比了一下打印出来的publicKey和后台传的值确实是一样的,但encrypted总是返回false。上网查了下原因,据说是Base64编码问题,因此我将后台Servlet返回到前端的publicKey做了如下处理:

ret.put("publicKey", PUBLICKEY);

改为:

ret.put("publicKey", RSAUtil.getPublicKey(PUBLICKEY).getEncoded());

RSAUtil中的方法:

public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
	//通过X509编码的Key指令获得公钥对象
	KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
	X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
	RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
	return key;
}
  • algid parse error, not a sequence

在后台做校验的Servlet中报错:

Caused by: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
	at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
	at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
	at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(Unknown Source)
	at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(Unknown Source)
	at sun.security.rsa.RSAKeyFactory.generatePrivate(Unknown Source)
	... 18 more

上网查了下原因,据说是用openssl生成私钥的时候没有进行pkcs8编码导致的;将私钥保存到rsa_private_key.pem文件中,执行以下命令:

openssl
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt

使用转换后的私钥替换原来的私钥即可校验通过。

校验方法逻辑:

final String PRIVATEKEY = "..转换后的私钥..";
String decryptData = RSAUtil.privateDecrypt(password, RSAUtil.getPrivateKey(PRIVATEKEY));
if("123456".equals(decryptData)){
	out.print("Welcome!");
}else{
	out.print("Authentication failed!");
}

RSAUtil中的方法:

public static String privateDecrypt(String data, RSAPrivateKey privateKey){
	try{
		Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
	}catch(Exception e){
		throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
	}
}

public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
	//通过PKCS#8编码的Key指令获得私钥对象
	KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
	PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
	RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
	return key;
}
JMeter中使用Javascript函数:${__javaScript(Math.random().toFixed(10))}
参考资料: