Decrypt result of Java Cipher.getInstance(“AES/CBC/NoPadding”) failed with OpenResty's resty.aes module
我正在开发nginx(openresty)Lua模块,其中一项要求是解密由旧版Java程序生成的加密字符串。但是我的Lua代码无法解密它,因此我在这里寻求帮助。
可以的Java加密和解密代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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代码的输出:
1 2 | 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部分中声明:
1 2 3 | location /aescbc { content_by_lua_file conf/aescbc.lua; } |
conf / aescbc.lua的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | -- 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 |
使用" curl http://127.0.0.1:8080/aescbc"进行测试时,nginx错误日志:
1 | 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一定有问题,但是如何解决?
在网上搜索,阅读手册页并进行了几天的实验后,问题得以解决。所以我自己回答:
resty.aes模块使用OpenSSL的默认PKCS7填充算法,并且在当前版本的resty.aes中没有设置NoPadding选项的方法。所以我对此做了一个补丁:(添加填充参数并在aes:new中调用EVP_CIPHER_CTX_set_padding())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ 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测试代码做了一些小的修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | $ 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错误日志:
1 2 | 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" |