lua-lockbox padding problem

前言

在最近的项目中,使用nginx+lua来进行安全管理,其中要用到des算法;根据luajit官方的推荐,采用是lua-resty-nettle,但在使用过程中发现,lua-resty-nettle采用的是0补位,而JDK中实现的是PKCS5Padding;

继续寻找新的类库,由于团队对c并不熟悉,考虑到后续的维护方便,优先选择纯lua的实现,这时lua-lockbox进入到我们的视野;但在使用过程中发现虽然java和lua代码采用相同的分块模式(ECB),相同的补位(java是PKCS5Padding,lua是PKCS7Padding),但lua加密的数据无法用java解密。报错信息为:

javax.crypto.BadPaddingException: Given final block not properly padded;

错误信息很明细,lua补位不正确;直接看源码pkcs7.lua,发现其补位逻辑如下:


local Stream = require("lockbox.util.stream");

local PKCS7Padding = function(blockSize,byteCount)

local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1;

local bytesLeft = paddingCount;

local stream = function()

if bytesLeft > 0 then

bytesLeft = bytesLeft - 1;

return paddingCount;

else

return nil;

end

end

return stream;

end

return PKCS7Padding;

那么PKCS5Padding到底是如何补位的呢?具体可参考如下资料:

  • PKCS #7: Cryptographic Message Syntax Standard
    :An RSA Laboratories Technical Note, Version 1.5. Revised November 1, 1993.

  • PKCS #5: Password-Based Encryption Standard:
    An RSA Laboratories Technical Note, Version 1.5. Revised November 1, 1993. f

    阅读上面的资料,可以发现PKCS #7 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。 假定块长度为 8,数据长度为 9,则填充用八位字节数等于 7,数据等于 FF FF FF FF FF FF FF FF FF:
    数据: FF FF FF FF FF FF FF FF FF
    PKCS7 填充: FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07

    而根据lua-lockbox的补位逻辑,补位结果为:
    lua-lockbox补位:FF FF FF FF FF FF FF FF FF 09 09 09 09 09 09 09 09 09
    因此lua-lockbox对于PKCS7Padding的实现有误,修改代码为:

local paddingCount = blockSize - (byteCount % blockSize);

加密的补位问题解决了,但又发现另外一个问题,解密时,lua-lockbox没有去掉补位数据,从上面的pkcs7.lua代码可以看到,lua-lockbox并没有实现该逻辑,采用临时解决方案,修改ecb.lua或cbc.lua,将其解密的finish方法修改为:

        local data=Stream.toArray(outputQueue.pop)
        local paddingByte=data[#data]

        local realLength=#data-paddingByte--如果有padding,计算去除padding后的长度
        local padded=true
        for i=#data,realLength+1,-1 do
            if(data[i]~=paddingByte) then
                padded=false
            end
        end
        print("realLength is "..realLength)
        local paddedBytes=Array.slice(data,1,realLength)
        
        if padded then
            Array.writeToQueue(outputQueue,paddedBytes)
        end
        --paddingStream = padding(blockCipher.blockSize,inputQueue.getHead());
        --public.update(paddingStream);

        return public;

目前的解决办法比较粗糙,后续有时间进行完善;

PKCS#5/7区别

在PKCS5Padding中,明确定义Block的大小是8位,而PKCS7Padding定义中,块的大小是不确定的,可以在1-255之间(块长度超出255的尚待研究),填充值的算法都是一样的:

value=k - (l mod k)  ,K=块大小,l=数据长度,如果l=8, 则需要填充额外的8个byte的8

DES填充方式

DES是对64位数据的加密算法,如数据位数不足64位的倍数,需要填充,补充到64位的倍数。

  • NoPadding
    API或算法本身不对数据进行处理,加密数据由加密双方约定填补算法。例如若对字符串数据进行加解密,可以补充\0或者空格,然后trim

  • PKCS5Padding
    加密前:数据字节长度对8取余,余数为m,若m>0,则补足8-m个字节,字节数值为8-m,即差几个字节就补几个字节,字节数值即为补充的字节数,若为0则补充8个字节的8
    解密后:取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文

  • SSL3Padding
    SSL3.0协议定义的填补算法

参考资料

  • http://www.open-open.com/solution/view/1320502797546
  • http://www.cnblogs.com/AloneSword/p/3491466.html

你可能感兴趣的:(lua-lockbox padding problem)