PHP與iOS之間的AES加解密

前言

在項(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ò)誤,解決方案如下:

  1. 將字符串中的所有控制符替換成空字符
NSString *newStr = [oldStr stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
  1. 將處理后的字符串進(jìn)行json序列化操作
NSError *err = nil;
            
NSData *jsondata = [str dataUsingEncoding:NSUTF8StringEncoding];
            
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsondata options:NSJSONReadingMutableLeaves error:&err];

附上相關(guān)模塊的代碼

AESSecurity-PHP

AESSecurity-iOS

歡迎star和fork~


歡迎訪問(wèn)我的個(gè)人博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惩猫,一起剝皮案震驚了整個(gè)濱河市芝硬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌轧房,老刑警劉巖拌阴,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奶镶,居然都是意外死亡迟赃,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)厂镇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纤壁,“玉大人,你說(shuō)我怎么就攤上這事捺信∽妹剑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵迄靠,是天一觀的道長(zhǎng)秒咨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)掌挚,這世上最難降的妖魔是什么拭荤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮疫诽,結(jié)果婚禮上舅世,老公的妹妹穿的比我還像新娘旦委。我一直安慰自己,他們只是感情好雏亚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布缨硝。 她就那樣靜靜地躺著,像睡著了一般罢低。 火紅的嫁衣襯著肌膚如雪查辩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天网持,我揣著相機(jī)與錄音宜岛,去河邊找鬼。 笑死功舀,一個(gè)胖子當(dāng)著我的面吹牛萍倡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辟汰,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼列敲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了帖汞?” 一聲冷哼從身側(cè)響起戴而,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翩蘸,沒(méi)想到半個(gè)月后所意,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡催首,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年扶踊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翅帜。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖命满,靈堂內(nèi)的尸體忽然破棺而出涝滴,到底是詐尸還是另有隱情,我是刑警寧澤胶台,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布歼疮,位于F島的核電站,受9級(jí)特大地震影響诈唬,放射性物質(zhì)發(fā)生泄漏韩脏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一铸磅、第九天 我趴在偏房一處隱蔽的房頂上張望赡矢。 院中可真熱鬧杭朱,春花似錦、人聲如沸吹散。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)空民。三九已至刃唐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間界轩,已是汗流浹背画饥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浊猾,地道東北人抖甘。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像与殃,于是被迫代替她去往敵國(guó)和親单山。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容