iOS與PHP/Android AES128 ECB NoPadding加密
來自: http://www.henishuo.com/ios-aes128-ecb-nopadding/
前言
談談AES加密,網上有很多的版本,當我沒有真正在加密安全問題前,總以為百度出來某個AES加密算法就可以直接使用,實際上當我真正要做加密時,遇到了很多的坑,原來不是拿過來就能用的。寫下本篇文章,記錄下曾經遇到的坑,嚴防以后再出現同樣的坑。
AES規則
原輸入數據不夠16字節的整數位時,就要補齊。因此就會有padding,若使用不同的padding,那么加密出來的結果也會不一樣。
AES加密算法
蘋果提供給我們的API只有這一個函數用來加密或者解密:
CCCryptorStatus CCCrypt( CCOperation op, /* kCCEncrypt, etc. */ CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */ CCOptions options, /* kCCOptionPKCS7Padding, etc. */ const void *key, size_tkeyLength, const void *iv, /* optional initialization vector */ const void *dataIn, /* optional per op and alg */ size_tdataInLength, void *dataOut, /* data RETURNED here */ size_tdataOutAvailable, size_t*dataOutMoved) __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
- 其中第一個 CCOperation 只有兩個值,要么是 kCCEncrypt 表示加密,要么是 kCCDecrypt 表示解密。
- 第二個參數表示加密的算法,它只有以下向種類型:
enum {
kCCAlgorithmAES128 = 0,
kCCAlgorithmAES = 0,
kCCAlgorithmDES,
kCCAlgorithm3DES,
kCCAlgorithmCAST,
kCCAlgorithmRC4,
kCCAlgorithmRC2,
kCCAlgorithmBlowfish
};
typedef uint32_tCCAlgorithm;
我們這里使用的是 kCCAlgorithmAES128 表示使用AES128位加密。
- 第三個參數表示選項,這里使用的是 kCCOptionECBMode ,表示ECB:
enum {
/* options for block ciphers */
kCCOptionPKCS7Padding = 0x0001,
kCCOptionECBMode = 0x0002
/* stream ciphers currently have no options */
};
typedef uint32_tCCOptions;
- 第四個參數表示加密/解密的密鑰。
- 第五個參數keyLength表示密鑰的長度。
- 第六個參數iv是個固定值,通過直接使用密鑰即可。大家一定要注視這個參數,如果安卓、服務端和iOS端不統一,那么加密結果就會不一樣,解密可能能解出來,但是解密后在末尾會出現一些\0、\t之類的。
- 第七個參數dataIn表示要加密/解密的數據。
- 第八個參數dataInLength表示要加密/解密的數據的長度。
- 第九個參數dataOut用于接收加密后/解密后的結果。
- 第十個參數dataOutAvailable表示加密后/解密后的數據的長度。
- 第十一個參數dataOutMoved表示實際加密/解密的數據的長度。(因為有補齊)
加密算法
依賴于第三方庫: GTMBase64 ,這個庫已經幾年沒有維護了,現在還是MRC版本,要使用請到GITHUB查看使用教程,那里有ARC接入說明:
+ (NSString *)hyb_AESEncrypt:(NSString *)plainTextpassword:(NSString *)key {
if (key == nil || (key.length != 16 && key.length != 32)) {
return nil;
}
char keyPtr[kCCKeySizeAES128+1];
memset(keyPtr, 0, sizeof(keyPtr));
[keygetCString:keyPtrmaxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];
char ivPtr[kCCBlockSizeAES128+1];
memset(ivPtr, 0, sizeof(ivPtr));
[keygetCString:ivPtrmaxLength:sizeof(ivPtr)encoding:NSUTF8StringEncoding];
NSData* data = [plainTextdataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [datalength];
int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
unsigned long newSize = 0;
if(diff > 0) {
newSize = dataLength + diff;
}
char dataPtr[newSize];
memcpy(dataPtr, [databytes], [datalength]);
for(int i = 0; i < diff; i++) {
// 這里是關鍵,這里是使用NoPadding的
dataPtr[i + dataLength] = 0x0000;
}
size_tbufferSize = newSize + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
memset(buffer, 0, bufferSize);
size_tnumBytesCrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionECBMode,
[keyUTF8String],
kCCKeySizeAES128,
ivPtr,
dataPtr,
sizeof(dataPtr),
buffer,
bufferSize,
&numBytesCrypted);
if (cryptStatus == kCCSuccess) {
NSData *resultData = [NSDatadataWithBytesNoCopy:bufferlength:numBytesCrypted];
return [GTMBase64stringByEncodingData:resultData];
}
free(buffer);
return nil;
}
對于加密算法,大家一定要注意,保證iOS、安卓、服務端的加密規則是一定的,建議統一使用No Padding的,這里使用No Padding是這樣的:
for(int i = 0; i < diff; i++) {
// 這里是關鍵,這里是使用NoPadding的
dataPtr[i + dataLength] = 0x0000;
}
其實所謂Padding就是指在位數不夠需要補齊時,使用什么來填充,而No Padding就是使用16個0,對應0x0000.如果三端不統一,加密出來就算能解密,也會出現一些奇怪的字符,甚至會有部分亂碼。
另外,這里使用的是 kCCOptionECBMode ,也就是ECB。在安卓端和PHP端,也得使用ECB。在調試過程中,發現PHP使用CBC解密不了IOS端的。于是改成了使用ECB。
解密算法
依賴于第三方庫: GTMBase64 ,這個庫已經幾年沒有維護了,現在還是MRC版本,要使用請到GITHUB查看使用教程,那里有ARC接入說明:
+ (NSString *)hyb_AESDecrypt:(NSString *)encryptTextpassword:(NSString *)key {
if (key == nil || (key.length != 16 && key.length != 32)) {
return nil;
}
char keyPtr[kCCKeySizeAES128 + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[keygetCString:keyPtrmaxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];
char ivPtr[kCCBlockSizeAES128 + 1];
memset(ivPtr, 0, sizeof(ivPtr));
[keygetCString:ivPtrmaxLength:sizeof(ivPtr)encoding:NSUTF8StringEncoding];
NSData *data = [GTMBase64decodeData:[encryptTextdataUsingEncoding:NSUTF8StringEncoding]];
NSUInteger dataLength = [datalength];
size_tbufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_tnumBytesCrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionECBMode,
[keyUTF8String],
kCCBlockSizeAES128,
ivPtr,
[databytes],
dataLength,
buffer,
bufferSize,
&numBytesCrypted);
if (cryptStatus == kCCSuccess) {
NSData *resultData = [NSDatadataWithBytesNoCopy:bufferlength:numBytesCrypted];
NSString *decoded=[[NSString alloc]initWithData:resultDataencoding:NSUTF8StringEncoding];
return decoded;
}
free(buffer);
return nil;
}
解密時也得跟加密一樣指定為ECB,否則解出來會出現亂碼,或者末尾會出現\0、\t之類的符號。
寫在最后
開發中總會遇到各種坑,網上查了很多的資料,但是終究沒有說明解決的辦法,而是只將自己的代碼放出來。對于剛接觸這方面知識的開發人員來說,是很懵懂的。甚至很多新手會覺得系統就是這樣的,我也沒辦法。其實總會有解決辦法的,關鍵在于與其他各端統一連調。
關注我
Swift/ObjC技術群一:324400294(已滿)
Swift/ObjC技術群二:494669518
ObjC/Swift高級群:461252383(注明年限,新手勿擾)
關注微信公眾號: iOSDevShares
關注新浪微博賬號:標哥Jacky
標哥的GITHUB地址: CoderJackyHuang
支持并捐助
如果您覺得文章對您很有幫忙,希望得到您的支持。您的捐肋將會給予我最大的鼓勵,感謝您的支持!
| 支付寶捐助 | |||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|