6.ios-对称加密

566 阅读6分钟

常见算法

  • DES 数据加密标准(用得少,因为强度不够)

  • 3DES 使用3个密钥,对相同的数据执行3次加密,强度增强

  • AES 高级密码标准。

应用模式

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

    • 最基本的加密模式,也就是通常理解的加密,相同的明文将永远加密成相同的密文,无初始向量,容易受到密码本重放攻击,一般情况下很少用。
  • CBC(Cipher Block Chaining):密码分组链接模式。使用一个密钥和一个初始化向量[IV]对数据执行加密。

    • 明文被加密前要与前面的密文进行异或运算后再加密,因此只要选择不同的初始向量,相同的密文加密后会形成不同的密文,这是目前应用最广泛的模式。CBC加密后的密文是上下文相关的,但明文的错误不会传递到后续分组,但如果一个分组丢失,后面的分组将全部作废(同步错误)。

    • CBC可以有效的保证密文的完整性,如果一个数据块在传递是丢失或改变,后面的数据将无法正常解密。

ECB实操演练

  • 创建一个文件夹
  • 文件夹内创建一个123.txt image.png
  • 打开终端 进入文件夹
  • 输入指令 : openssl enc -des-ecb -K 616213 -nosalt -in 123.txt -out msg1.bin
  • 创建了一个加密的二进制文件 image.png
  • 指令:cat msg1.bin 无法查看
  • 指令:xxd msg1.bin 可以查看
  • 查看重复的数据 image.png
  • 数据还是有重复的 为了方便验证可以将数据写少一些 image.png
  • 修改其中一小段内容在查看变化生成一个msg2.bin 查看两个.bin文件的差别 image.png
  • 差别 image.png 结论: ECB(Electronic Code Book):电子密码本模式。每一块数据,独立加密。

DES是一种block(块)密文的加密算法,是把数据加密成多大的块?()
A、32位
B、64位
C、128位
D、256位 参考答案:B

  • 64位 8位为一个字节 为8个字节 为一个单位

CBC实操演练

  • 指令 openssl enc -des-cbc -K 616213 -iv 0102030405060708 -nosalt -in 123.txt -out msg1.bin
  • 修改一部分数据后再生成一个msg2.bin
  • iv 随机值。 image.png
  • 查看差别
  • 从这里开始不一致后下面的全都不一致了。 image.png

指令合集

  • 提示:
  •  1> 加密过程是先加密,再base64编码
    
  •  2> 解密过程是先base64解码,再解密
    

加密

  • DES
    • 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
    • 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
    • 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

代码演练

------- EncryptionTools.h ----

//
//  EncryptionTools.h
//  EncryptDemo
//
//  Created by H on 2018/10/16.
//  Copyright © 2018年 hank. All rights reserved.


#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

------- EncryptionTools.m -----------

//
//  EncryptionTools.m
//  EncryptDemo
//
//  Created by H on 2018/10/16.
//  Copyright © 2018年 hank. All rights reserved.
//


#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 {
        /**
         kCCOptionECBMode                          ECB 的模式
         kCCOptionPKCS7Padding                     CBC 的加密
         */
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 设置输出缓冲区
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 开始加密
    size_t encryptedSize = 0;
    //加密解密都是它 -- CCCrypt
    
    /**
     1.kCCEncrypt 加密/kCCDecrypt解密
     2.加密算法
     3.加密选项  ECB/CBC
     4.KEY 的地址
     5.KEY 的长度
     6.iv 初始化向量
     7.加密的数据(地址)
     8.加密的数据长度
     9.密文的内存地址
     10.密文缓冲区的大小
     11.加密结果大小
     */
    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

  • 操作
- (void)viewDidLoad {
    [super viewDidLoad];
    
    [EncryptionTools sharedEncryptionTools].algorithm = kCCAlgorithmDES;
    
    /** AES - ECB加密 */
    NSString * key = @"abc";
    NSString * encStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:nil];
    
    
    //AES - CBC 加密
    uint8_t iv[8] = {1,2,3,4,5,6,7,8};
    NSData * ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
    
    
    NSLog(@"加密的结果是%@",[[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:ivData]);
    
    NSLog(@"解密的结果是%@",[[EncryptionTools sharedEncryptionTools] decryptString:@"u3W/N816uzFpcg6pZ+kbdg==" keyString:key iv:ivData]);
    
}


616263 对应 abc的 ASCII 码

image.png