V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
dunhanson
V2EX  ›  问与答

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

  •  
  •   dunhanson · 121 天前 · 1531 次点击
    这是一个创建于 121 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为啥对不上?好奇怪

    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
    }
    
    11 条回复    2024-07-29 00:45:13 +08:00
    kran
        1
    kran  
       121 天前 via Android
    虽然忘了具体参数,但可以匹配上。曾经做过。
    kran
        2
    kran  
       121 天前 via Android
    ```
    @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
        3
    dunhanson  
    OP
       121 天前
    @kran 我用的 aes_128_cbc_md5 ,这个 iv 不知道是多少
    kran
        4
    kran  
       121 天前 via Android
    iv 自己定的
    kran
        5
    kran  
       121 天前 via Android
    随机 iv 我记得两边匹配不上
    dunhanson
        6
    dunhanson  
    OP
       121 天前
    @kran 确实,随机的匹配不上,固定的匹配上了。
    但是好奇怪,随机,为啥 openresty 自己可以解密?
    forvvvv123
        7
    forvvvv123  
       121 天前   ❤️ 2
    OP 意思是同样的 key ,同样的明文,2 和 3 结果不一样吗?

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

    你这两个都没有明确设置 iv ,那得看 lua 的默认行为和 java 默认行为最终导致的进入运算的 iv 一不一样; op 可以手动设置 iv ,或者查查手册把 iv 打印出来
    Citrus
        8
    Citrus  
       121 天前 via iPhone   ❤️ 4
    刚好研究过。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
        9
    Citrus  
       121 天前 via iPhone
    @forvvvv123 不一样。OP 在 Lua 没指定,自动生成了。但是在 Java 指定了,指定了全 0 。
    Citrus
        10
    Citrus  
       121 天前 via iPhone
    @dunhanson 自己可以解是因为 OpenResty 用了 OpenSSL 的 https://docs.openssl.org/3.1/man3/EVP_BytesToKey/ 函数根据 Password 算出了 IV ,所以同一个 Password 算出的 Key 和 IV 是一样的,所以才能解密。
    forvvvv123
        11
    forvvvv123  
       121 天前
    楼上老哥解释的很详细; 原来 lua 是默认调用了一个 openssl 的自动生成 iv 的函数,不是用全 0 的 iv ;

    其实一般做法 iv 都是自己写段逻辑自己生成的,用随机数也好,不重复生成的算法也好。 因为传输密文的时候这个 iv 怎么传得跟对方约定好的,是单独一个业务字段,还是放到开头结尾; 所以 iv 这块总是要自己处理的;
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5497 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 07:17 · PVG 15:17 · LAX 23:17 · JFK 02:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.