前言
在最近的項(xiàng)目中玛追,使用nginx+lua來(lái)進(jìn)行安全管理,其中要用到des算法扎即;根據(jù)luajit官方的推薦,采用是lua-resty-nettle,但在使用過(guò)程中發(fā)現(xiàn)花沉,lua-resty-nettle采用的是0補(bǔ)位,而JDK中實(shí)現(xiàn)的是PKCS5Padding进鸠;
繼續(xù)尋找新的類庫(kù)稠曼,由于團(tuán)隊(duì)對(duì)c并不熟悉,考慮到后續(xù)的維護(hù)方便客年,優(yōu)先選擇純lua的實(shí)現(xiàn)霞幅,這時(shí)lua-lockbox進(jìn)入到我們的視野;但在使用過(guò)程中發(fā)現(xiàn)雖然java和lua代碼采用相同的分塊模式(ECB),相同的補(bǔ)位(java是PKCS5Padding,lua是PKCS7Padding)量瓜,但lua加密的數(shù)據(jù)無(wú)法用java解密司恳。報(bào)錯(cuò)信息為:
javax.crypto.BadPaddingException: Given final block not properly padded;
錯(cuò)誤信息很明細(xì)绍傲,lua補(bǔ)位不正確扔傅;直接看源碼pkcs7.lua,發(fā)現(xiàn)其補(bǔ)位邏輯如下:
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到底是如何補(bǔ)位的呢烫饼?具體可參考如下資料:
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閱讀上面的資料猎塞,可以發(fā)現(xiàn)PKCS #7 填充字符串由一個(gè)字節(jié)序列組成,每個(gè)字節(jié)填充該字節(jié)序列的長(zhǎng)度杠纵。 假定塊長(zhǎng)度為 8荠耽,數(shù)據(jù)長(zhǎng)度為 9,則填充用八位字節(jié)數(shù)等于 7比藻,數(shù)據(jù)等于 FF FF FF FF FF FF FF FF FF:
數(shù)據(jù): 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而根據(jù)lua-lockbox的補(bǔ)位邏輯铝量,補(bǔ)位結(jié)果為:
lua-lockbox補(bǔ)位:FF FF FF FF FF FF FF FF FF 09 09 09 09 09 09 09 09 09
因此lua-lockbox對(duì)于PKCS7Padding的實(shí)現(xiàn)有誤,修改代碼為:
local paddingCount = blockSize - (byteCount % blockSize);
加密的補(bǔ)位問(wèn)題解決了银亲,但又發(fā)現(xiàn)另外一個(gè)問(wèn)題慢叨,解密時(shí),lua-lockbox沒(méi)有去掉補(bǔ)位數(shù)據(jù)群凶,從上面的pkcs7.lua代碼可以看到插爹,lua-lockbox并沒(méi)有實(shí)現(xiàn)該邏輯哄辣,采用臨時(shí)解決方案请梢,修改ecb.lua或cbc.lua,將其解密的finish方法修改為:
local data=Stream.toArray(outputQueue.pop)
local paddingByte=data[#data]
local realLength=#data-paddingByte--如果有padding,計(jì)算去除padding后的長(zhǎng)度
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;
目前的解決辦法比較粗糙赠尾,后續(xù)有時(shí)間進(jìn)行完善;
PKCS#5/7區(qū)別
在PKCS5Padding中毅弧,明確定義Block的大小是8位气嫁,而PKCS7Padding定義中,塊的大小是不確定的够坐,可以在1-255之間(塊長(zhǎng)度超出255的尚待研究)寸宵,填充值的算法都是一樣的:
value=k - (l mod k) ,K=塊大小,l=數(shù)據(jù)長(zhǎng)度元咙,如果l=8, 則需要填充額外的8個(gè)byte的8
DES填充方式
DES是對(duì)64位數(shù)據(jù)的加密算法梯影,如數(shù)據(jù)位數(shù)不足64位的倍數(shù),需要填充庶香,補(bǔ)充到64位的倍數(shù)甲棍。
NoPadding
API或算法本身不對(duì)數(shù)據(jù)進(jìn)行處理,加密數(shù)據(jù)由加密雙方約定填補(bǔ)算法赶掖。例如若對(duì)字符串?dāng)?shù)據(jù)進(jìn)行加解密感猛,可以補(bǔ)充\0或者空格,然后trimPKCS5Padding
加密前:數(shù)據(jù)字節(jié)長(zhǎng)度對(duì)8取余奢赂,余數(shù)為m陪白,若m>0,則補(bǔ)足8-m個(gè)字節(jié),字節(jié)數(shù)值為8-m膳灶,即差幾個(gè)字節(jié)就補(bǔ)幾個(gè)字節(jié)咱士,字節(jié)數(shù)值即為補(bǔ)充的字節(jié)數(shù),若為0則補(bǔ)充8個(gè)字節(jié)的8
解密后:取最后一個(gè)字節(jié)轧钓,值為m司致,則從數(shù)據(jù)尾部刪除m個(gè)字節(jié),剩余數(shù)據(jù)即為加密前的原文SSL3Padding
SSL3.0協(xié)議定義的填補(bǔ)算法