在最近的项目中,使用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