谈谈AES加密,网上有很多的版本,当我没有真正在加密安全问题前,总以为百度出来某个AES加密算法就可以直接使用,实际上当我真正要做加密时,遇到了很多的坑,原来不是拿过来就能用的。写下本篇文章,记录下曾经遇到的坑,严防以后再出现同样的坑。
原输入数据不够16字节的整数位时,就要补齐。因此就会有padding,若使用不同的padding,那么加密出来的结果也会不一样。
苹果提供给我们的API只有这一个函数用来加密或者解密:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
CCCryptorStatus CCCrypt(
CCOperation op, /* kCCEncrypt, etc. */
CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */
CCOptions options, /* kCCOptionPKCS7Padding, etc. */
const void *key,
size_t keyLength,
const void *iv, /* optional initialization vector */
const void *dataIn, /* optional per op and alg */
size_t dataInLength,
void *dataOut, /* data RETURNED here */
size_t dataOutAvailable,
size_t *dataOutMoved)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
|
CCOperation
只有两个值,要么是kCCEncrypt
表示加密,要么是kCCDecrypt
表示解密。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
enum {
kCCAlgorithmAES128 = 0,
kCCAlgorithmAES = 0,
kCCAlgorithmDES,
kCCAlgorithm3DES,
kCCAlgorithmCAST,
kCCAlgorithmRC4,
kCCAlgorithmRC2,
kCCAlgorithmBlowfish
};
typedef uint32_t CCAlgorithm;
|
我们这里使用的是kCCAlgorithmAES128
表示使用AES128位加密。
kCCOptionECBMode
,表示ECB:
1
2
3
4
5
6
7
8
9
|
enum {
/* options for block ciphers */
kCCOptionPKCS7Padding = 0x0001,
kCCOptionECBMode = 0x0002
/* stream ciphers currently have no options */
};
typedef uint32_t CCOptions;
|
依赖于第三方库:GTMBase64,这个库已经几年没有维护了,现在还是MRC版本,要使用请到GITHUB查看使用教程,那里有ARC接入说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
+ (NSString *)hyb_AESEncrypt:(NSString *)plainText password:(NSString *)key {
if (key == nil || (key.length != 16 && key.length != 32)) {
return nil;
}
char keyPtr[kCCKeySizeAES128+1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCBlockSizeAES128+1];
memset(ivPtr, 0, sizeof(ivPtr));
[key getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSData* data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
unsigned long newSize = 0;
if(diff > 0) {
newSize = dataLength + diff;
}
char dataPtr[newSize];
memcpy(dataPtr, [data bytes], [data length]);
for(int i = 0; i < diff; i++) {
// 这里是关键,这里是使用NoPadding的
dataPtr[i + dataLength] = 0x0000;
}
size_t bufferSize = newSize + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
memset(buffer, 0, bufferSize);
size_t numBytesCrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionECBMode,
[key UTF8String],
kCCKeySizeAES128,
ivPtr,
dataPtr,
sizeof(dataPtr),
buffer,
bufferSize,
&numBytesCrypted);
if (cryptStatus == kCCSuccess) {
NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
return [GTMBase64 stringByEncodingData:resultData];
}
free(buffer);
return nil;
}
|
对于加密算法,大家一定要注意,保证iOS、安卓、服务端的加密规则是一定的,建议统一使用No Padding的,这里使用No Padding是这样的:
1
2
3
4
5
6
|
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接入说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
+ (NSString *)hyb_AESDecrypt:(NSString *)encryptText password:(NSString *)key {
if (key == nil || (key.length != 16 && key.length != 32)) {
return nil;
}
char keyPtr[kCCKeySizeAES128 + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCBlockSizeAES128 + 1];
memset(ivPtr, 0, sizeof(ivPtr));
[key getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSData *data = [GTMBase64 decodeData:[encryptText dataUsingEncoding:NSUTF8StringEncoding]];
NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesCrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionECBMode,
[key UTF8String],
kCCBlockSizeAES128,
ivPtr,
[data bytes],
dataLength,
buffer,
bufferSize,
&numBytesCrypted);
if (cryptStatus == kCCSuccess) {
NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
NSString *decoded=[[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
return decoded;
}
free(buffer);
return nil;
}
|
解密时也得跟加密一样指定为ECB,否则解出来会出现乱码,或者末尾会出现\0、\t之类的符号。
开发中总会遇到各种坑,网上查了很多的资料,但是终究没有说明解决的办法,而是只将自己的代码放出来。对于刚接触这方面知识的开发人员来说,是很懵懂的。甚至很多新手会觉得系统就是这样的,我也没办法。其实总会有解决办法的,关键在于与其他各端统一连调。
iOS与PHP/Android AES128 ECB NoPadding加密
原文:http://www.cnblogs.com/dzhs/p/5377060.html