使用 OpenResty 的 resty.aes 模块解密 Java Cipher.getInstance("AES/CBC/NoPadding") 结果失败

Posted

技术标签:

【中文标题】使用 OpenResty 的 resty.aes 模块解密 Java Cipher.getInstance("AES/CBC/NoPadding") 结果失败【英文标题】:Decrypt result of Java Cipher.getInstance("AES/CBC/NoPadding") failed with OpenResty's resty.aes module 【发布时间】:2018-01-10 11:58:25 【问题描述】:

我正在开发一个 nginx(openresty) Lua 模块,其中一个要求是破译由遗留 Java 程序生成的加密字符串。但是我的 Lua 代码无法破译它,我在这里寻求帮助。

Java加解密代码就ok了:

public class AesCbc 
    private static String PLAIN = "usr/passwd@bizdb:127.0.0.1:5432";

    public static void main(String[] args) throws Exception 
        Cipher aesCipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec("1234567890ABCDEF".getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec("fedcba0987654321".getBytes());

        aesCipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        byte[] rawBytes = PLAIN.getBytes();
        byte[] aligned;
        int mod = rawBytes.length % 16; // prevent javax.crypto.IllegalBlockSizeException
        if (mod == 0) 
            aligned = new byte[rawBytes.length];
         else 
            aligned = new byte[rawBytes.length + 16 - mod];
        
        System.arraycopy(rawBytes, 0, aligned, 0, rawBytes.length);
        byte[] cipherBytes = aesCipher.doFinal(aligned);
        String base64Result = Base64.getEncoder().encodeToString(cipherBytes);
        System.out.println("cipher:[" + base64Result + "], rawBytes.length=" + rawBytes.length + ", mod=" + mod);

        aesCipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        cipherBytes = Base64.getDecoder().decode(base64Result);
        aligned = aesCipher.doFinal(cipherBytes);
        int posNil;
        for (posNil = 0; posNil < aligned.length; posNil++) 
            if (aligned[posNil] == 0x00)
                break;
        
        rawBytes = new byte[posNil];
        System.arraycopy(aligned, 0, rawBytes, 0, posNil);
        String plain = new String(rawBytes);
        System.out.println("plain:[" + plain + "], posNil=" + posNil + ", aligned.length=" + aligned.length);
    

Java 代码的输出:

cipher:[l1buytGEL4RKa/RezInQ3dJxvMtL6nyE2wTi7VyoS4w=], rawBytes.length=31, mod=15
plain:[usr/passwd@bizdb:127.0.0.1:5432], posNil=31, aligned.length=32

我的 Lua 测试文件在 nginx.conf->http->server 部分声明:

            location /aescbc 
                    content_by_lua_file conf/aescbc.lua;
            

conf/aescbc.lua 的内容:

-- aescbc.lua

local aes = require "resty.aes"
local str = require "resty.string"
local aes128Cbc = aes:new("1234567890ABCDEF", nil, aes.cipher(128, "cbc"), iv="fedcba0987654321")

-- result of AesCbc.java for my test
local BASE64CIPHER = "l1buytGEL4RKa/RezInQ3dJxvMtL6nyE2wTi7VyoS4w="

local cipherBytes = ngx.decode_base64(BASE64CIPHER)
if not cipherBytes then
    ngx.log(ngx.WARN, "decode base64 [" .. BASE64CIPHER .. "] failed")
    return
end

local aligned = aes128Cbc:decrypt(cipherBytes)
if not aligned then
    ngx.log(ngx.WARN, "decrypt cipherBytes [" .. str.to_hex(cipherBytes) .. "] failed")
    return
end

ngx.log(ngx.NOTICE, "aligned [" .. str.to_hex(aligned) .. "]")
return

使用“curlhttp://127.0.0.1:8080/aescbc”进行测试时,nginx 错误日志:

2017/08/03 11:28:26 [warn] 13799#0: *5 [lua] aescbc.lua:18: decrypt cipherBytes [9756eecad1842f844a6bf45ecc89d0ddd271bccb4bea7c84db04e2ed5ca84b8c] failed, client: 127.0.0.1, server: , request: "GET /aescbc HTTP/1.1", host: "127.0.0.1:8080"

我认为我对resty.aes的使用一定有问题,但是如何解决呢?

【问题讨论】:

【参考方案1】:

在网上搜索,阅读手册页并尝试了几天后,问题解决了。所以我自己回答:

resty.aes 模块使用OpenSSL 默认的PKCS7 填充算法,当前版本的resty.aes 没有设置NoPadding 选项的方法。所以我给它做了一个补丁:(添加一个 padding 参数并在 aes:new 中调用 EVP_CIPHER_CTX_set_padding()

$ diff ./lualib/resty/aes.lua.orig ./lualib/resty/aes.lua
79a80
> int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *x, int padding);
128c129
< function _M.new(self, key, salt, _cipher, _hash, hash_rounds)
---
> function _M.new(self, key, salt, _cipher, _hash, hash_rounds, padding)
177a179,184
>     end
> 
>     if padding then
>         -- 0:NoPadding, 1:PKCS7(default), 2:ISO7816_4, 3:ANSI923, 4:ISO10126, 5:ZERO
>         C.EVP_CIPHER_CTX_set_padding(encrypt_ctx, padding);
>         C.EVP_CIPHER_CTX_set_padding(decrypt_ctx, padding);

并对 lua 测试代码做了一些小的修改:

$ cat ./nginx/conf/aescbc.lua 
-- aescbc.lua

local aes = require "resty.aes"
local str = require "resty.string"
local aes128Cbc = aes:new("1234567890ABCDEF", nil, aes.cipher(128, "cbc"), iv="fedcba0987654321", nil, 0)

-- result of AesCbc.java for my test
local BASE64CIPHER = "l1buytGEL4RKa/RezInQ3dJxvMtL6nyE2wTi7VyoS4w="

local cipherBytes = ngx.decode_base64(BASE64CIPHER)
if not cipherBytes then
    ngx.log(ngx.WARN, "decode base64 [" .. BASE64CIPHER .. "] failed")
    return
end

local aligned = aes128Cbc:decrypt(cipherBytes)
if not aligned then
    ngx.log(ngx.WARN, "decrypt cipherBytes [" .. str.to_hex(cipherBytes) .. "] failed")
    return
end

ngx.log(ngx.NOTICE, "aligned [" .. str.to_hex(aligned) .. "], len=" .. aligned:len())

local plain 
local idx = aligned:find('\0')
if idx then
    plain = aligned:sub(1, idx - 1)
else
    plain = aligned
end

ngx.log(ngx.NOTICE, "plain [" .. plain .. "], len=" .. plain:len())

return

测试的 nginx 错误日志:

2017/08/07 15:17:55 [notice] 34632#0: *21 [lua] aescbc.lua:22: aligned [7573722f7061737377644062697a64623a3132372e302e302e313a3534333200], len=32, client: 127.0.0.1, server: , request: "GET /aescbc HTTP/1.1", host: "127.0.0.1:8080"
2017/08/07 15:17:55 [notice] 34632#0: *21 [lua] aescbc.lua:32: plain [usr/passwd@bizdb:127.0.0.1:5432], len=31, client: 127.0.0.1, server: , request: "GET /aescbc HTTP/1.1", host: "127.0.0.1:8080"

【讨论】:

我遇到了类似的问题,但加密发生在 .NET 应用程序中。我想知道您是否可以共享 aes.lua 补丁的完整版本,从您的回答中我不明白“C.EVP_CIPHER_CTX_set_padding”调用在 lua 文件中的位置。问候。

以上是关于使用 OpenResty 的 resty.aes 模块解密 Java Cipher.getInstance("AES/CBC/NoPadding") 结果失败的主要内容,如果未能解决你的问题,请参考以下文章

openresty开发系列25--openresty中使用json模块

OpenResty安装与使用

尹吉峰:使用 OpenResty 搭建高性能 Web 应用

openresty 用啥ide

openresty的初步使用

Openresty使用