在最近的项目中,使用nginx+lua来进行安全管理,其中要用到des算法;根据luajit官方的推荐,采用是lua-resty-nettle,但在使用过程当中发现,lua-resty-nettle采用的是0补位,而JDK中实现的是PKCS5Padding;html
继续寻找新的类库,因为团队对c并不熟悉,考虑到后续的维护方便,优先选择纯lua的实现,这时lua-lockbox进入到咱们的视野;但在使用过程当中发现虽然java和lua代码采用相同的分块模式(ECB),相同的补位(java是PKCS5Padding,lua是PKCS7Padding),但lua加密的数据没法用java解密。报错信息为:java
javax.crypto.BadPaddingException: Given final block not properly padded;nginx
错误信息很明细,lua补位不正确;直接看源码pkcs7.lua,发现其补位逻辑以下: local Stream = require("lockbox.util.stream");算法
local PKCS7Padding = function(blockSize,byteCount)安全
local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1;ui
local bytesLeft = paddingCount;加密
local stream = function()lua
if bytesLeft > 0 thenrest
bytesLeft = bytesLeft - 1;code
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