Crypto-JS 简介

Crypto-JS 是一个 JS 的加密库,提供了各种加密算法,目前支持的算法包括:MD5、SHA-1、SHA-256、AES、Rabbit、MARC4、HMAC、PBKDF2。具体的介绍和源码 戳这里

缘起

由于项目需要,在研究某网站的时候,发现这个网站在发送密码的时候竟然对输入的内容进行了加密,于是顺藤摸瓜就找到了这个库。先来说说某网站对数据进行的核心加密操作吧。
查看它们的 JS,可以发现这样的代码:


$.fn.valAesEncryptSet = function() {var i = this.val(),
    n,
    t;
    try {n = this.aesDecrypt(i);
        n != "" && (t = this.aesEncrypt(n), t != i && (n = ""))
    } catch(r) {n = ""}
    return n == "" && (t = this.aesEncrypt(i)),
    this.val(t),
    this.val()}

$.fn.aesEncrypt = function(n) {var t = CryptoJS.MD5("secret"),
    i = CryptoJS.enc.Utf8.parse(t),
    r = CryptoJS.enc.Utf8.parse("1234567812345678"),
    u = CryptoJS.AES.encrypt(n, i, {iv: r});
    return u + ""
};

$.fn.aesDecrypt = function(n) {var t = CryptoJS.MD5("secret"),
    i = CryptoJS.enc.Utf8.parse(t),
    r = CryptoJS.enc.Utf8.parse("1234567812345678");
    return CryptoJS.AES.decrypt(n, i, {iv: r}).toString(CryptoJS.enc.Utf8)
};

虽然 JS 代码被压缩精简了,但还是可以看得出,它在 Crypto-JS 的基础上封装了一层 Jquery 的操作,用到了 AES 数据加密,一个密钥 t 和一个向量 iv,没有指定模式和填充方式的话,默认模式 CBC、填充方式 PKCS7Padding。然后我就得想办法把上面这段代码转化成 Java 的爬虫代码,找了很久,终于转化对了。

public static String encrypt(byte[] content, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchProviderException {SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivps);
        return Base64.encodeBase64String(cipher.doFinal(content));
    }

到目前为止,一切都还很顺利,接下来就碰到难点了,这个网站真的很喜欢用 Crypto-JS 进行加密。

挫折

第一步顺利搞定,之后按照这个思路继续走了下去,看到了这段代码。

var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase"); 

省去了 iv 向量,直接就一个密钥和明文,然后我在控制台运行了几次,发现加密出来的内容还不一样,最要命的是用它自己的解密方法居然可以解出来。到 Stackoverflow 上查了下,发现这种做法是不安全的,也没人说为啥不安全。这就陷入了僵局,不知道到底默认用了什么鬼加密方式。

希望

整了一个晚上,就是不知道它这种操作用了什么方式去加密,最后没办法,去查看 Crypto-JS 的源码,发现我特么高估了对 JS 的理解,像在看天书。之后我给出了两个方案,A 方案就是继续搞下去,找出原理;B 方案就是利用 Java 的脚本引擎动态执行 Crypto-JS 的解密脚本。B 方案在效率上肯定比不上 A 方案。睡了一觉之后,继续寻找默认加密算法,终于有了眉目。Stackoverflow 上有人提出了这样的疑问:How does CryptoJS get an IV when none is specified?,原来默认加密方式是 OpenSSL 的,于是追根溯源,找 Java 版本的实现方式,最后被我找到了 这个 ,这不就是我要找的源码么。YES,根据这段代码,我顺利搞定了。

结尾

自己也爬过很多网站,形形色色的防爬虫技术也见过不少,这种博弈很有意思。这次的这个案例让我觉得思路挺新颖,虽然 Web 页对外是暴露的,但是这样一加密,对一些新手乃至于老手,在获得抓取规则上付出了时间成本,以后可以在自己的项目里面借鉴这种方式。