由于公司对于信息安全比较注重,需要对json数据并用AES加密,然后生成url_safe base64编码。然后继续拼接时间戳 session还有其他字段进行MD5加密。然后使用RAC签名后传输。本来没觉得什么,以为加密算法之前也接触过,或者去网上直接套用就好,结果,发现没那么简单 = =
一: Base64
- 是网络上使用最广泛的编码系统,能够将任何二进制数据,转换成只有 65 个字符 * 组成的文本文件.
- 编码后的数据由 a-z A-Z 0-9 + / = 表示.
- base64 编码后的结果能够反算,不够安全.
- base64 是所有现代加密算法的基础算法.
base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本,使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。
完整的base64定义可见RFC 1421和RFC 2045。编码后的数据比原始数据略长,为原来的4/3.。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。( 如果源码为760个字符, 则编码后的长度为760 * (4 / 3) + 10(换行符) )
URL_Safe Base64
这个参数意思是加密时不使用对URL和文件名有特殊意义的字符来作为加密字符,因为标准base64包含"+"和"/",因此不适合直接放在URL中传输.因为URL编译器会将"+","/"转换成XX%的样式.而这些"%"在录入数据库的时候还会进行转码,为了解决此问题可采用一种适用于URL的改进base64,它不在末位添加"=",将"+"转换成"-",将"/"转换成"_".
iOS原生API的有它自己的局限性, 所以我使用了base64的神器(GTMBase, 这个框架满足了每一种的base64模式的编解码.)
因此为了两个平台的兼容,使用总结如下
GTMBase64 Padded YES != Java DEFAULT
GTMBase64 Padded NO == Java NO_PADDING | NO_WRAP
GTMBase64 Padded YES == Java NO_WRAP
GTMBase64 websafe Padded NO == java NO_PADDING | NO_WRAP | URL_SAFE
GTMBase64 websafe Padded YES == java NO_WRAP | URL_SAFE
GTMBase64传送门
二:AES加解密
- 相较于DES和3DES算法而言,AES算法有着更高的速度和资源使用效率,安全级别也较之更高了,被称为下一代加密标准。
- AES使用的是对称加密.所谓对称加密就是加解密双方使用的密钥相同.因此通过一种保密的方法让客户端与服务器拥有该密钥,即可成功使用加解密.
常见的IOS中AES加密
//加密
+(NSData *) AES128EncryptWithKey:(NSData*)key data:(NSData*)data
{
return [self doCipher:data iv:nil key:key context:kCCEncrypt error:nil];
}
//解密
+(NSData *) AES128DecryptWithKey:(NSData*)key data:(NSData*)data
{
return [self doCipher:data iv:nil key:key context:kCCDecrypt error:nil];
}
+ (NSData *)doCipher:(NSData *)dataIn
iv:(NSData *)iv
key:(NSData *)symmetricKey
context:(CCOperation)encryptOrDecrypt
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
symmetricKey.bytes,
symmetricKey.length,
iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:@"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}
坑爹的地方地方来了,传给后台永远是乱码.用了网上大多数方法都不行...一开始怀疑是不是URL_Safe Base64错了,或者是哪个地方没有UTF-8。与后台的沟通,反复验证数据加密后的字符串,就是对不上~~~
- 网上拼命查资料发现好像是iv不对,iv是什么??......
- 大概意思说,此属性可选,但只能用于CBC模式。如果出现那么他的长度必须和算法的block size保持一致。 如果是因为默认选择的CBC模式而且向量没有定义,那么向量会被定义为NULL。如果选择了ECB模式或是其他的流密码算法,之前所说的逻辑都不成立。
CBC??ECB一脸懵逼,继续查资料
- 初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密,然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV。对于CBC和CFB,重用IV会导致泄露明文首个块的某些信息,亦包括两个不同消息中相同的前缀。对于OFB和CTR而言,重用IV会导致完全失去安全性。另外,在CBC模式中,IV在加密时必须是无法预测的;特别的,在许多实现中使用的产生IV的方法,例如SSL2.0使用的,即采用上一个消息的最后一块密文作为下一个消息的IV,是不安全的。
ECB模式不需要初始化向量,CBC需要。
完善之后AES的代码
//AES加密
+ (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv withNSData:(NSData *)data
{
char keyPtr[kCCKeySizeAES128+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCKeySizeAES128+1];
bzero(ivPtr, sizeof(ivPtr));
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
int newSize = 0;
if(diff > 0)
{
newSize = (int)(dataLength + diff);
}
char dataPtr[newSize];
memcpy(dataPtr, [data bytes], [data length]);
for(int i = 0; i < diff; i++)
{
dataPtr[i + dataLength] = 0x00;
}
size_t bufferSize = newSize + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
0x00, //No padding
keyPtr,
kCCKeySizeAES128,
ivPtr,
dataPtr,
sizeof(dataPtr),
buffer,
bufferSize,
&numBytesEncrypted);
if(cryptStatus == kCCSuccess)
{
// NSData *data =[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
// NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
return nil;
}
//AES解密
+ (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv withNSData:(NSData *)data
{
char keyPtr[kCCKeySizeAES128+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCKeySizeAES128+1];
bzero(ivPtr, sizeof(ivPtr));
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
0x00, //No padding
keyPtr,
kCCKeySizeAES128,
ivPtr,
[data bytes],
dataLength,
buffer,
bufferSize,
&numBytesEncrypted);
if(cryptStatus == kCCSuccess)
{
// NSData *data =[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
// NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
return nil;
}
写个工具类复制过去就可以了
三:MD5
- 对任意的数据进行计算,生成固定长度的字符串.32个字符.
- 一般用来加密密码.
- 有时候也用来验证文件下载时,是否被篡改过.
文件下载完成之后计算文件的md5值,与服务器计算的MD5值比较,如果不一样那么这个文件在下载的过程中被篡改了.
MD5终端命令
# 得到文件的MD5值
$ md5 文件名
# 得到字符串的MD5值
md5 -s "string"
+(NSString *) md5: (NSString *) inPutText
{
const char *cStr = [inPutText UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(cStr, strlen(cStr), result);
return [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
] lowercaseString];
}
- 如果原始密码过于简单,直接进行MD5加密是很容易被暴力破解的.
- 为了增强密码的安全性,防止加密的密码被暴力破解,可以向原始密码中加盐.
- 盐 : 服务器端和客户端约定的一个字符串.
- MD5+盐 : 原始密码+盐拼接出新的密码字符串,再进行MD加密.
- 以上为加一勺盐,比单纯的直接MD5加密安全性要高.
- 盐要足够的咸,越咸越安全.
RSA部分会在第二部分,如果有时间的话 笑~
参考文章:加密解密介绍