08-密码学(终)

837 阅读8分钟

前言

本篇文章讲解密码算法中的对称加密,也是很常用的一个重要算法策略,希望大家掌握!

一、对称加密

对称加密 👉 明文通过密钥加密得到密文密文通过密钥解密得到明文

1.1 常见算法

常用的对称加密算法有👇

  1. DES 👉 数据加密标准(用得少,因为强度不够)。
  2. 3DES 👉 使用3个密钥,对相同的数据执行3次加密,强度增强。是通过数量级来增加强度,有3个密钥难保管,没有大量使用。
  3. AES 👉 高级密码标准。苹果的钥匙串访问就是使用AES

1.2 应用模式

对称加密算法有2种应用模式:

  1. ECB(Electronic Code Book)电子密码本模式。
  2. CBC(Cipher Block Chaining)密码分组链接模式。

1.2.1 ECB

  • 最基本的加密模式,也就是通常理解的加密,相同的明文永远加密相同的密文,无初始向量,容易受到密码本重放攻击,一般情况下很少用。
  • 独立加密,独立解密,一块丢失不影响其他块,但是如果有一块被破解其他也一样被破解了。

加密过程如下图👇

image.png

上图中红色是加密的,蓝色是未加密的。红色与蓝色互不影响。

1.2.2 CBC

使用一个密钥一个初始化向量[IV]对数据执行加密。

  • 明文被加密前 要与 前面的密文 进行异或运算后再加密,因此只要选择不同的初始向量,相同的密文加密后会形成不同的密文,这是目前应用最广泛的模式。
  • CBC加密后的密文是上下文相关的,明文的错误不会传递到后续分组,但如果一个分组丢失后面的分组将全部作废(同步错误)
  • CBC可以有效的保证密文的完整性,如果一个数据块在传递是丢失或改变,后面的数据将无法正常解密。

总之,CBC模式下,加密和解密,都是后面的数据依赖前面的数据,这样,即使一块被破解其他块不一定被破解。如下图👇

image.png

二、终端演示

2.1 ECB演示

首先创建一个文本文件,写入

0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222

image.png

然后加密👇

//openssl默认会加盐
openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out message.bin

image.png

xxd表示是二进制格式查看

接着修改内容👇

0000000000
1111111111 
8822222222
0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222

再次加密查看👇

image.png

与第一次加密的相比,发现只有红框处的有变动,共16字节,其他数据完全相同。 那么问题来了,ECB模式加密算法下,分块变动的最小数据是多少呢?

  • 只改一个数字看结果👇
0000000000
1111111111 
8222222222
0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222

image.png

这次变动4个字节

  • 只修改一个二进制位👇
1000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222

image.png

经过这一些列的变动,可见,是以8字节为单位进行加密的。只影响修改的数据,前后都不影响

2.2 CBC演示

加密命令👇 openssl enc -des-cbc -K 616263 -iv 0102030405060708 -nosalt -in message.txt -out message.bin 和ECB一样,首先对原始文本加密👇

0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222

修改文本再加密👇

0000000000
1111111111 
8822222222
0000000000
1111111111 
2222222222
0000000000
1111111111 
2222222222

image.png

2.3 其它命令

2.3.1 DES

  • DES算法下的 ECB模式
// 加密
echo -n LGPerson | openssl enc -des-ecb -K 616263 -nosalt | base64
// 结果
n1dRLbUQF6GZBAZUCi5cXQ==
// 解密
echo -n n1dRLbUQF6GZBAZUCi5cXQ== | base64 -D | openssl enc -des-ecb -K 616263 -nosalt -d
// 结果
LGPerson%  
  • DES算法下的 CBC模式
// 加密
echo -n LGPerson | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
// 结果
K5OLACWULS09wjOhrrXlLg==
// 解密
echo -n K5OLACWULS09wjOhrrXlLg== | base64 -D | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -d
// 结果
LGPerson% 

2.3.2 AES

  • AES算法下的 ECB模式
// 加密
echo -n LGPerson | openssl enc -aes-128-ecb -K 616263 -nosalt | base64
// 结果
kwTnmdXzPxV+fAD8Sh3Dkg==
// 解密
echo -n kwTnmdXzPxV+fAD8Sh3Dkg== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d
// 结果
LGPerson%  
  • AES算法下的 CBC模式
// 加密
echo -n LGPerson | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
// 结果
7f4sishVR9UeNiqq1FrQXQ==
// 解密
echo -n 7f4sishVR9UeNiqq1FrQXQ== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d
// 结果
LGPerson% 

⚠️注意:不论是DES算法还是AES算法,都有以下共同点👇

  1. 加密过程是先加密,再base64编码
  2. 解密过程是先base64解码,再解密
  3. 加密和解密都是enc,通过-d区分加密还是解密

2.4 key的转换

可以通过xxd获取key对应的二进制数据👇

image.png

上图左侧就是二进制数据,右侧就是地址对应的值。

三、代码演示

iOS通过CommonCrypto库支持。

    /*
    algorithm 控制AES  DES
    iv控制 ECB CBC
    */

    //key
    NSString *key = @"abc";
    //iv
    uint8_t iv[8] = {1,2,3,4,5,6,7,8};
    NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
    //message
    NSString *message = @"LGPerson";


    // AES算法
    // ECB 加密
    NSString *encAES_ECBResult = [[EncryptionTools sharedEncryptionTools] encryptString:message keyString:key iv:nil];
    NSLog(@"AES_ECB enc:%@",encAES_ECBResult);
    // ECB 解密
    NSString *decAES_ECBResult = [[EncryptionTools sharedEncryptionTools] decryptString:encAES_ECBResult keyString:key iv:nil];
    NSLog(@"AES_ECB dec:%@",decAES_ECBResult);

    // CBC 加密
    NSString *encAES_CBCResult = [[EncryptionTools sharedEncryptionTools] encryptString:message keyString:key iv:ivData];
    NSLog(@"AES_CBC enc:%@",encAES_CBCResult);
    // CBC  解密
    NSString *decAES_CBCResult = [[EncryptionTools sharedEncryptionTools] decryptString:encAES_CBCResult keyString:key iv:ivData];
    NSLog(@"AES_CBC dec:%@",decAES_CBCResult);


    //修改加密为DES
    [EncryptionTools sharedEncryptionTools].algorithm = kCCAlgorithmDES;

    // ECB 加密
    NSString *encDES_ECBResult = [[EncryptionTools sharedEncryptionTools] encryptString:message keyString:key iv:nil];
    NSLog(@"DES_ECB enc:%@",encDES_ECBResult);
    // ECB 解密
    NSString *decDES_ECBResult = [[EncryptionTools sharedEncryptionTools] decryptString:encDES_ECBResult keyString:key iv:nil];
    NSLog(@"DES_ECB dec:%@",decDES_ECBResult);

    // CBC 加密
    NSString *encDES_CBCResult = [[EncryptionTools sharedEncryptionTools] encryptString:message keyString:key iv:ivData];
    NSLog(@"DES_CBC enc:%@",encDES_CBCResult);
    // CBC  解密
    NSString *decDES_CBCResult = [[EncryptionTools sharedEncryptionTools] decryptString:encDES_CBCResult keyString:key iv:ivData];
    NSLog(@"DES_CBC dec:%@",decDES_CBCResult);

run👇

image.png

示例Demo

XFCryptor

四、CCCrypt函数

4.1 CCCrypt介绍

CCCrypt对称加密算法的核心函数(加密/解密),共有11个参数👇

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)
    API_AVAILABLE(macos(10.4), ios(2.0));
  1. CCOperation 👉 kCCEncrypt 加密,kCCDecrypt 解密👇
enum {
    kCCEncrypt = 0,
    kCCDecrypt,
};
typedef uint32_t CCOperation;
  1. CCAlgorithm 👉 加密算法,默认为AES
enum {
    kCCAlgorithmAES128 = 0, /* Deprecated, name phased out due to ambiguity with key size */
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,
    kCCAlgorithmCAST,
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,
    kCCAlgorithmBlowfish
};
typedef uint32_t CCAlgorithm;
  1. CCOptions 👉 加密模式👇
    • ECB:kCCOptionPKCS7Padding | kCCOptionECBMode
    • CBC:kCCOptionPKCS7Padding
enum {
    /* options for block ciphers */
    kCCOptionPKCS7Padding   = 0x0001,
    kCCOptionECBMode        = 0x0002
    /* stream ciphers currently have no options */
};
typedef uint32_t CCOptions;
  1. key 👉 密钥
  2. keyLength 👉 密钥长度
  3. iv 👉 iv 初始化向量,ECB 不需要。iv定长所以不需要长度(8字节)。
  4. dataIn 👉 加密/解密的数据
  5. dataInLength 👉 加密/解密的数据长度
  6. dataOut 👉 缓冲区(地址),存放密文/明文
  7. dataOutAvailable 👉 缓冲区大小
  8. dataOutMoved 👉 加密/解密结果大小

4.2 CCCrypt安全隐患

如果直接在CCCrypt函数上打符号断点👇

image.png

image.png

然后读取x6x7的值就直接拿到了明文数据LGPerson和对应的长度👇

image.png

4.3解决方案

那如何解决上面的安全隐患呢?有人就想到了很直接的办法 👉 不使用CCCrypt函数自定义一套加解密算法。这个当然没问题,是可以的。但是如果仍然要坚持用CCCrypt函数呢?👇

  1. 在调用CCCrypt之前对数据做一层加密/转换。比如 👉 可以对明文数据进行按位异或再加密,解密时先解密再按位异或还原
  2. OC方法名称进行混淆(可编写脚本进行)。

总结

  • 对称加密
    • 明文通过密钥加密得到密文密文通过密钥解密得到明文
    • 常见算法 1. DES 2. 3DES 3. AES
    • 应用模式
      1. ECB(Electronic Code Book)电子密码本模式。 独立加密,独立解密,块与块之间互不影响。
      2. CBC(Cipher Block Chaining)密码分组链接模式。后面的数据依赖前面的数据,即使其中一块被破解,要继续破解其它的很难。
        • 多一个参数 iv 初始化向量
        • CCCrypt函数:直接使用会有安全隐患 👉 按位异或&代码混淆