密码学

密码学基础(二):对称加密

2019-10-22  本文已影响0人  康小曹

什么是对称加密

加密和解密使用相同的秘钥称为对称加密。

主流的对称加密算法

DES:已经淘汰
3DES:相对于DES有所加强,但是仍然存在较大风险
AES:全新的对称加密算法。

对称加密的特点

特点决定使用场景,对称加密拥有如下特点:

快速

速度快,可用于频率很高的加密场景。

可逆

使用同一个秘钥进行加密和解密。

分组加密

可选按照128、192、256位为一组的加密方式,加密后的输出值为所选分组位数的倍数。密钥的长度不同,推荐加密轮数也不同,加密强度也更强。

例如:
AES加密结果的长度由原字符串长度决定:一个字符为1byte=4bit,一个字符串为n+1byte,因为最后一位为'\0',所以当字符串长度小于等于15时,AES128得到的16进制结果为32位,也就是324=128byte,当长度超过15时,就是64位为1282byte。

对称加密的使用场景

因为对称加密速度快的特点,对称加密被广泛运用在各种加密场所中。但是因为其需要传递秘钥,一旦秘钥被截获或者泄露,其加密就会玩完全破解,所以AES一般和RSA一起使用。

因为RSA不用传递秘钥,加密速度慢,所以一般使用RSA加密AES中锁使用的秘钥后,再传递秘钥,保证秘钥的安全。秘钥安全传递成功后,一直使用AES对会话中的信息进行加密,以此来解决AES和RSA的缺点并完美发挥两者的优点,其中相对经典的例子就是HTTPS加密,后文会专门研究。

AES加密算法的基本原理

本文针对ECB模式下的AES算法进行大概讲解,针对每一步的详细算法不再该文讨论范围内。

1、明文分组

128位的明文被分成16个字节的明文矩阵,然后将明文矩阵转化成状态矩阵,以“abcdefghijklmnop”的明文为例:


明文矩阵
2、密文分组

同样的,128位密钥被分成16组的状态矩阵。与明文不同的是,密文会以列为单位,生成最初的4x8x4=128的秘钥,也就是一个组中有4个元素,每个元素由每列中的4个秘钥叠加而成,其中矩阵中的每个秘钥为1个字节也就是8位。

生成初始的w[0]、w[1]、w[2]、w[3]原始密钥之后,通过密钥编排函数,该密钥矩阵被扩展成一个44个组成的序列W[0],W[1], … ,W[43]。该序列的前4个元素W[0],W[1],W[2],W[3]是原始密钥,用于加密运算中的初始密钥加,后面40个字分为10组,每组4个32位的字段组成,总共为128位,分别用于10轮加密运算中的轮密钥加密,如下图所示:


秘钥编排函数
3、轮密钥加

之所以把这一步单独提出来,是因为ECB和CBC模式中主要的区别就在这一步。

ECB模式中,初始秘钥扩展后生成秘钥组后(w0-w43),明文根据当前轮数取出w[i,i+3]进行加密操作。

CBC模式中,则使用前一轮的密文(明文加密之后的值)和当前的明文进行异或操作之后再进行加密操作。如图所示:

ECB和CBC
4、剩余轮操作

根据不同位数分组,官方推荐的加密轮数:


AES加密轮数

轮操作加密的第1轮到第9轮的轮函数一样,包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。


轮加密
5、按照组数对每组进行轮操作

当第一组加密完成时,后面的组循环进行加密操作知道所有的组都完成加密操作。

6、转码

一般会将结果转化成base64位,此时在iOS中应该使用base64编码的方式进行解码操作,而不是UTF-8。

Base64基本介绍

base64是一种编码方式,常用语传输8bit字节码。其编码原理如下所示:

1、取原数据

将原数据按照3个字节取为一组,即为3x8=24位

2、分组

将3x8=24的数据分为4x6=24的数据,也就是分为了4组

3、高位补0

将4个组中的数据分别在高位补上2个0,也就成了8x4=32,所以原数据增大了三分之一。

4、转码

根据base64编码表对数据进行转换,如果要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办,Base64用\x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。

举个栗子:Man最后的结果就是TWFu。


base64编码原理

为什么需要高位补0
因为base64产生的基础是避免在传输8Bit字节码数据时,不可见字符的可能产生的一些错误。因为字符是以8位为一个字节来处理,补零只是为了方便当前的处理规则,如果是6位,那么又需要做适配或者更改当前的处理规则。

base64编码的意义

计算机中所有的数据都是以0和1的二进制来存储,而所有的文字都是通过ascii表转化而来进而显示成对应的语言。但是ascii表中存在许多不可见字符,这些不可见字符在数据传输时,有可能经过不同硬件上各种类型的路由,在转义时容易发生错误,所以规定了64个可见字符(a-z、A-Z、0-9、+、/),通过base64转码之后,所有的二进制数据都是可见的。

ECB && CBC

ECB和CBC是两种加密工作模式。其相同点都是在开始轮加密之前,将明文和密文按照128/192/256进行分组。以128位为例,明文和密文都分为16组,每组1个字节为8位。

ECB工作模式中,每一组的明文和密文相互独立,每一组的明文通过对应该组的密文加密后生成密文,不影响其他组。

CBC工作模式中,后一组的明文在加密之前先使用前一组的密文进行异或运算后再和对应该组的密文进行加密操作生成密文。

为简单的分组加密。将明文和密文分成若干组后,使用密文对明文进行加密生成密文
CBC

对称加密在iOS中的使用

加密:

- (NSString *)AES128EncryptWithKey:(NSString *)key plainText:(NSString *)plainText {
    char keyPtr[kCCKeySizeAES128];
    // 初始化数组
    memset(keyPtr, 0, sizeof(keyPtr));
    
    // NSString->char[]
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    // 原文处理
    NSData *data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = [data length];
    
    // 密文输出占位
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    
    // 加密
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode, keyPtr, kCCBlockSizeAES128, NULL, [data bytes], dataLength, buffer, bufferSize, &numBytesEncrypted);
    
    if (cryptStatus == kCCSuccess) {
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        NSString *stringBase64 = [resultData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        return stringBase64;
    }
    free(buffer);
    return nil;
}

解密:

- (NSString *)AES128DecryptWithKey:(NSString *)key secret:(NSString *)secret {
    char keyPtr[kCCKeySizeAES128];
    memset(keyPtr, 0, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    //base64解码
    NSData *data = [[NSData alloc] initWithBase64EncodedString:secret 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 | kCCOptionECBMode, keyPtr, kCCBlockSizeAES128, NULL, [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;
}
欢迎关注
上一篇下一篇

猜你喜欢

热点阅读