openresty 的 aes 算法, Java 实现对不上

124 天前
 dunhanson

为啥对不上?好奇怪

1 、openresty github 官方示例

https://github.com/openresty/lua-resty-string

local aes = require "resty.aes"
local str = require "resty.string"
local aes_128_cbc_md5 = aes:new("AKeyForAES")
-- the default cipher is AES 128 CBC with 1 round of MD5
-- for the key and a nil salt
local encrypted = aes_128_cbc_md5:encrypt("Secret message!")
ngx.say("AES 128 CBC (MD5) Encrypted HEX: ", str.to_hex(encrypted))
ngx.say("AES 128 CBC (MD5) Decrypted: ", aes_128_cbc_md5:decrypt(encrypted))

2 、自己编写 lua 测试

local aes = require "resty.aes"
local str = require "resty.string"
local aes_128_cbc_md5 = aes:new("gOxiO7IIRthJ406X")
-- the default cipher is AES 128 CBC with 1 round of MD5
-- for the key and a nil salt
local encrypted = aes_128_cbc_md5:encrypt("hello")
ngx.say("AES 128 CBC (MD5) Encrypted HEX: ", str.to_hex(encrypted))
ngx.say("AES 128 CBC (MD5) Decrypted: ", aes_128_cbc_md5:decrypt(encrypted))

-- 输出结果
AES 128 CBC (MD5) Encrypted HEX: de6641e8a49ef1911a10d9ec88cc477b
AES 128 CBC (MD5) Decrypted: hello

3 、java 实现

@Test
public void testEncryptText() {
    String key = "gOxiO7IIRthJ406X";
    byte[] iv = new byte[16];
    String text = "hello";
    AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, SecureUtil.md5(key).getBytes(), iv);
    System.out.println(aes.encryptHex(text));
    // 输出:446d1192d40aa0d05e3c30392ac43ec3
    aes = new AES(Mode.CBC, Padding.PKCS5Padding, key.getBytes(), iv);
    System.out.println(aes.encryptHex(text));
    // 输出:190ede3e1359a6e4d8ecf38c8f4bce63
}
1538 次点击
所在节点    问与答
11 条回复
kran
124 天前
虽然忘了具体参数,但可以匹配上。曾经做过。
kran
124 天前
```
@Provides @Singleton
private Cipher provideAESEncryptCipher(GlobalConfig config) {
var key = Base64.decode(config.getAesKey());
var cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", new BouncyCastleProvider());
var iv = config.getAesIV().getBytes(StandardCharsets.US_ASCII);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES/CBC/PKCS7Padding"), new IvParameterSpec(iv));


return cipher;
}
}

local c = aes:new(key, nil, aes.cipher(128,"cbc"), {iv="0123456789abcdef"})

local path = ngx.re.gsub(string.sub(ngx.var.uri, 2), '-', '+')
local url, err = c:decrypt(ngx.decode_base64(path))

```
dunhanson
124 天前
@kran 我用的 aes_128_cbc_md5 ,这个 iv 不知道是多少
kran
124 天前
iv 自己定的
kran
124 天前
随机 iv 我记得两边匹配不上
dunhanson
124 天前
@kran 确实,随机的匹配不上,固定的匹配上了。
但是好奇怪,随机,为啥 openresty 自己可以解密?
forvvvv123
124 天前
OP 意思是同样的 key ,同样的明文,2 和 3 结果不一样吗?

如果是这个意思的话,那是因为 cbc 模式要求有 iv ,正常的做法,每一次加密 iv 都是一个不一样的值,同样 key 同样明文,iv 不同获得的密文也不同; 接收方要解密的时候必须知道 key + iv 才能解密,一般是约定附加在密文开头或者指定另一个字段;

你这两个都没有明确设置 iv ,那得看 lua 的默认行为和 java 默认行为最终导致的进入运算的 iv 一不一样; op 可以手动设置 iv ,或者查查手册把 iv 打印出来
Citrus
124 天前
刚好研究过。OpenResty 的 AES 库实际是调用 OpenSSL ,如果你不显示指定 IV ,会用 key 生成一个 IV 。具体看这里: https://docs.openssl.org/3.1/man3/EVP_BytesToKey/
所以这个库调用的时候看起来没有传 IV ,但是却可以正常加解密,且用全 0 IV 无法解密。

你的 Java 代码,IV 没有初始化,也就是全 0 ,跟 OpenSSL 生成的肯定是对不上的。所以解密失败了。

如果你想用 Java 解,有两种方式。一是实现 EVP BytesToKey 函数,算一个正确的 IV 出来。二是在 Lua 里,自己传 IV 进去,不要用自动生成的。
Citrus
124 天前
@forvvvv123 不一样。OP 在 Lua 没指定,自动生成了。但是在 Java 指定了,指定了全 0 。
Citrus
124 天前
@dunhanson 自己可以解是因为 OpenResty 用了 OpenSSL 的 https://docs.openssl.org/3.1/man3/EVP_BytesToKey/ 函数根据 Password 算出了 IV ,所以同一个 Password 算出的 Key 和 IV 是一样的,所以才能解密。
forvvvv123
124 天前
楼上老哥解释的很详细; 原来 lua 是默认调用了一个 openssl 的自动生成 iv 的函数,不是用全 0 的 iv ;

其实一般做法 iv 都是自己写段逻辑自己生成的,用随机数也好,不重复生成的算法也好。 因为传输密文的时候这个 iv 怎么传得跟对方约定好的,是单独一个业务字段,还是放到开头结尾; 所以 iv 这块总是要自己处理的;

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1060706

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX