在JMeter的JSR223预处理器中使用CryptoJS 4.0.0时出现原生加密模块错误的求助
我在JMeter的JSR223预处理器(基于ECMAScript 262 Edition 5.1引擎)里尝试使用CryptoJS 4.0.0做AES加密,代码如下:
load('C:/Users/hlopezgu/Downloads/crypto-js-4.0.0/crypto-js.js'); var key = 'secret key 123'; var textToEncrypt = '123456'; var encryptedText = CryptoJS.AES.encrypt(textToEncrypt, key).toString(); log.info("Texto cifrado: " + encryptedText);
运行后却遇到了错误:
Error: Native crypto module could not be used to get secure random number
我的需求是用这个库加密JSON中的数据,然后通过POST请求发送给Web服务,请问该怎么解决这个问题?
解决方案
这个错误的根源是CryptoJS 4.x版本依赖了Node.js的原生crypto模块生成安全随机数,但JMeter使用的Nashorn JavaScript引擎并没有这个模块,我们可以通过以下几种方式解决:
方案1:降级到CryptoJS 3.x版本
CryptoJS 3.x系列(比如3.3.0)不依赖原生crypto模块,完全用JavaScript实现了随机数生成逻辑。你不需要修改现有代码,只需要把加载的crypto-js.js替换成3.x版本的文件,就能直接运行原有代码,这是最简单的解决办法。
方案2:为CryptoJS添加自定义随机数生成器
如果必须使用4.0.0版本,可以手动为CryptoJS提供基于JavaSecureRandom的随机数生成器,替换它默认的依赖逻辑,修改后的代码如下:
load('C:/Users/hlopezgu/Downloads/crypto-js-4.0.0/crypto-js.js'); // 用Java的SecureRandom实现CryptoJS需要的随机数生成方法 CryptoJS.lib.WordArray.random = function(nBytes) { var secureRandom = new java.security.SecureRandom(); var bytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, nBytes); secureRandom.nextBytes(bytes); var wordArray = CryptoJS.lib.WordArray.create(); wordArray.sigBytes = nBytes; var words = wordArray.words; for (var i = 0; i < nBytes; i++) { words[i >>> 2] |= (bytes[i] & 0xff) << (24 - (i % 4) * 8); } return wordArray; }; var key = 'secret key 123'; var textToEncrypt = '123456'; var encryptedText = CryptoJS.AES.encrypt(textToEncrypt, key).toString(); log.info("Texto cifrado: " + encryptedText);
这段代码通过Java的安全随机数生成器满足CryptoJS的需求,从而绕过对原生crypto模块的依赖。
方案3:直接使用Java加密API(推荐)
既然JMeter本身是Java应用,直接使用Java原生的加密API会更高效,也不需要引入外部JS库,而且生成的加密结果和CryptoJS默认格式完全兼容,示例代码如下:
importPackage(javax.crypto); importPackage(javax.crypto.spec); importPackage(java.nio.charset); var key = 'secret key 123'; var textToEncrypt = '123456'; // 将原始密钥处理成AES要求的32字节密钥(通过SHA-256哈希) var keyBytes = key.getBytes(StandardCharsets.UTF_8); var md = java.security.MessageDigest.getInstance("SHA-256"); md.update(keyBytes); var secretKey = new SecretKeySpec(md.digest(), "AES"); // 初始化AES加密器(CBC模式+PKCS5填充,自动生成随机IV) var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); // 执行加密 var inputBytes = textToEncrypt.getBytes(StandardCharsets.UTF_8); var encryptedBytes = cipher.doFinal(inputBytes); // 组合IV和加密数据(和CryptoJS的输出格式一致) var iv = cipher.getIV(); var combined = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, iv.length + encryptedBytes.length); java.lang.System.arraycopy(iv, 0, combined, 0, iv.length); java.lang.System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length); // 转成Base64字符串输出 var encryptedText = new sun.misc.BASE64Encoder().encode(combined); log.info("Texto cifrado: " + encryptedText);
这个方案不需要依赖任何外部JS库,稳定性和性能都更适合JMeter的运行环境。
备注:内容来源于stack exchange,提问作者Hans López

