本文主要是对称算法的终端演示+代码演示
OpenSSL终端演示
下面主要采用DES、AES
和 ECB、CBC
两两组合的方式进行演示,涉及的终端命令主要有以下一些
加密
AES + ECB
加密“hello”字符串
echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64
AES + CBC
加密“hello”字符串
echo -n hello | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
解密
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、DES + ECB
加密
- vi abc.txt
000000000000 111111111111 222222222222 000000000000 111111111111 222222222222 000000000000 111111111111 222222222222 000000000000 111111111111 222222222222 000000000000 111111111111 222222222222 000000000000 111111111111 222222222222
- 对称加密(默认会加盐):
openssl enc -des-ecb -K 016263 -nosalt -in abc.txt -out msg1.bin
enc
: 表示加密方式,即对称加密- msg1.bin: 二进制文件
- 查看二进制文件:
xxd msg1.bin
- 将第二个重复的前两个
00,改成88
000000000000 111111111111 222222222222 880000000000 111111111111 222222222222 000000000000 ......
查看此时的加密后密文二进制,与上面进行对比
- 变化:
931f 4a54 79bf 730f 4453 2df5 e152 38f1
变成了c7e1 1de2 c778 9df6 4d79 8bec 04ad 08c4
。说明修改两个字符,其最小单位16字节
- 如果修改
1
个字符,最小单位是8
个字节。所以加密过程中,最低是8字节
2、DES + CBC
- vi abc.txt(内容与1中相同)
- 加密:
openssl enc -des-cbc -K 616263 -iv 0102030405060708 -nosalt -in abc.txt -out msg3.bin
- 修改一个字符,获取 msg4.bin
-iv
:向量的表示方式616263
:加密的key,换成 abc 也是可以的
- 与ECB模式对比:从第2个开始,其二进制就不同了(CBC是链式加密)
注:剩余的AES+ECB、AES+CBC
请读者自行演练,这里就不在做演示了
代码演示
同样是通过DES、AES
和 ECB、CBC
两两组合的方式进行演示
1、AES + ECB
- (void)testEnc{ // AES + ECB 加密 NSString *key = @"abc"; NSString *encStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:nil]; NSLog(@"AES + ECB : %@", encStr); } <!--运行结果--> AES + ECB : d1QG4T2tivoi0Kiu3NEmZQ== <!--终端命令--> $ echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64 //与程序运行结果是一样的 d1QG4T2tivoi0Kiu3NEmZQ== $ echo -n d1QG4T2tivoi0Kiu3NEmZQ== | base64 -D |openssl enc -aes-128-ecb -K 616263 -nosalt -d hello%
echo -n hello
输出hello|
:表示输出符- 注:des 和 aes对比,加密强度不一样
2、AES + CBC
- (void)testEnc{ // 2、AES + CBC 加密 uint8_t iv[8] = {1, 2, 3, 4, 5, 6, 7, 8}; NSData *data = [NSData dataWithBytes:iv length:sizeof(iv)]; NSString *key = @"abc"; NSString *encStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:data]; //解密 NSString * decStr = [[EncryptionTools sharedEncryptionTools] decryptString:encStr keyString:key iv:data]; NSLog(@"AES + CBC : %@", encStr); NSLog(@"AES + CBC : %@", decStr); } //打印结果 AES + CBC : u3W/N816uzFpcg6pZ+kbdg== AES + CBC : hello <!--终端命令--> $ echo -n hello | openssl enc -aes-128-cbc -K 616263 -iv 0102030405060708 -nosalt | base64 u3W/N816uzFpcg6pZ+kbdg== $ echo -n u3W/N816uzFpcg6pZ+kbdg== | base64 -D |openssl enc -aes-128-cbc -K 616263 -iv 0102030405060708 -nosalt -d hello%
3、DES + ECB
- (void)testEnc{ // 3、DES + ECB [EncryptionTools sharedEncryptionTools].algorithm = kCCAlgorithmDES; NSString *key = @"abc"; NSString *encStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:nil]; NSLog(@"DES + ECB : %@", encStr); } //运行结果 DES + ECB : HQr0Oij2kbo= <!--终端命令--> $ echo -n hello | openssl enc -des-ecb -K 616263 -nosalt | base64 HQr0Oij2kbo= $ echo -n HQr0Oij2kbo= | base64 -D | openssl enc -des-ecb -K 616263 -nosalt -d hello%
4、DES + CBC
- (void)testEnc{ // 4、DES + CBC [EncryptionTools sharedEncryptionTools].algorithm = kCCAlgorithmDES; uint8_t iv[8] = {1, 2, 3, 4, 5, 6, 7, 8}; NSData *data = [NSData dataWithBytes:iv length:sizeof(iv)]; NSString *key = @"abc"; NSString *encStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:data]; //解密 NSString * decStr = [[EncryptionTools sharedEncryptionTools] decryptString:encStr keyString:key iv:data]; NSLog(@"AES + CBC : %@", encStr); NSLog(@"AES + CBC : %@", decStr); } //运行结果 AES + CBC : alvrvb3Gz88= AES + CBC : hello <!--终端命令--> $ echo -n hello | openssl enc -des-cbc -K 616263 -iv 0102030405060708 -nosalt | base64 alvrvb3Gz88= $ echo -n alvrvb3Gz88= | base64 -D | openssl enc -des-cbc -K 616263 -iv 0102030405060708 -nosalt -d hello%
加密解密实现
以下是DES、AES的完整对称加解密的代码封装
<!--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 { /* - kCCOptionPKCS7Padding | 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 加密 / kCCDeccrypt 解密 - 参数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
主要是通过系统的CCCrypt
实现,其中涉及11个参数,分别是
- 参数1:kCCEncrypt 加密 / kCCDeccrypt 解密 - 参数2:加密算法 - 参数3:加密选项 ECB / CBC - 参数4:KEY的地址 - 参数5:KEY的长度 - 参数6:iv初始化向量 - 参数7:加密的数据 - 参数8:加密数据的长度 - 参数9:密文的内存地址 - 参数10:密文缓冲区的大小 - 参数11:数据的指针(加密结果大小)
安全隐患:使用系统函数同样有数据泄漏的风险
调试CCCrypt
下面我们通过断点调试来说明其安全隐患
- 加符号断点
CCCrypt
- 运行程序
调试CCCrypt-01
通过寄存器获取参数,发现 hello 是明文,这是非常不安全的
有以下的改进建议
- 1、在系统函数之上做一层封装,例如 使用按位异或(最简单的封装)
- 加密:传入string时,做一次
按位异或运算
- 解密:先解密,再按位异或
- 2、方法名混淆 - 即方法名不变,但打包上架后进行了一系列的变化
总结
- 对称加密在iOS中使用的是系统的
CCCrypt
,有11个参数 - 直接使用系统的
CCCrypt
是存在明文泄漏的安全隐患的,所以需要在系统函数之上在做一些操作,来保证明文的安全性