一、对称加密

1、什么是对称加密?

对称加密就是指,加密和解密使用同一个密钥的加密方式。

2、对称加密的工作过程

发送方使用密钥将明文数据加密成密文,然后发送出去,接收方收到密文后,使用同一个密钥将密文解密成明文读取。

3、对称加密的优点

加密计算量小、速度块,适合对大量数据进行加密的场景。(记住这个特点,实际使用是会用到的)

4、对称加密的两大不足

  • 密钥传输问题:如上所说,由于对称加密的加密和解密使用的是同一个密钥,所以对称加密的安全性就不仅仅取决于加密算法本身的强度,更取决于密钥是否被安全的保管,因此加密者如何把密钥安全的传递到解密者手里,就成了对称加密面临的关键问题。(比如,我们客户端肯定不能直接存储对称加密的密钥,因为被反编译之后,密钥就泄露了,数据安全性就得不到保障,所以实际中我们一般都是客户端向服务端请求对称加密的密钥,而且密钥还得用非对称加密加密后再传输。)
  • 密钥管理问题:再者随着密钥数量的增多,密钥的管理问题会逐渐显现出来。比如我们在加密用户的信息时,不可能所有用户都用同一个密钥加密解密吧,这样的话,一旦密钥泄漏,就相当于泄露了所有用户的信息,因此需要为每一个用户单独的生成一个密钥并且管理,这样密钥管理的代价也会非常大。

二、AES加密算法

1、什么是AES加密算法及AES加密算法的形成过程

(1)什么是AES加密算法

上一小节说到了对称加密,那么针对对称加密的话,是有很多的对称加密算法的,如DES加密算法、3DES加密算法等,但是因为AES加密算法的安全性要高于DES和3DES,所以AES已经成为了主要的对称加密算法,因此本篇主要学习一下AES加密算法。

AES加密算法就是众多对称加密算法中的一种,它的英文全称是Advanced Encryption Standard,翻译过来是高级加密标准,它是用来替代之前的DES加密算法的。

AES加密算法采用分组密码体制,每个分组数据的长度为128位16个字节,密钥长度可以是128位16个字节、192位或256位,一共有四种加密模式,我们通常采用需要初始向量IV的CBC模式,初始向量的长度也是128位16个字节。

2)AES加密算法的形成过程

  • 没人理,呜呜呜,只要有一个人听,我就讲~

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWNkZDdmMTNmOTQ0NDEzNzQucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

我每天要处理很多很多的数据,我要把很多神奇的高机密数据加密成枯燥的数据包发给你的WIFI路由器,这一切都是我做的!

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWI3ZTA5M2UzY2ViN2ZiNzIucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

但是,一直以来似乎没有人对我和我的故事感兴趣,呜呜呜.....

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWMyZjg1MzkxNjI3NWUwZGUucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

可其实我的故事要比《灰姑娘》更精彩呢,因为我现在可是分组加密世界的国王。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTg3ZDljN2ZjZjRlYzU0ZjgucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

哇,你还没走啊,你是想要听我的故事吗?好吧,让我们荡起双桨......

  • 变法风云吹进门,DES称霸武林

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWMxMDYwODE5OThlM2IxZGEucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

在1975以前,加密界一片混乱,没有人能说出哪种加密算法更好,诸侯争霸,涂炭生灵。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWUxYmM5YTY0M2MxYjYzZWEucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

终于有一天,国家标准局出台了一个活动,说是要号召大家来创建一个好的安全的加密算法,就是选盟主呗。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWQ2OWRiYmY1M2U0MjZmMTgucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

这时一个强有力的竞争者站了出来,它就是IBM,IBM研究出了一套加密算法。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTdhM2JlMTE2NjZlM2UzNzYucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

IBM研究的这一套加密算法在被国家安全局修修改改之后,被国家标准局钦定为了数据加密标准,即DES,因为DES的key更短(key即密钥),S盒更强(S盒是对称加密进行置换计算的基本结构,S盒的强度直接决定了该对称加密算法的好坏)

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTU2YmQ4YTg4ZjE3YzZhMTEucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

DES统治了对称加密世界近20年,学术界也开始认真的研究它,这是第一次,真正的研究它,现代密码学就此诞生。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTI0NGQ5YzA4YmMwMTVlYzgucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

这些年间,许多攻击者挑战着DES,它经历了风风雨雨,而且它真得被打败过几次。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTk1NmJmNGNiMTY0ZGIyMzcucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

而阻止攻击者的唯一办法就是使用DES加密算法3次,即“Triple-DES”或“3DES”,这种办法可行,但是实在是太慢了。

  • 改革春风吹得响,AES要登场

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTRlMjlkY2JlN2JlZGUxMTQucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

终于,国家标准学会憋不住了,又出台了一个活动,说是号召大家推举一个新的武林盟主,盟主3DES虽然武功厉害,但毕竟太老太慢了。新盟主的条件是武功(加密强度)至少和3DES一样厉害,但是要比3DES快而灵巧。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LWM4OWEwMmFmYTA5Y2RhZmYucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

来自五湖四海的朋友们都跃跃欲试。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTg4MGJiZTVhNTAyNDgzZWIucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

我的创造者,Vincent Rijmen和Joan Daemen也在他们之中。他俩把他们俩的名字组合起来,给我起了个名叫Rijndael。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTBkMTQ5ZWViMDE1Y2I5NjYucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

经过好几轮的pk,大家伙一起投票!

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTJiMDFhYWE0YTljYWUyZDIucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

最后,我赢了!哦,赢喽,湖人总冠军喽,冠军喽!

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTZjMTEwOGEwZmZiMGI0ZmQucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

好了,现在我--AES王朝的序幕已然拉开,跪服吧,我的子民!普天之下,莫非王土;率土之滨,莫非王臣。强如英特尔也不得不向我朝来贺,甚至专门为我量身定制了底层的指令,来让我飞的更高!

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTk1MGIyMjRhMGZjYmExNDIucG5nP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy8xMDAwL2Zvcm1hdC93ZWJw.jpg

啊,吹牛逼的感觉好爽啊!你们可有什么问题?不要让我停下来呀!

A:老师老师,你牛逼吹得这么响,你倒是说说你--AES加密的原理是什么啊!

B:我靠,又要开始了,我走了......

欸,那位同学不要走......哎,可惜他没有机会看到我的内在了......

2、AES的加密流程

我们先让AES喝口水歇一下,了解一下它的加密流程之后,再让它给我们讲它的加密原理。

(1)五个关键词讲解
要理解AES的加密流程,会涉及到AES加密的五个关键词,分别是:分组密码体制、Padding、密钥、初始向量IV和四种加密模式,下面我们一一介绍。

  • 分组密码体制:所谓分组密码体制就是指将明文切成一段一段的来加密,然后再把一段一段的密文拼起来形成最终密文的加密方式。AES采用分组密码体制,即AES加密会首先把明文切成一段一段的,而且每段数据的长度要求必须是128位16个字节,如果最后一段不够16个字节了,就需要用Padding来把这段数据填满16个字节,然后分别对每段数据进行加密,最后再把每段加密数据拼起来形成最终的密文。
  • Padding:Padding就是用来把不满16个字节的分组数据填满16个字节用的,它有三种模式PKCS5、PKCS7和NOPADDING。PKCS5是指分组数据缺少几个字节,就在数据的末尾填充几个字节的几,比如缺少5个字节,就在末尾填充5个字节的5。PKCS7是指分组数据缺少几个字节,就在数据的末尾填充几个字节的0,比如缺少7个字节,就在末尾填充7个字节的0。NoPadding是指不需要填充,也就是说数据的发送方肯定会保证最后一段数据也正好是16个字节。那如果在PKCS5模式下,最后一段数据的内容刚好就是16个16怎么办?那解密端就不知道这一段数据到底是有效数据还是填充数据了,因此对于这种情况,PKCS5模式会自动帮我们在最后一段数据后再添加16个字节的数据,而且填充数据也是16个16,这样解密段就能知道谁是有效数据谁是填充数据了。PKCS7最后一段数据的内容是16个0,也是同样的道理。解密端需要使用和加密端同样的Padding模式,才能准确的识别有效数据和填充数据。我们开发通常采用PKCS7 Padding模式。
  • 初始向量IV:初始向量IV的作用是使加密更加安全可靠,我们使用AES加密时需要主动提供初始向量,而且只需要提供一个初始向量就够了,后面每段数据的加密向量都是前面一段的密文。初始向量IV的长度规定为128位16个字节,初始向量的来源为随机生成。至于为什么初始向量能使加密更安全可靠,会在下面的加密模式中提到。
  • 密钥:AES要求密钥的长度可以是128位16个字节、192位或者256位,位数越高,加密强度自然越大,但是加密的效率自然会低一些,因此要做好衡量。我们开发通常采用128位16个字节的密钥,我们使用AES加密时需要主动提供密钥,而且只需要提供一个密钥就够了,每段数据加密使用的都是这一个密钥,密钥来源为随机生成。(注意:后面我们在谈到AES的加密原理时,会提到一个初始密钥,那个初始密钥指的就是这里我们提供的这个密钥。)
  • 四种加密模式:AES一共有四种加密模式,分别是ECB(电子密码本模式)、CBC(密码分组链接模式)、CFB、OFB,我们一般使用的是CBC模式。四种模式中除了ECB相对不安全之外,其它三种模式的区别并没有那么大,因此这里只会对ECB和CBC模式做一下简单的对比,看看它们在做什么。
  • ECB(电子密码本模式)

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTYwZjU2YTU1OTI2NGY3YTcuanBnP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy83ODIvZm9ybWF0L3dlYnA.jpg

ECB模式是最基本的加密模式,即仅仅使用明文和密钥来加密数据,相同的明文块会被加密成相同的密文块,这样明文和密文的结构将是完全一样的,就会更容易被破解,相对来说不是那么安全,因此很少使用。

  • CBC(密码分组链接模式)

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTc2ZjMwOGIwYmRiYWE3MWQuanBnP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy81ODMvZm9ybWF0L3dlYnA.jpg

而CBC模式则比ECB模式多了一个初始向量IV,加密的时候,第一个明文块会首先和初始向量IV做异或操作,然后再经过密钥加密,然后第一个密文块又会作为第二个明文块的加密向量来异或,依次类推下去,这样相同的明文块加密出的密文块就是不同的,明文的结构和密文的结构也将是不同的,因此更加安全,我们常用的就是CBC加密模式。

  • CFB、OFB:略。

(2)AES的加密流程

经过了(1)五个关键词的讲解,我们可以先说一句话:我们通常使用AES加密,会采用128位16个字节的密钥和CBC加密模式。

因此,我们这里要说的加密流程就是针对这种情况的。让我们再把CBC加密模式的图托到这里,重新整理一下AES的加密流程:

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTE2NzI1LTYwMzBkMTFhYWZlZDg5YzUuanBnP2ltYWdlTW9ncjIvYXV0by1vcmllbnQvc3RyaXAlN0NpbWFnZVZpZXcyLzIvdy81ODMvZm9ybWF0L3dlYnA.jpg

  • 首先AES加密会把明文按128位16个字节,切成一段一段的数据,如果数据的最后一段不够16个字节,会用Padding来填充。
  • 然后把明文块0与初始向量IV做异或操作,再用密钥加密,得到密文块0,同时密文块0也会被用作明文块1的加密向量。
  • 然后明文块1与密文块0进行异或操作,再用密钥加密,得到密文块1。(当然这里只是假设数据只有两段,如果数据不止两段,依次类推,就可以得到很多段密文块。)
  • 最后把密文块拼接起来就能得到最终的密文。

3、AES的加密原理

我们探讨AES的加密原理,其实就是探讨上图中加密器里面做了什么。那加密器里面做了什么呢?这里不妨先透漏一下,加密器里其实做了四个重要的操作,分别是:密钥扩展、初始轮、重复轮和最终轮。AES的加密原理其实可以说,就是这四个操作。
详细请查看https://blog.csdn.net/qq_37141773/article/details/97107255

4、AES加密的代码

AES128Encrypt

//  AES128Encrypt.m
//  EncryptDemo
//
//  Created by 意一yiyi on 2018/5/31.
//  Copyright © 2018年 意一yiyi. All rights reserved.
//
 
#import "AES128Encrypt.h"
#import <CommonCrypto/CommonCryptor.h>
 
@implementation AES128Encrypt
 
/**
 * AES128加密,Base64编码输出
 *
 * @param plainText 明文
 * @param secretKey 密钥
 *
 * @return AES128加密后的密文
 */
+ (NSString *)aes128CiphertextFromPlainText:(NSString *)plainText secretKey:(NSString *)secretKey iv:(NSString *)iv {
    
    char keyPtr[kCCKeySizeAES128 + 1];
    memset(keyPtr, 0, sizeof(keyPtr));
    [secretKey getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCBlockSizeAES128 + 1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSData *data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = [data length];
    
    int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
    NSUInteger newSize = 0;
    
    if(diff > 0) {
        
        newSize = 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);
    memset(buffer, 0, bufferSize);
    
    size_t numBytesCrypted = 0;
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,// 加密
                                          kCCAlgorithmAES128,// AES128加密
                                          kCCOptionPKCS7Padding,// PKCS7 Padding模式
                                          keyPtr,// 密钥
                                          kCCKeySizeAES128,// 密钥长度
                                          ivPtr,// 初始向量
                                          dataPtr,
                                          sizeof(dataPtr),
                                          buffer,
                                          bufferSize,
                                          &numBytesCrypted);
    if (cryptStatus == kCCSuccess) {
        
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
        // 转换成Base64并返回
        return [resultData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    }
    free(buffer);
    return nil;
}
 
/**
 * AES128解密,Base64编码输入
 *
 * @param ciphertext 密文
 * @param secretKey 密钥
 * @param iv 初始向量
 *
 * @return AES128解密后的明文
 */
+ (NSString *)aes128PlainTextFromCiphertext:(NSString *)ciphertext secretKey:(NSString *)secretKey iv:(NSString *)iv {
    
    char keyPtr[kCCKeySizeAES128 + 1];
    memset(keyPtr, 0, sizeof(keyPtr));
    [secretKey getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCBlockSizeAES128 + 1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSData *data = [[NSData alloc] initWithBase64EncodedData:[ciphertext dataUsingEncoding:NSUTF8StringEncoding] options:NSDataBase64DecodingIgnoreUnknownCharacters];
    NSUInteger dataLength = [data length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    
    size_t numBytesCrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,// 解密
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [data bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesCrypted);
    if (cryptStatus == kCCSuccess) {
        
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
        // 转换成普通字符串并返回
        return [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
    }
    free(buffer);
    return nil;
}
 
@end

AES128Encrypt

//  AES128Encrypt.m
//  EncryptDemo
//
//  Created by 意一yiyi on 2018/5/31.
//  Copyright © 2018年 意一yiyi. All rights reserved.
//
 
#import "AES128Encrypt.h"
#import <CommonCrypto/CommonCryptor.h>
 
@implementation AES128Encrypt
 
/**
 * AES128加密,Base64编码输出
 *
 * @param plainText 明文
 * @param secretKey 密钥
 *
 * @return AES128加密后的密文
 */
+ (NSString *)aes128CiphertextFromPlainText:(NSString *)plainText secretKey:(NSString *)secretKey iv:(NSString *)iv {
    
    char keyPtr[kCCKeySizeAES128 + 1];
    memset(keyPtr, 0, sizeof(keyPtr));
    [secretKey getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCBlockSizeAES128 + 1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSData *data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = [data length];
    
    int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
    NSUInteger newSize = 0;
    
    if(diff > 0) {
        
        newSize = 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);
    memset(buffer, 0, bufferSize);
    
    size_t numBytesCrypted = 0;
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,// 加密
                                          kCCAlgorithmAES128,// AES128加密
                                          kCCOptionPKCS7Padding,// PKCS7 Padding模式
                                          keyPtr,// 密钥
                                          kCCKeySizeAES128,// 密钥长度
                                          ivPtr,// 初始向量
                                          dataPtr,
                                          sizeof(dataPtr),
                                          buffer,
                                          bufferSize,
                                          &numBytesCrypted);
    if (cryptStatus == kCCSuccess) {
        
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
        // 转换成Base64并返回
        return [resultData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    }
    free(buffer);
    return nil;
}
 
/**
 * AES128解密,Base64编码输入
 *
 * @param ciphertext 密文
 * @param secretKey 密钥
 * @param iv 初始向量
 *
 * @return AES128解密后的明文
 */
+ (NSString *)aes128PlainTextFromCiphertext:(NSString *)ciphertext secretKey:(NSString *)secretKey iv:(NSString *)iv {
    
    char keyPtr[kCCKeySizeAES128 + 1];
    memset(keyPtr, 0, sizeof(keyPtr));
    [secretKey getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCBlockSizeAES128 + 1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSData *data = [[NSData alloc] initWithBase64EncodedData:[ciphertext dataUsingEncoding:NSUTF8StringEncoding] options:NSDataBase64DecodingIgnoreUnknownCharacters];
    NSUInteger dataLength = [data length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    
    size_t numBytesCrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,// 解密
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [data bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesCrypted);
    if (cryptStatus == kCCSuccess) {
        
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
        // 转换成普通字符串并返回
        return [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
    }
    free(buffer);
    return nil;
}
 
@end

5、实际开发中使用AES加密需要注意的地方

  • 服务端和我们客户端必须使用一样的密钥和初始向量IV。
  • 服务端和我们客户端必须使用一样的加密模式。
  • 服务端和我们客户端必须使用一样的Padding模式。

以上三条有一个不满足,双方就无法完成互相加解密。

同时针对对称加密密钥传输问题这个不足:我们一般采用RSA+AES加密相结合的方式,用AES加密数据,而用RSA加密AES的密钥。同时密钥和IV可以随机生成,这要是128位16个字节就行,但是必须由服务端来生成,因为如果由我们客户端生成的话,就好比我们客户端存放了非对称加密的私钥一样,这样容易被反编译,不安全,一定要从服务端请求密钥和初始向量IV。

Last modification:December 25th, 2019 at 11:54 am
如果觉得我的文章对你有用,请随意赞赏
评论打卡也可以哦,您的鼓励是我最大的动力!