前言
????前段時(shí)間調(diào)研了iOS有關(guān)加密的框架CommonCrypto
.在查閱資料的過程中整胃,有一篇《iOS加密解密:AES,DES,3DES,BLOWFISH(含有多種模式和算法說明)》
的文章颗圣。
在文章中作者對(duì)需要加密的數(shù)據(jù)實(shí)現(xiàn)PKCS7Padding填充,并在調(diào)用CCCryptorCreateWithMode()
函數(shù)時(shí)將padding參數(shù)賦值為ccNoPadding(不填充)
屁使。在作者的基礎(chǔ)上我增添了ANSIX923
,ISO10126
以及Zero
填充方式,以及解密之后的去除數(shù)據(jù)的填充在岂。
????在對(duì)稱加密中,可以概分為兩種模式加密蛮寂,流加密以及塊加密蔽午,當(dāng)我們使用塊加密(也就是分組加密)的時(shí)候,例如AES酬蹋、DES及老,每次是對(duì)固定大小的分組數(shù)據(jù)進(jìn)行處理。但是大多數(shù)需要加密的數(shù)據(jù)并不是固定大小的倍數(shù)長度范抓。例如AES數(shù)據(jù)塊為128位骄恶,也就是16字節(jié)長度,而需要加密的長度可能為15尉咕、26等等叠蝇。為了解決這個(gè)問題璃岳,我們就需要對(duì)數(shù)據(jù)進(jìn)行填補(bǔ)操作年缎,將數(shù)據(jù)補(bǔ)齊至對(duì)應(yīng)塊長度。
補(bǔ)碼原理
接下來呢講一下關(guān)于數(shù)據(jù)填充ANSIX923
铃慷、ISO10126
单芜、PKCS7
以及Zero
具體的補(bǔ)碼原理。
注犁柜,補(bǔ)碼原理來自于文章Padding (cryptography)洲鸠,讀中文的小伙伴們可以查看這篇 關(guān)于PKCS5Padding與PKCS7Padding的區(qū)別.
-
ANSIX923 填充方式
ANSIX923 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中最后一個(gè)字節(jié)填充為
需要填充的字節(jié)長度值
, 填充字節(jié)中其余字節(jié)均填充數(shù)字零.
例:假定塊長度為8 ,數(shù)據(jù)長度為 10,則填充字節(jié)數(shù)等于 6馋缅,數(shù)據(jù)等于 FF FF FF FF FF FF FF FF FF DD: 數(shù)據(jù): FF FF FF FF FF FF FF FF FF X923 填充后: FF FF FF FF FF FF FF FF | FF DD 00 00 00 00 00 06
-
ISO10126 填充方式
ISO10126 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中最后一個(gè)字節(jié)填充為
需要填充的字節(jié)長度值
, 填充字節(jié)中其余字節(jié)均填充隨機(jī)數(shù)值.
例:假定塊長度為 16扒腕,數(shù)據(jù)長度為 9,則填充字節(jié)數(shù)等于 7,數(shù)據(jù)等于 FF FF FF FF FF FF FF FF FF : | FF FF FF FF FF FF FF FF FF 73 68 C4 81 A6 23 07 |
-
PKCS7 填充方式
PKCS7 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中所有字節(jié)填充為
需要填充的字節(jié)長度值
例:假定塊長度為 8萤悴,數(shù)據(jù)長度為 3,則填充字節(jié)數(shù)等于 5瘾腰,數(shù)據(jù)等于 FF FF FF : | FF FF FF 05 05 05 05 05 |
-
Zero 填充方式
Zero 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中所有字節(jié)填充為
0x00
,零填充在數(shù)據(jù)最后字節(jié)為零的時(shí)候可能不可逆
例:假定塊長度為 8覆履,數(shù)據(jù)長度為 2,則填充字節(jié)數(shù)等于 6蹋盆,數(shù)據(jù)等于 FF FF : | FF FF 00 00 00 00 00 00 |
特定的费薄,為了使算法可以逆向去除多余的填充字符,所以當(dāng)數(shù)據(jù)長度恰好等于塊長度的時(shí)候栖雾,需要補(bǔ)足塊長度的字節(jié).例如塊長度為8楞抡,數(shù)據(jù)長度為8,則填充字節(jié)數(shù)等于8.
代碼部分
為了可以完成自己的填充算法方便調(diào)用析藕,并同時(shí)省去PKCS7部分的填充操作召廷,先實(shí)現(xiàn)一個(gè)枚舉并保持系統(tǒng)枚舉CCPadding
的枚舉值也在其中,代碼實(shí)例如下
typedef enum : NSUInteger {
CcCryptorNoPadding = 0, //No Padding to source Data
CcCryptorPKCS7Padding = 1, // PKCS_7 | Each byte fills in the length of the sequence of the bytes . ***This Padding Mode use the system method.***
CcCryptorZeroPadding = 2, // 0x00 Padding | Each byte fills 0x00
CcCryptorANSIX923, // The last byte fills the length of the byte sequence, and the remaining bytes are filled with 0x00.
CcCryptorISO10126 // The last byte fills the length of the byte sequence and the remaining bytes fill the random data.
}CcCryptorPadding;
并在調(diào)用CCCryptorCreateWithMode
函數(shù)之前账胧,判斷填充模式CCPadding paddingMode = ((padding == ccPKCS7Padding) ? ccPKCS7Padding:ccNoPadding) ;
,使得我們可以正常使用系統(tǒng)的填充模式之外使用其他自定義填充模式柱恤。
下面是iOS部分如何實(shí)現(xiàn)對(duì)數(shù)據(jù)進(jìn)行填充,此函數(shù)需要在CCCryptorCreateWithMode
函數(shù)之前對(duì)數(shù)據(jù)進(jìn)行處理.
// Fill in the bytes that need to be encrypted.
static NSData * bitPadding(CCOperation operation, CCAlgorithm algorithm ,CcCryptorPadding padding, NSData *data)
{
//當(dāng)模式為PKCS7填充時(shí)找爱,我們選擇使用系統(tǒng)PKCS7填充梗顺,返回原始數(shù)據(jù),避免二次填充
if (padding == CcCryptorPKCS7Padding) {
return data;
}
// 當(dāng)前處于加密模式车摄,并且不是流加密的情況下.
if (operation == kCCEncrypt && (algorithm != CcCryptoAlgorithmRC4) ) {
//獲取一份可操作的數(shù)據(jù)對(duì)象
NSMutableData *sourceData = data.mutableCopy;
//設(shè)置默認(rèn)塊大小為8.
int blockSize = 8;
switch (algorithm) {
// 當(dāng)AES加密時(shí)寺谤,塊大小為16.
case kCCAlgorithmAES:
blockSize = kCCBlockSizeAES128;
break;
case kCCAlgorithmDES:
case kCCAlgorithm3DES:
case kCCAlgorithmCAST:
case kCCAlgorithmBlowfish:
case kCCAlgorithmRC2:
default:
blockSize = 8;
break;
}
// 根據(jù)選定填充模式實(shí)現(xiàn)不同填充
switch (padding) {
// 零填充
case CcCryptorZeroPadding:
{
int pad = 0x00;
int diff = blockSize - (sourceData.length % blockSize);
for (int i = 0; i < diff; i++) {
[sourceData appendBytes:&pad length:1];
}
}
break;
// ANSIX923
case CcCryptorANSIX923:
{
int pad = 0x00;
int diff = blockSize - (sourceData.length % blockSize);
for (int i = 0; i < diff - 1; i++) {
[sourceData appendBytes:&pad length:1];
}
[sourceData appendBytes:&diff length:1];
}
break;
//ISO10126
case CcCryptorISO10126:
{
int diff = blockSize - (sourceData.length % blockSize);
for (int i = 0; i < diff - 1; i++) {
int pad = arc4random() % 254 + 1;
[sourceData appendBytes:&pad length:1];
}
[sourceData appendBytes:&diff length:1];
}
break;
default:
break;
}
return sourceData;
}
return data;
}
當(dāng)我們對(duì)數(shù)據(jù)解密過后,如果不去除填充字節(jié)吮播,會(huì)發(fā)現(xiàn)在解密后的字符串末尾發(fā)現(xiàn)填充字節(jié)造成的字符
因此变屁,我們需要在解密過后對(duì)數(shù)據(jù)進(jìn)行去除可能存在的填充字節(jié)的處理。代碼示例如下:
static NSData * removeBitPadding(CCOperation operation, CCAlgorithm algorithm ,CcCryptorPadding padding, NSData *sourceData)
{
//若為PKCS7意狠,則交由系統(tǒng)函數(shù)處理.
if (padding == CcCryptorPKCS7Padding) {
return sourceData;
}
//若為解密粟关,并且為塊加密
if (operation == kCCDecrypt && (algorithm != CcCryptoAlgorithmRC4) ) {
// 設(shè)置需要非填充字節(jié)長度為0
int correctLength = 0;
// 設(shè)置塊長度默認(rèn)值為8
int blockSize = 8;
判斷塊長度
switch (algorithm) {
case kCCAlgorithmAES:
blockSize = kCCBlockSizeAES128;
break;
case kCCAlgorithmDES:
case kCCAlgorithm3DES:
case kCCAlgorithmCAST:
case kCCAlgorithmBlowfish:
default:
blockSize = 8;
break;
}
// 將data -> byte
Byte *testByte = (Byte *)[sourceData bytes];
// 獲取最后一位字節(jié).
char end = testByte[sourceData.length - 1];
// 根據(jù)選定填充模式不同去除可能存在的填充字符
if (padding == CcCryptorZeroPadding && end == 0) {
for (int i = (short)sourceData.length - 1; i > 0 ; i--) {
//若不相等為零字節(jié),則停止
if (testByte[i] != end) {
// 非填充字節(jié)的長度為i
correctLength = i + 1;
break;
}
}
}
else if ((padding == CcCryptorANSIX923 || padding == CcCryptorISO10126) && (end > 0 && end < blockSize + 1)){
if (padding == CcCryptorISO10126 || ( testByte[sourceData.length - 2] == 0 && testByte[sourceData.length - end] == 0)) {
//若填充模式為ANSIX923 或者ISO10126 則根據(jù)最后一位字節(jié)的大小判斷填充字節(jié)的長度以及非填充字節(jié)的長度
correctLength = (short)sourceData.length - end;
}
}
// 直接根據(jù)獲取的非填充字節(jié)的長度以及Byte 生成新的僅有正確字節(jié)的data數(shù)據(jù).
NSData *data = [NSData dataWithBytes:testByte length:correctLength];
return data;
}
return sourceData;
}
想看源碼的可以去Github.
有小伙伴們對(duì)CommonCrypto
环戈、對(duì)稱加密感興趣的可以看看這篇文章iOS 關(guān)于CommonCrypto加密 ②(CommonCryptor AES|DES|3DES|CAST|BlowFish)