openresty / lua-resty-string

String utilities and common hash functions for ngx_lua and LuaJIT
429 stars 143 forks source link

lua aes ECB 128 decrypt error #28

Open lilien1010 opened 9 years ago

lilien1010 commented 9 years ago

I tryed encrypt a text with a key with PHP code,then it is OK when decrypt it with Java and Object-C ,

 mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $bindkey, $text, MCRYPT_MODE_ECB )

I dont know why do you need a sha1 when I just want want decrypt a ECB mode string

local aes_default = aes:new(key,nil, aes.cipher(128,"ecb"),aes.hash.sha1)

pbby commented 8 years ago

I have the same question. @lilien1010 Did you have the solution already?

agentzh commented 8 years ago

Maybe due to a different padding used by PHP?

pbby commented 8 years ago

@agentzh Thanks for answering. I do not know much about the encryption algorithm. but i tested on many sites with aes tool, like:

They have the same encrypted string, when the cipher is aes, the key is secret, the data is hello, and the mode is ecb, just the issue owner says. I got hex: ebb28eae23f5d293629d02af7e3f4756 base64: 67KOriP10pNinQKvfj9HVg==

then, I tested with the aes of lua-resty-string lib, there is my code

local aes = require "resty.aes"
local str = require "resty.string"

local data = "hello"
local aes_128_ecb, err = aes:new("secret", nil, aes.cipher(128,"ecb"), nil)
if err  then
    ngx.say(err)
    ngx.exit(200)
end
local encrypted = aes_128_ecb:encrypt(data)
ngx.say("AES 128 EBC Encrypted HEX: ", str.to_hex(encrypted))
ngx.say("AES 128 EBC Decrypted: ", aes_128_ecb:decrypt(encrypted))

the result is:

AES 128 EBC Encrypted HEX: daae45d76f329165629d839f0c7f009f
AES 128 EBC Decrypted: hello

The result is not matched, and I do it on a wrong way?

agentzh commented 8 years ago

@surfire91 Maybe this is helpful: https://github.com/openresty/lua-resty-string/issues/10#issuecomment-11960797

puImp7Im commented 8 years ago

(I have removed my code. It may misleading you.)

pbby commented 8 years ago

@WiLdWiNd-WH I got a error bad key length as your way.

Maybe ecb mode don't need a iv ?

pbby commented 8 years ago

@agentzh I think #10 is a question, not a solution......

bungle commented 8 years ago

I'm pretty sure, as @agentzh said, this is issue with different padding. Some info:

  1. AES-128 key/secret size is fixed 128 bits or 16 bytes, now your key secret is only 6 bytes, so it needs to be padded (or possibly hashed with hash function that provides 16 bytes of data). Simplest way to pad is just zero-pad, aka add needed number of \0 to end of that key.
  2. AES is a block cipher and the size of the block is fixed 16 bytes. Now your data hello is only 5 bytes, so it needs to be padded to 16 bytes, now again, simplest way to pad is to zero pad.

Let me illustrate this using my lua-resty-nettle:

lua-resty-nettle doesn't pad any keys or data automatically (at least by now, but I may implement padding mechanisms later). Now to get the results you want, I need to pad myself:

local function hex(str,spacer)
    return (string.gsub(str,"(.)", function (c)
        return string.format("%02X%s",string.byte(c), spacer or "")
    end))
end

local aes = require "resty.nettle.aes"
local aes128 = aes.new "secret\0\0\0\0\0\0\0\0\0\0"
local ciphertext = aes128:encrypt "hello\0\0\0\0\0\0\0\0\0\0\0"
print("aes128 ecb encrypt", #ciphertext, hex(ciphertext))
local aes128 = aes.new "secret\0\0\0\0\0\0\0\0\0\0"
local plaintext = aes128:decrypt(ciphertext)
print("aes128 ecb decrypt", #plaintext, plaintext)

And when I run this I get this output (just like in PHP):

aes128 ecb encrypt  16  EBB28EAE23F5D293629D02AF7E3F4756
aes128 ecb decrypt  5   hello

I'm not sure about how lua-resty-string does the padding, so I cannot say for sure. But maybe this helps you to understand. There are more moving parts in the method signature in lua-resty-string so I cannot say for sure, what happens inside. The hash-parameter may use some default, I don't know (maybe it auto hashes the secret).

bungle commented 8 years ago

And the IV thing, AES in ECB mode doesn't support it as far as I know because it doesn't perform chaining between blocks. It is the worst mode to use if the data you are trying to encrypt is more that 16 bytes (also the most simple and stupid).

pbby commented 8 years ago

@bungle Thanks I got that, php do auto padding on background.

Wang commented 8 years ago

For PHP mycrypt the padding is \0 and openssl use PKCS#5 padding, so if you use openssl api's to decrypt php's encrypt result you can disabled default padding.

  1. openssl
  2. PKCS#5 padding

Or using my aes-php

qqlee commented 7 years ago

@Wang hai shi ni niubi!!!

haorenfsa commented 7 years ago

Hi, @agentzh
I run into this issue today, thought that this API suit should use zeropadding defaultlly. I suggest add an flag parameter in new() function to set padding rules ,which is by default PKCS#5 to compatite with current version; and can select other ways to padding, like zero paddings. this could make it much user-friendly I paste my simple code example below, and if u agree with it, I'll make a pull request for it

-- key too long
if padding_mode == ZERO_PADDING then
if #key > _cipherLength then
    return nil
else
    -- padding key with 0x00, work for all of 128,192,256 bits
    key = key.."\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
end
ffi_copy(gen_key, key, _cipherLength)

`

agentzh commented 7 years ago

@haorenfsa What do you think of this pending pull request?

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

haorenfsa commented 7 years ago

@agentzh

35 is about data padding. and I think there's some problem with the key.

I did some test, found this module even not compatite with openssl enc -aes-256-xxx cmds, and then I found that my issue (also most people's I guess) lies on that the parameter key we input is usually the real key we want to use, while aes.lua take our input key and use md5 to regenarate another string, and use it as the key, which is some random md5 value. and that causes this module not compatite with all others like php openssl. for example, a common php openssl aes api usage is like:

openssl_encrypt('data here', 'AES-256-ECB', 'some_key');

and the actual key used to encrypt is "some_key\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

while in ngx.resty.aes if u input 'some_key' as key, then after md5 the real key is actually "3d70412c7e9ea2d96fa23d4f1f1f0a1c"

And thus bad time comes........

agentzh commented 7 years ago

@haorenfsa That sounds like a bug to me. We should not do MD5 on the key unless explicitly dictated.

haorenfsa commented 7 years ago

@agentzh yes, exactlly