From 8299b9cc1b2b9e7556ef3311f3ad00959098c091 Mon Sep 17 00:00:00 2001 From: James ZHANG Date: Thu, 8 Nov 2018 21:48:55 +0800 Subject: [PATCH 1/6] use `table.concat insteed of `for` loop on the `get_sha1` --- lib/resty/crypt.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/resty/crypt.lua b/lib/resty/crypt.lua index 6b7d602..f8de0f5 100644 --- a/lib/resty/crypt.lua +++ b/lib/resty/crypt.lua @@ -56,12 +56,11 @@ function _M.new (self, token, aes_key, app_id) end function _M.get_sha1 (self, sha1_table) - table.sort(sha1_table) + local tb_sort = table.sort + tb_sort(sha1_table) - local to_sha1 = "" - for k,v in pairs(sha1_table) do - to_sha1 = to_sha1 .. v - end + local tb_join = table.concat + local to_sha1 = tb_join(sha1_table) local sha1 = resty_sha1:new() sha1:update(to_sha1) From 68be25b849af8f065898c17978415682b9eeec0b Mon Sep 17 00:00:00 2001 From: James ZHANG Date: Thu, 8 Nov 2018 22:15:18 +0800 Subject: [PATCH 2/6] patched on `resty.aes` with `set_padding` --- lib/resty/aes_pad.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/resty/aes_pad.lua diff --git a/lib/resty/aes_pad.lua b/lib/resty/aes_pad.lua new file mode 100644 index 0000000..f4751c2 --- /dev/null +++ b/lib/resty/aes_pad.lua @@ -0,0 +1,28 @@ +-- patched +-- @see https://github.com/openresty/lua-resty-string/pull/35 + +local aes = require "resty.aes" +local ffi = require "ffi" +local C = ffi.C +ffi.cdef[[ +typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; + +int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int pad); +]] + +-- @link https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c#L569-L576 +function aes.set_padding(self, pad) + if self._encrypt_ctx == nil or self._decrypt_ctx == nil then + return nil, "the aes instance doesn't existed" + end + + -- @link https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c#L402-L410 + C.EVP_CIPHER_CTX_set_padding(self._encrypt_ctx, pad) + + -- @link https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c#L515-L523 + C.EVP_CIPHER_CTX_set_padding(self._decrypt_ctx, pad) + + return 1 +end + +return aes From 3886647ce61e05426ac6d495138004c85451c5a8 Mon Sep 17 00:00:00 2001 From: James ZHANG Date: Thu, 8 Nov 2018 22:17:53 +0800 Subject: [PATCH 3/6] use `resty.aes_pad` insteed of the special one --- lib/resty/crypt.lua | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/resty/crypt.lua b/lib/resty/crypt.lua index f8de0f5..560d915 100644 --- a/lib/resty/crypt.lua +++ b/lib/resty/crypt.lua @@ -1,10 +1,11 @@ local random = require "resty.random" local str = require "resty.string" -local aes = require "resty.aes" +local aes = require "resty.aes_pad" local bit = require "bit" local resty_sha1 = require "resty.sha1" +local setmetatable = setmetatable -local _M = {} +local _M = { _VERSION = '0.2' } local mt = { __index = _M } @@ -52,7 +53,9 @@ end function _M.new (self, token, aes_key, app_id) local aes_key = ngx.decode_base64(aes_key .. "=") - return setmetatable({token = token, aes_key = aes_key, app_id = app_id}, mt) + local cipher = aes.cipher(256, "cbc") + local iv = string.sub(aes_key, 0, 16) + return setmetatable({token = token, aes_key = aes_key, app_id = app_id, cipher = cipher, iv = iv}, mt) end function _M.get_sha1 (self, sha1_table) @@ -77,8 +80,9 @@ function _M.decrypt (self, encrypted) local iv = string.sub(self.aes_key, 0, 16) local aes_crypt = assert( - aes:new(self.aes_key, nil, aes.cipher(256,"cbc"), {iv=iv}, nil, 0) + aes:new(self.aes_key, nil, self.cipher, {iv = self.iv}) ) + aes:set_padding(0) local text = aes_crypt:decrypt(ciphertext_dec) text = pkcs7_decode(string.sub(text, 17, #text)) @@ -90,15 +94,15 @@ end function _M.encrypt (self, text, timestamp, nonce) - local random = str.to_hex(random.bytes(8, true)) - local iv = string.sub(self.aes_key, 0, 16) + local prefix = str.to_hex(random.bytes(8, true)) - text = random .. pack_text_len(#text) .. text .. self.app_id + text = prefix .. pack_text_len(#text) .. text .. self.app_id text = pkcs7_encode(text) local aes_crypt = assert( - aes:new(self.aes_key, nil, aes.cipher(256,"cbc"), {iv=iv}, nil, 0) + aes:new(self.aes_key, nil, self.cipher, {iv = self.iv}) ) + aes:set_padding(0) local encrypted = ngx.encode_base64(aes_crypt:encrypt(text)) local signature = self:get_sha1({self.token, timestamp, nonce, encrypted}) From 7375c7701ce903cc7dc4946018853923674f6089 Mon Sep 17 00:00:00 2001 From: James ZHANG Date: Thu, 8 Nov 2018 22:55:47 +0800 Subject: [PATCH 4/6] re-doc --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index daf08c7..e26c6c0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,44 @@ # WXBizMsgCrypt -模仿[微信公众平台加密解密技术方案](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318482&lang=zh_CN) -写了一个openresty 版的加密解密。 +[the WeChat Message Cryptography](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318482&lang=zh_CN) in the openresty lua verison. -注意点: -aes.lua不是openresty 主版本的,而是用了这个[no padding](https://github.com/openresty/lua-resty-string/pull/35) +## aes_pad.lua + +extend the [aes.lua](https://github.com/openresty/lua-resty-string) with a `set_padding` method, [discussing here](https://github.com/openresty/lua-resty-string/pull/35) + +## crypt.lua + +`encrypt(text, timestamp, nonce)` + +`decrypt(text_encrypted)` + +`get_sha1({token, timestamp, nonce, text_encrypted})` + +## Synopsis + +```lua +# nginx.conf: + +server { + location = /test { + content_by_lua_block { + local token = "pamtest" + local aesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG" + local timestamp = "1409304348" + local nonce = "xxxxxx" + local appId = "wxb11529c136998cb6" + local sample = "1407743423" + + local crypt = require "resty.crypt" + local wxmc = crypt:new(token, aesKey, appId) + local encrypted = wxmc:encrypt(sample, timestamp, nonce) + ngx.say("the sample encrypted: ", encrypted) + + local gsub = ngx.re.gsub + sample = gsub(encrypted, [=[.*(?:)?.*]=], "$1") + ngx.say("the sample decrypted: ", wxmc:decrypt(sample)) + } + } +} +``` From c493f06737d94271a5be7f57d6bc6e3b13366297 Mon Sep 17 00:00:00 2001 From: James ZHANG Date: Thu, 8 Nov 2018 22:59:34 +0800 Subject: [PATCH 5/6] typo `aes_crypt:set_padding` --- lib/resty/crypt.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/resty/crypt.lua b/lib/resty/crypt.lua index 560d915..67a0c3f 100644 --- a/lib/resty/crypt.lua +++ b/lib/resty/crypt.lua @@ -82,7 +82,7 @@ function _M.decrypt (self, encrypted) local aes_crypt = assert( aes:new(self.aes_key, nil, self.cipher, {iv = self.iv}) ) - aes:set_padding(0) + aes_crypt:set_padding(0) local text = aes_crypt:decrypt(ciphertext_dec) text = pkcs7_decode(string.sub(text, 17, #text)) @@ -102,7 +102,7 @@ function _M.encrypt (self, text, timestamp, nonce) local aes_crypt = assert( aes:new(self.aes_key, nil, self.cipher, {iv = self.iv}) ) - aes:set_padding(0) + aes_crypt:set_padding(0) local encrypted = ngx.encode_base64(aes_crypt:encrypt(text)) local signature = self:get_sha1({self.token, timestamp, nonce, encrypted}) From 3158e352fff7a41b8534ee2b22bf8d76197c11c6 Mon Sep 17 00:00:00 2001 From: James ZHANG Date: Tue, 27 Nov 2018 14:09:43 +0800 Subject: [PATCH 6/6] optimze(local vars) --- README.md | 2 +- lib/resty/aes_pad.lua | 8 +++--- lib/resty/crypt.lua | 62 ++++++++++++++++++++----------------------- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e26c6c0..613ad25 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ server { local gsub = ngx.re.gsub sample = gsub(encrypted, [=[.*(?:)?.*]=], "$1") - ngx.say("the sample decrypted: ", wxmc:decrypt(sample)) + ngx.say("the sample decrypted: ", wxmc:decrypt(sample)) } } } diff --git a/lib/resty/aes_pad.lua b/lib/resty/aes_pad.lua index f4751c2..17bb107 100644 --- a/lib/resty/aes_pad.lua +++ b/lib/resty/aes_pad.lua @@ -12,15 +12,17 @@ int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int pad); -- @link https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c#L569-L576 function aes.set_padding(self, pad) - if self._encrypt_ctx == nil or self._decrypt_ctx == nil then + local encrypt_ctx, decrypt_ctx = self._encrypt_ctx, self._decrypt_ctx + + if encrypt_ctx == nil or decrypt_ctx == nil then return nil, "the aes instance doesn't existed" end -- @link https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c#L402-L410 - C.EVP_CIPHER_CTX_set_padding(self._encrypt_ctx, pad) + C.EVP_CIPHER_CTX_set_padding(encrypt_ctx, pad) -- @link https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c#L515-L523 - C.EVP_CIPHER_CTX_set_padding(self._decrypt_ctx, pad) + C.EVP_CIPHER_CTX_set_padding(decrypt_ctx, pad) return 1 end diff --git a/lib/resty/crypt.lua b/lib/resty/crypt.lua index 67a0c3f..85a313c 100644 --- a/lib/resty/crypt.lua +++ b/lib/resty/crypt.lua @@ -3,66 +3,64 @@ local str = require "resty.string" local aes = require "resty.aes_pad" local bit = require "bit" local resty_sha1 = require "resty.sha1" -local setmetatable = setmetatable +local setmetatable, assert = setmetatable, assert +local str_char, str_sub, str_byte, str_format = string.char, string.sub, string.byte, string.format +local decode_base64, encode_base64 = ngx.decode_base64, ngx.encode_base64 local _M = { _VERSION = '0.2' } local mt = { __index = _M } local function pack_text_len(text_len) - return string.char( - bit.band(bit.rshift(text_len, 24), 0xff), - bit.band(bit.rshift(text_len, 16), 0xff), - bit.band(bit.rshift(text_len, 8), 0xff), - bit.band(text_len, 0xff) + local band, rshift, mask = bit.band, bit.rshift, 0xff + return str_char( + band(rshift(text_len, 24), mask), + band(rshift(text_len, 16), mask), + band(rshift(text_len, 8), mask), + band(text_len, mask) ) end local function unpack_text_len(text_len) - local a, b, c, d = string.byte(text_len, 1, 4) - return bit.bor(bit.lshift(a, 24), bit.lshift(b, 16), bit.lshift(c, 8), d), 1 + 4 + local bor, lshift = bit.bor, bit.lshift + local a, b, c, d = str_byte(text_len, 1, 4) + return bor(lshift(a, 24), lshift(b, 16), lshift(c, 8), d), 1 + 4 end local function pkcs7_encode(text) - local amount_to_pad = 32 - (#text % 32) + local PAD_BLOCK_LENGTH = 32 + local amount_to_pad = PAD_BLOCK_LENGTH - (#text % PAD_BLOCK_LENGTH) if amount_to_pad == 0 then - amount_to_pad = 32 + amount_to_pad = PAD_BLOCK_LENGTH end + local str_pad = str_char(amount_to_pad):rep(amount_to_pad) - local padding = "" - - local pad = string.char(amount_to_pad) - - for i = 1, amount_to_pad do - padding = padding .. pad - end - - return text .. padding + return text .. str_pad end local function pkcs7_decode(text) - local pad = string.byte(text, #text - 1) + local pad = str_byte(text, #text - 1) if (pad < 1 or pad > 32) then pad = 0 end - return string.sub(text, 1, #text - pad); + return str_sub(text, 1, #text - pad); end function _M.new (self, token, aes_key, app_id) - local aes_key = ngx.decode_base64(aes_key .. "=") + local aes_key = decode_base64(aes_key .. "=") local cipher = aes.cipher(256, "cbc") - local iv = string.sub(aes_key, 0, 16) + local iv = str_sub(aes_key, 0, 16) + return setmetatable({token = token, aes_key = aes_key, app_id = app_id, cipher = cipher, iv = iv}, mt) end function _M.get_sha1 (self, sha1_table) - local tb_sort = table.sort + local tb_sort, tb_join = table.sort, table.concat tb_sort(sha1_table) - local tb_join = table.concat local to_sha1 = tb_join(sha1_table) local sha1 = resty_sha1:new() @@ -72,27 +70,25 @@ function _M.get_sha1 (self, sha1_table) end function _M.decrypt (self, encrypted) - local ciphertext_dec = ngx.decode_base64(encrypted) + local ciphertext_dec = decode_base64(encrypted) if ciphertext_dec == nil then return nil end - local iv = string.sub(self.aes_key, 0, 16) local aes_crypt = assert( aes:new(self.aes_key, nil, self.cipher, {iv = self.iv}) ) aes_crypt:set_padding(0) local text = aes_crypt:decrypt(ciphertext_dec) - text = pkcs7_decode(string.sub(text, 17, #text)) + text = pkcs7_decode(str_sub(text, 17, #text)) - local xml_len = unpack_text_len(string.sub(text, 1, 4)) + local xml_len = unpack_text_len(str_sub(text, 1, 4)) - return string.sub(text, 4 + 1, xml_len + 4) + return str_sub(text, 4 + 1, xml_len + 4) end - function _M.encrypt (self, text, timestamp, nonce) local prefix = str.to_hex(random.bytes(8, true)) @@ -104,11 +100,11 @@ function _M.encrypt (self, text, timestamp, nonce) ) aes_crypt:set_padding(0) - local encrypted = ngx.encode_base64(aes_crypt:encrypt(text)) + local encrypted = encode_base64(aes_crypt:encrypt(text)) local signature = self:get_sha1({self.token, timestamp, nonce, encrypted}) local xml_content = "%s"; - return string.format(xml_content, encrypted, signature, timestamp, nonce) + return str_format(xml_content, encrypted, signature, timestamp, nonce) end return _M