前言
在項(xiàng)目開(kāi)發(fā)過(guò)程中拜银,為了保證傳輸數(shù)據(jù)的安全性梭灿,我們經(jīng)常要對(duì)傳輸?shù)膬?nèi)容進(jìn)行加密處理缔逛,以增加別人破解的成本备埃。常用的加密算法有很多,今天我們先圍繞AES
加密算法進(jìn)行一個(gè)使用總結(jié)
AES算法介紹
AES
是高級(jí)加密標(biāo)準(zhǔn)(Advanced Encryption Standard)的縮寫(xiě)褐奴,在密碼學(xué)中又被稱為Rijndael加密法按脚,如果想對(duì)AES
的背景有更多的了解可以移步到維基百科-高級(jí)加密標(biāo)準(zhǔn)
AES
加密時(shí)需要統(tǒng)一四個(gè)參數(shù):
- 密鑰長(zhǎng)度 (Key Size)
- 加密模式 (Cipher Mode)
- 填充方式 (Padding)
- 初始向量 (Initialization Vector)
由于前后端開(kāi)發(fā)所使用的語(yǔ)言不統(tǒng)一,導(dǎo)致經(jīng)常出現(xiàn)前后端之間互相不能解密的情況出現(xiàn)敦冬,其實(shí)辅搬,無(wú)論什么語(yǔ)言系統(tǒng),AES
的算法總是相同的脖旱,導(dǎo)致結(jié)果不一致的原因在于上述的四個(gè)參數(shù)不一致堪遂,下面就來(lái)了解一下這四個(gè)參數(shù)的含義
密鑰長(zhǎng)度
AES
算法下,key的長(zhǎng)度有三種:128萌庆、192溶褪、256 bits,三種不同密鑰長(zhǎng)度就需要我們傳入的key傳入不同長(zhǎng)度的字符串践险,例如我們選擇AES-128,那我們定的key需要是長(zhǎng)度為16的字符串
加密模式
AES
屬于塊加密猿妈,塊加密中有CBC吹菱、ECB、CTR彭则、OFB鳍刷、CFB等幾種工作模式,為了保持前后端統(tǒng)一俯抖,我們選擇ECB模式
填充方式
由于塊加密只能對(duì)特定長(zhǎng)度的數(shù)據(jù)塊進(jìn)行加密输瓜,因此CBC、ECB模式需要在最后一數(shù)據(jù)塊加密前進(jìn)行數(shù)據(jù)填充
初始向量
使用除ECB以外的其他加密模式均需要傳入一個(gè)初始向量蚌成,其大小與Block Size相等
代碼實(shí)現(xiàn)
PHP端代碼實(shí)現(xiàn)
<?php
/*
* 定義類cryptAES 專用于AES加解密
* 初始化時(shí)傳入密鑰長(zhǎng)度前痘、加密Key、初始向量担忧、加密模式四個(gè)字段
*/
class cryptAES
{
public $iv = null;
public $key = null;
public $bit = 128;
private $cipher;
public function __construct($bit, $key, $iv, $mode)
{
if(empty($bit) || empty($key) || empty($iv) || empty($mode))
{
return NULL;
}
$this->bit = $bit;
$this->key = $key;
$this->iv = $iv;
$this->mode = $mode;
switch($this->bit)
{
case 192 : $this->cipher = MCRYPT_RIJNDAEL_192; break;
case 256 : $this->cipher = MCRYPT_RIJNDAEL_256; break;
default : $this->cipher = MCRYPT_RIJNDAEL_128;
}
switch($this->mode)
{
case 'ecb' : $this->mode = MCRYPT_MODE_ECB; break;
case 'cfb' : $this->mode = MCRYPT_MODE_CFB; break;
case 'ofb' : $this->mode = MCRYPT_MODE_OFB; break;
case 'nofb' : $this->mode = MCRYPT_MODE_NOFB; break;
default : $this->mode = MCRYPT_MODE_CBC;
}
}
/*
* 加密數(shù)據(jù)并返回
*/
public function encrypt($data)
{
$data = base64_encode(mcrypt_encrypt($this->cipher, $this->key, $data, $this->mode, $this->iv));
return $data;
}
/*
* 解密數(shù)據(jù)并返回
*/
public function decrypt($data)
{
$data = mcrypt_decrypt($this->cipher, $this->key, base64_decode($data), $this->mode, $this->iv);
$data = rtrim(rtrim($data), "\x00..\x1F");
return $data;
}
}
iOS端代碼實(shí)現(xiàn)
// NSString+AESSecurity.h
@interface NSString (AESSecurity)
+ (NSString *)encrypyAES:(NSString *)content key:(NSString *)key;
+ (NSString *)descryptAES:(NSString *)content key:(NSString *)key;
@end
// NSString+AESSecurity.m
#import "NSString+AESSecurity.h"
#import <CommonCrypto/CommonCrypto.h>
// 初始向量
NSString *const kInitVector = @"0123456789";
// 密鑰長(zhǎng)度
size_t const kKeySize = kCCKeySizeAES128;
@implementation NSString (AESSecurity)
+ (NSString *)encrypyAES:(NSString *)content key:(NSString *)key {
NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = contentData.length;
// 為結(jié)束符'\\0' +1
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t encryptSize = dataLength + kCCBlockSizeAES128;
void *encryptedBytes = malloc(encryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding | kCCOptionECBMode, // 加密模式
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
encryptedBytes,
encryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
// 對(duì)加密后的數(shù)據(jù)進(jìn)行 base64 編碼
return [[NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
free(encryptedBytes);
return nil;
}
+ (NSString *)descryptAES:(NSString *)content key:(NSString *)key {//
// 把 base64 String 轉(zhuǎn)換成 NSData
NSData *contentData = [[NSData alloc] initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSUInteger dataLength = contentData.length;
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t decryptSize = dataLength + kCCBlockSizeAES128;
void *decryptedBytes = malloc(decryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding | kCCOptionECBMode, // 加密模式
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
decryptedBytes,
decryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
return [[NSString alloc] initWithData:[NSData dataWithBytesNoCopy:decryptedBytes length:actualOutSize] encoding:NSUTF8StringEncoding];
}
free(decryptedBytes);
return nil;
}
@end
注意點(diǎn)
在iOS上芹缔,字符串經(jīng)過(guò)加解密后可能會(huì)在數(shù)據(jù)中添加一些操作符這會(huì)導(dǎo)致我們想進(jìn)一步處理解密后的字符串時(shí)會(huì)處理失敗,例如瓶盛,當(dāng)我們想將解密后的json字符串轉(zhuǎn)成字典時(shí)最欠,可能會(huì)拋出Garbage at End
的錯(cuò)誤,解決方案如下:
- 將字符串中的所有控制符替換成空字符
NSString *newStr = [oldStr stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
- 將處理后的字符串進(jìn)行json序列化操作
NSError *err = nil;
NSData *jsondata = [str dataUsingEncoding:NSUTF8StringEncoding];
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsondata options:NSJSONReadingMutableLeaves error:&err];
附上相關(guān)模塊的代碼
歡迎star和fork~
歡迎訪問(wèn)我的個(gè)人博客