JSEncrypt与JMeter JSR223
最近在使用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
- 在官网下载
解压后,在bin
目录中可以看到:jsencrypt.js
与jsencrypt.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
文件,将其中的navigator和window等变量注释掉。
正则表达式提取器提取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;
}