前言
在項目開發過程中,為了保證傳輸數據的安全性,我們經常要對傳輸的內容進行加密處理,以增加別人破解的成本。常用的加密算法有很多,今天我們先圍繞AES
加密算法進行一個使用總結
AES算法介紹
AES
是高級加密標準(Advanced Encryption Standard)的縮寫,在密碼學中又被稱為Rijndael加密法,如果想對AES
的背景有更多的了解可以移步到維基百科-高級加密標準
AES
加密時需要統一四個參數:
- 密鑰長度 (Key Size)
- 加密模式 (Cipher Mode)
- 填充方式 (Padding)
- 初始向量 (Initialization Vector)
由于前后端開發所使用的語言不統一,導致經常出現前后端之間互相不能解密的情況出現,其實,無論什么語言系統,AES
的算法總是相同的,導致結果不一致的原因在于上述的四個參數不一致,下面就來了解一下這四個參數的含義
密鑰長度
AES
算法下,key的長度有三種:128、192、256 bits,三種不同密鑰長度就需要我們傳入的key傳入不同長度的字符串,例如我們選擇AES-128,那我們定的key需要是長度為16的字符串
加密模式
AES
屬于塊加密,塊加密中有CBC、ECB、CTR、OFB、CFB等幾種工作模式,為了保持前后端統一,我們選擇ECB模式
填充方式
由于塊加密只能對特定長度的數據塊進行加密,因此CBC、ECB模式需要在最后一數據塊加密前進行數據填充
初始向量
使用除ECB以外的其他加密模式均需要傳入一個初始向量,其大小與Block Size相等
代碼實現
PHP端代碼實現
<?php
/*
* 定義類cryptAES 專用于AES加解密
* 初始化時傳入密鑰長度、加密Key、初始向量、加密模式四個字段
*/
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;
}
}
/*
* 加密數據并返回
*/
public function encrypt($data)
{
$data = base64_encode(mcrypt_encrypt($this->cipher, $this->key, $data, $this->mode, $this->iv));
return $data;
}
/*
* 解密數據并返回
*/
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端代碼實現
// 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";
// 密鑰長度
size_t const kKeySize = kCCKeySizeAES128;
@implementation NSString (AESSecurity)
+ (NSString *)encrypyAES:(NSString *)content key:(NSString *)key {
NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = contentData.length;
// 為結束符'\\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) {
// 對加密后的數據進行 base64 編碼
return [[NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
free(encryptedBytes);
return nil;
}
+ (NSString *)descryptAES:(NSString *)content key:(NSString *)key {//
// 把 base64 String 轉換成 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
注意點
在iOS上,字符串經過加解密后可能會在數據中添加一些操作符這會導致我們想進一步處理解密后的字符串時會處理失敗,例如,當我們想將解密后的json字符串轉成字典時,可能會拋出Garbage at End
的錯誤,解決方案如下:
- 將字符串中的所有控制符替換成空字符
NSString *newStr = [oldStr stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
- 將處理后的字符串進行json序列化操作
NSError *err = nil;
NSData *jsondata = [str dataUsingEncoding:NSUTF8StringEncoding];
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsondata options:NSJSONReadingMutableLeaves error:&err];
附上相關模塊的代碼
歡迎star和fork~
歡迎訪問我的個人博客