iOS逆向攻防之HASH,数字签名,对称加密算法

在密码学中,加密部分主要分为对称加密非对称加密非对称加密主要有RSA非对称加密(使用公钥/私钥来加密解密),对称加密主要有DES/3DES/AES对称加密算法,顺带提一下咱们今天介绍的Hash算法,Hash属于一种消息摘要算法,不属于加密算法,可是因为其单向运算,不可逆性,因此Hash是加密算法中的构成部分,Hash算法主要有MD5/Sha1/Sha2,这几个只是Hash算法加密精度有所不一样。算法

那么紧接以前的非对称加密RSA,直接上此次的干货部分安全

一、Hash概述
二、数字签名
三、对称加密算法简介
四、对称加密算法终端命令
    五、对称加密算法终端演练
六、对称加密算法代码演练
七、CCCrypt函数
复制代码

1、Hash概述


一、Hash的概念bash

Hash, 通常翻译为'散列', 也有直接音译的'哈希',就是把任意长度的输入,经过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是散列值的空间一般小于输入的空间,不一样的输入可能会散列成相同的输出,因此不可能从散列值来肯定惟一的输入值。简单说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。函数

二、Hash特色工具

一、算法是公开的
二、对相同的数据运算,获得的结果是同样的
三、对于不一样的数据运算,如MD5(Hash算法的一种)的到的结果默认是128位的,32个字符(16进制标识)
四、无法逆运算(由于哈希值是映射关系,会存在散列碰撞【即:无限个数据加密获得有限个数据,就存在一个或许多个数据存在一样的哈希值】)
五、信息摘要,信息’指纹‘,通常是用来作数据识别的(因为无法作逆运算,因此通常不会用来作加密数据,只是把数据的哈希值取到,而后用来对比,作数据识别的)

复制代码

三、Hash函数(单向散列函数)测试

一、MD5(Message Digest Algorithm 5)
二、SHA(Secure Hash Algorithm)
   SHA又分为:
   SHA-1
   SHA-2系列(224,256,384,512,512/224,512/256统称为SHA-2系列)
三、MAC(Message Authentication Code)
四、CRC(Cyclic Redundancy Check)
五、SM3(国产哈希算法)

复制代码

四、Hash用途ui

一、用户密码的加密
二、搜索引擎
三、版权
四、数字签名(应用签名)

复制代码

五、HMAC搜索引擎

什么是HMAC?HMAC(Hash-based message authentication code)是一种使用Hash函数(单向散列函数)来构造消息认证码的方法,利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要做为输出。主要是为了能让人对对方身份正确性和消息有效性进行验证,与消息摘要的最大不一样,就是有签名密钥!编码

HMAC经过两次hash两个不一样的key来生成。 目前尚未发现有任何的方法来产生碰撞。atom

HMAC中所使用的单向散列函数并不只限于一种,任何高强度的Hash函数(单向散列函数)均可以被用于HMAC。 好比使用SHA-一、SHA-22四、SHA-25六、SHA-38四、SHA-512所构造的HMAC,分别称为HMAC-SHA一、HMAC-SHA-22四、HMAC-SHA-38四、HMAC-SHA-512。

2、数字签名


一、什么是数字签名

数字签名就是用于鉴别数字信息的方法;

二、数字签名

下面咱们以电商支付金额这个场景来描述数字签名的具体意义:

数字签名理解示例.png

图示中,通过RSA加密的原商品信息Hash值这个总体就叫作数字签名。

数字签名的概念.png

3、对称加密概述


一、对称加密算法定义:

对称加密方式:就是明文经过密钥加密获得密文。密文经过密钥解密获得明文。

二、对称加密常见算法

一、DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合;(题外话:实际上用的很少,由于加密强度不够)
二、3DES(Triple DES):是基于DES的对称算法,对相同的数据用3个不一样的密钥执行3次加密,强度更高;(题外话:不过由于3个密钥管理起来麻烦,因此通常不是很经常使用~一出生就挂掉了,很惨。。。)
三、RC2和RC4:用变长密钥对大量数据进行加密,比DES快哦~
四、AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高,在21世纪AES标准的一个实现是Rijndael算法;(题外话:很安全,苹果的钥匙串访问就是用的AES,美国国家安全局也是用的AES,想要暴力破解基本不可能)

复制代码

三、对称加密应用模式 对称加密主要有两种应用模式,下面来详细介绍一下

ECB(Electronic Code Book):电子密码本模式。每一块数据, 独立加密。

ECB是最基本的加密方式,也就是一般理解的加密,相同的明文将永远加密成相同的密文,无初始向量,容易受到密码本重放攻击,通常状况下不多用。

CBC(Cipher Block Chaining):密码分组连接模式。使用一个密钥和一个初始化向量(IV)对数据进行加密。

CBC加密方式,明文被加密前要与前面的密文进行异或运算后再加密,所以只要选择不一样的初始向量,相同的密文加密后会造成不一样的密文,这是目前应用最普遍的模式。CBC加密后的密文是上下文相关的,但明文的错误不会传递到后续分组,但若是一个分组丢失,后面的分组将所有做废(同步错误).

CBC能够有效的保证密文的完整性,若是一个数据块在传递时丢失或者改变,后面的数据没法进行正常的解密。

4、对称加密算法终端命令


AES对称加密算法两种应用模式下的终端命令分别以下: 一、AES(ECB)的加密与解密

AES(ECB)加密'battleMage'字符串

$ echo -n battleMage | openssl enc -aes-128-ecb -K 616263 -nosalt | base64

复制代码

AES(ECB)解密'battleMage'字符串

$ echo -n kXcE5nnetsinAMBEcK6D5g== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d

复制代码

二、AES(CBC)的加密与解密

AES(CBC)加密'battleMage'字符串

$ echo -n battleMage | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64

复制代码

AES(CBC)解密'battleMage'字符串

$ echo -n H3tn3dXCEtKNvijJYLsStw== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d

复制代码

5、对称加密算法终端演练对比


一、新建一个message.txt文本文件

$ vi message.txt

复制代码

回车进入编辑界面,点击'i',进入编辑界面,输入5排'1234567890',点击'esc',再点击'shift+:',输入'wq'回车保存。

message.txt文件.png

二、对该'message.txt'文件直接使用AES(ECB)进行加密,而后输出一个'meg1.bin'文件

$ openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out meg1.bin

复制代码

直接敲回车,获得一个 meg1.bin 的文件

而后直接修改message.txt文件,把最后一排的第一个1改为2,

AES(ECB)修改以后再次加密.png

再次使用上述命令进行加密,而后输出一个'meg2.bin'文件

$ openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out meg2.bin

复制代码

直接敲回车,获得一个 meg2.bin 的文件

接下来使用xxd命令查看meg1.bin 和 meg2.bin文件

一样经过AES(CBC)加密'message.txt'并输出一个‘meg3.bin’文件

$ openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out meg3.bin

复制代码

再次手动编辑message.txt文件,把message.txt还原,而后经过AES(CBC)加密并输出一个‘meg4.bin’文件

$ openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out meg4.bin
复制代码

对好比下图,

AES两种方式ECB和CBC的区别.png

6、对称加密算法代码演练


接下来开始代码演练部分,须要导入一个工具类,工具类代码并很少,这里直接贴工具类的内容吧,工具类头文件AES,DES各类终端命令也都包含在内了:

.h文件

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

/**
 *  终端测试指令
 *
 *  DES(ECB)加密
 *  $ echo -n hello | openssl enc -des-ecb -K 616263 -nosalt | base64
 *
 * DES(CBC)加密
 *  $ echo -n hello | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
 *
 *  AES(ECB)加密
 *  $ echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64
 *
 *  AES(CBC)加密
 *  $ echo -n hello | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
 *
 *  DES(ECB)解密
 *  $ echo -n HQr0Oij2kbo= | base64 -D | openssl enc -des-ecb -K 616263 -nosalt -d
 *
 *  DES(CBC)解密
 *  $ echo -n alvrvb3Gz88= | base64 -D | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -d
 *
 *  AES(ECB)解密
 *  $ echo -n d1QG4T2tivoi0Kiu3NEmZQ== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d
 *
 *  AES(CBC)解密
 *  $ echo -n u3W/N816uzFpcg6pZ+kbdg== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d
 *
 *  提示:
 *      1> 加密过程是先加密,再base64编码
 *      2> 解密过程是先base64解码,再解密
 */
@interface EncryptionTools : NSObject
    
+ (instancetype)sharedEncryptionTools;
    
    /**
     @constant   kCCAlgorithmAES     高级加密标准,128位(默认)
     @constant   kCCAlgorithmDES     数据加密标准
     */
    @property (nonatomic, assign) uint32_t algorithm;
    
    /**
     *  加密字符串并返回base64编码字符串
     *
     *  @param string    要加密的字符串
     *  @param keyString 加密密钥
     *  @param iv        初始化向量(8个字节)
     *
     *  @return 返回加密后的base64编码字符串
     */
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;
    
    /**
     *  解密字符串
     *
     *  @param string    加密并base64编码后的字符串
     *  @param keyString 解密密钥
     *  @param iv        初始化向量(8个字节)
     *
     *  @return 返回解密后的字符串
     */
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;
    
    @end


复制代码

.m文件

#import "EncryptionTools.h"

@interface EncryptionTools()
    @property (nonatomic, assign) int keySize;
    @property (nonatomic, assign) int blockSize;
    @end

@implementation EncryptionTools
    
+ (instancetype)sharedEncryptionTools {
    static EncryptionTools *instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
        instance.algorithm = kCCAlgorithmAES;
    });
    
    return instance;
}
    
- (void)setAlgorithm:(uint32_t)algorithm {
    _algorithm = algorithm;
    switch (algorithm) {
        case kCCAlgorithmAES:
        self.keySize = kCCKeySizeAES128;
        self.blockSize = kCCBlockSizeAES128;
        break;
        case kCCAlgorithmDES:
        self.keySize = kCCKeySizeDES;
        self.blockSize = kCCBlockSizeDES;
        break;
        default:
        break;
    }
}
    
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 设置秘钥
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    
    // 设置iv
    uint8_t cIv[self.blockSize];
    bzero(cIv, self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 设置输出缓冲区
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 开始加密
    size_t encryptedSize = 0;
    //加密解密都是它 -- CCCrypt
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          self.algorithm,
                                          option,
                                          cKey,
                                          self.keySize,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &encryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
    } else {
        free(buffer);
        NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus);
    }
    
    return [result base64EncodedStringWithOptions:0];
}
    
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 设置秘钥
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    
    // 设置iv
    uint8_t cIv[self.blockSize];
    bzero(cIv, self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 设置输出缓冲区
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 开始解密
    size_t decryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          self.algorithm,
                                          option,
                                          cKey,
                                          self.keySize,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &decryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
        NSLog(@"[错误] 解密失败|状态编码: %d", cryptStatus);
    }
    
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
    
    @end

复制代码

接下来新建工程,把工具类.h,.m拖入工程,在ViewController.m实现touchBegin方法

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSString * key = @"abc";
    uint8_t iv[8] = {1,2,3,4,5,6,7,8};
    
    //备注:选择的是AES(ECB), 初始向量iv直接传nil,若是选择 AES(CBC),初始向量iv须要传值,具体可见工具类.m文件中实现,是判断iv是否为nil来选取加密方式的
    //一、选择AES(ECB)
    NSString * encStr =  [[EncryptionTools sharedEncryptionTools]encryptString:@"hello" keyString:key iv:nil];
    
    NSLog(@"AES(ECB)加密的结果是:%@", encStr);
    NSLog(@"AES(ECB)解密的结果是:%@", [[EncryptionTools sharedEncryptionTools]decryptString:encStr keyString:key iv:nil]);
    
    //二、选择AES(CBC)
    NSData * ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
    NSString * encStr1 =  [[EncryptionTools sharedEncryptionTools]encryptString:@"hello" keyString:key iv:ivData];
    
    NSLog(@"AES(CBC)加密的结果是:%@", encStr1);
    NSLog(@"AES(CBC)解密的结果是:%@", [[EncryptionTools sharedEncryptionTools]decryptString:encStr1 keyString:key iv:ivData]);
    
}


@end


复制代码

点击运行,运行结果OK

2019-10-12 21:57:26.858675+0800 CryptDemo[1790:115503] AES(ECB)加密的结果是:d1QG4T2tivoi0Kiu3NEmZQ==
2019-10-12 21:57:26.858896+0800 CryptDemo[1790:115503] AES(ECB)解密的结果是:hello
2019-10-12 21:57:26.859040+0800 CryptDemo[1790:115503] AES(CBC)加密的结果是:u3W/N816uzFpcg6pZ+kbdg==
2019-10-12 21:57:26.859194+0800 CryptDemo[1790:115503] AES(CBC)解密的结果是:hello

复制代码

7、CCCrypt函数


第六步已经使用过封装好的加密工具类EncryptionTools.h,这个工具类只是封装了CCCrypt函数,下面咱们来研究一下加密工具的核心函数CCCrypt函数:

使用CCCrypt函数,须要引入系统库

#import <CommonCrypto/CommonCrypto.h>

复制代码

无论是加密仍是解密都是使用这个函数,下面咱们来介绍一下这个函数中的参数,参数解释我直接备注在API的后面,注意里面有坑!!!

CCCrypt函数

参数介绍
一、 CCOperation op :操做类型:加密or解密,枚举值;
    kCCEncrypt 表明加密
    kCCDecrypt 表明解密

二、 CCAlgorithm alg:加密算法,枚举值;
    kCCAlgorithmAES     高级加密标准,128位(默认)
    kCCAlgorithmDES     数据加密标准
    
三、 CCOptions options:加密应用模式,枚举值;
	注意注意!!!!!!!!!!这里有个坑;kCCOptionPKCS7Padding表明填充模式,这个options必须加上填充模式;
    CCCrypt的option默认是CBC,因此只须要补充一个填充模式就能表明CBC; 可是ECB就须要额外再加上一个kCCOptionECBMode,因此选择ECB就须要kCCOptionPKCS7Padding | kCCOptionECBMode;
    
    因此想要选择CBC和ECB,须要按下面进行填写!
	kCCOptionPKCS7Padding;  表明CBC
	kCCOptionPKCS7Padding | kCCOptionECBMode; 表明ECB

四、 const void *key :加密的密钥的指针
五、 size_t keyLength:密钥的长度
六、 const void *iv: 初始化向量
七、 const void *dataIn:加密的原始数据
八、 size_t dataInLength:加密的原始数据的长度
九、 void *dataOut:加密后密文的内存地址
十、size_t dataOutAvailable:加密后密文的缓冲区大小
十一、size_t *dataOutMoved :加密结果的大小


CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. */
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector */
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved)


复制代码

须要注意的是,直接使用这个函数安全隐患很是的大!由于这个函数是系统提供的,无论你是加密仍是解密,都是调用了CCCrypt函数,而黑客能够经过越狱手机附加调试或者是非越狱手机重签调试,可以用函数断点断到你的CCCrypt函数,而后经过寄存器直接获取函数的对应参数,根据上面函数的介绍,咱们的数据实际上是其中的第七个参数'const void *dataIn', 第七个参数的下标为6, 而后调用汇编指令'x6',就能够拿到你的数据的地址,而后转一下类型,就能直接打印出你的加密数据!具体操做以下:

一、仍是打开以前的工程,设置函数断点CCCrypt,而后使用真机运行!!!必须用真机,由于真机和模拟器的CPU不同

一、符号断点设置为CCCrypt.png

二、运行工程,模拟黑客调试,而后点击屏幕出发touchBegin方法,而后断点停在了CCCrypt函数的地方

三、由于函数在调用的时候,都是存在CPU的寄存器上,输入寄存器查看指令

register read x6

复制代码

read x6是读取该函数对应的第7个参数,第一个参数是x0

四、拿到地址,而后强转类型,蒙圈了吧,你的数据就泄漏了

二、寄存器读取加密的数据.png

因此这个函数不能直接使用,如今只说基础,后面会详细说安全防御~今天就说到这里了~

相关文章
相关标签/搜索