02:hash算法和对称加密
复习
1:常见的hash算法
2:hash算法特点:算法公开、不可逆、相同数据md5相同、信息摘要
3:hash算法用途:密码加密,数字签名。
4:密码加密:md5、md5加盐、Hmac(2次散列)、登录权限。
5:数字签名,数字签名验证。
6:常见的对称加密
7:加密模式ecb、cbc
8:vi命令
目录
一:hash(算列)算法
1:常见的hash算法
2:hash算法特点
3:hash算法用途
4:密码加密流程、token
5:数字签名
二:对称加密
1:常见的对称加密
2:加密模式ecb、cbc
3:vi命令
正文
一:hash算法
1:hash 哈希(散列)函数,不是加密算法,经常和加密一起使用。
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。
MD5
SHA1/256/512
HMAC(加密的方案)
2:算法特点
1:算法公开
2:对相同的数据运算,结果一样。默认128位二进制进制的数据。32位的16进制。
3:不可逆运算。
4:信息摘要,用来做数据识别。
3:用途
1:用户密码的加密(不用rsa加密,数据库应该存储密码,用户隐私信息(包括密码)是hash算法)
2:版权
3:数字签名
4:搜索引擎
备注:hash算法一般是对字符串来操作的。
cmd5可以解开md5的值。
4:用户密码加密:
1:直接对密码进行md5不安全,可以反查询。
2:MD5 加盐。静态字符串。
拼接一个复杂的字符串。
缺点:盐是固定的,写死在程序中,一旦泄露就不安全了。
3:Hmac:加密方案。
使用一个秘钥(key)加密,并且做两次散列。在实际开发中,这个秘钥来自服务器。动态盐。
3.1:一个账号对应一个key,而且可以更新这个key。
3.2:什么时候客户端拿到key
首次登陆的时候拿到key。注册的时候拿到key。
换设备的时候,没有key,需要验证的时候。
3.3:对key进行扩展,设备锁::让原始设备允许的时候,才可以申请key。
Key是一个权限。有了key才可以登录。
3.4:目的:保护了用户的密码。
4:登录权限
添加服务器的时间戳(到分钟级别的时间戳)
(Hmac哈希值+202005211556).MD5用于登录。
5:数字签名
对原始数据的hash值用rsa加密。
用hash来验证数据的完整性,用来判断数据是否篡改。
发给服务器的数据是:
原始数据+原始数据的hash值的rsa加密值。
数组签名
数字签名验证
二:对称加密
1:常见加密算法
1.1:DES
1.2:3DES:3对秘钥,对同一个数据进行3次加密。
1.3:AES(高级密码标准)
2:对称机密-加密模式
Ecb:电子密码本模式
Cbc:密码分组链接模式,后一个密码块加密依赖前一个密码块的加密。
ECB(Electronic Code Book):电子密码本模式。每一块数据,独立加密。
最基本的加密模式,也就是通常理解的加密,相同的明文将永远加密成相同的密文,无初始向量,容易受到密码本重放攻击,一般情况下很少用。
CBC(Cipher Block Chaining):密码分组链接模式。使用一个密钥和一个初始化向量[IV]对数据执行加密。
明文被加密前要与前面的密文进行异或运算后再加密,因此只要选择不同的初始向量,相同的密文加密后会形成不同的密文,这是目前应用最广泛的模式。CBC加密后的密文是上下文相关的,但明文的错误不会传递到后续分组,但如果一个分组丢失,后面的分组将全部作废(同步错误)。
CBC可以有效的保证密文的完整性,如果一个数据块在传递是丢失或改变,后面的数据将无法正常解密。
3:终端命令
1: 创建message.txt文件
1.1:vi message.txt
1.2: 输入i进入编辑模式
1.3:输入文本内容
1.4:esc退出编辑模式
1.5: :wq 退出编辑模式,并且保存
2:加密message.txt
2.1: ecb模式
openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out msg.bin
2.2: cbc模式 xxd 查看二进制数据
openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out msg1.bin
三: 代码
1:hash算法
代码
#import <Foundation/Foundation.h> @interface NSString (Hash) #pragma mark - 散列函数 /** * 计算MD5散列结果 * * 终端测试命令: * @code * md5 -s "string" * @endcode * * <p>提示:随着 MD5 碰撞生成器的出现,MD5 算法不应被用于任何软件完整性检查或代码签名的用途。<p> * * @return 32个字符的MD5散列字符串 */ - (NSString *)md5String; /** * 计算SHA1散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl sha -sha1 * @endcode * * @return 40个字符的SHA1散列字符串 */ - (NSString *)sha1String; /** * 计算SHA256散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl sha -sha256 * @endcode * * @return 64个字符的SHA256散列字符串 */ - (NSString *)sha256String; /** * 计算SHA 512散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl sha -sha512 * @endcode * * @return 128个字符的SHA 512散列字符串 */ - (NSString *)sha512String; #pragma mark - HMAC 散列函数 /** * 计算HMAC MD5散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl dgst -md5 -hmac "key" * @endcode * * @return 32个字符的HMAC MD5散列字符串 */ - (NSString *)hmacMD5StringWithKey:(NSString *)key; /** * 计算HMAC SHA1散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl sha -sha1 -hmac "key" * @endcode * * @return 40个字符的HMAC SHA1散列字符串 */ - (NSString *)hmacSHA1StringWithKey:(NSString *)key; /** * 计算HMAC SHA256散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl sha -sha256 -hmac "key" * @endcode * * @return 64个字符的HMAC SHA256散列字符串 */ - (NSString *)hmacSHA256StringWithKey:(NSString *)key; /** * 计算HMAC SHA512散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl sha -sha512 -hmac "key" * @endcode * * @return 128个字符的HMAC SHA512散列字符串 */ - (NSString *)hmacSHA512StringWithKey:(NSString *)key; #pragma mark - 文件散列函数 /** * 计算文件的MD5散列结果 * * 终端测试命令: * @code * md5 file.dat * @endcode * * @return 32个字符的MD5散列字符串 */ - (NSString *)fileMD5Hash; /** * 计算文件的SHA1散列结果 * * 终端测试命令: * @code * openssl sha -sha1 file.dat * @endcode * * @return 40个字符的SHA1散列字符串 */ - (NSString *)fileSHA1Hash; /** * 计算文件的SHA256散列结果 * * 终端测试命令: * @code * openssl sha -sha256 file.dat * @endcode * * @return 64个字符的SHA256散列字符串 */ - (NSString *)fileSHA256Hash; /** * 计算文件的SHA512散列结果 * * 终端测试命令: * @code * openssl sha -sha512 file.dat * @endcode * * @return 128个字符的SHA512散列字符串 */ - (NSString *)fileSHA512Hash; @end
#import "NSString+Hash.h" #import <CommonCrypto/CommonCrypto.h> @implementation NSString (Hash) #pragma mark - 散列函数 - (NSString *)md5String { const char *str = self.UTF8String; uint8_t buffer[CC_MD5_DIGEST_LENGTH]; CC_MD5(str, (CC_LONG)strlen(str), buffer); return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH]; } - (NSString *)sha1String { const char *str = self.UTF8String; uint8_t buffer[CC_SHA1_DIGEST_LENGTH]; CC_SHA1(str, (CC_LONG)strlen(str), buffer); return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH]; } - (NSString *)sha256String { const char *str = self.UTF8String; uint8_t buffer[CC_SHA256_DIGEST_LENGTH]; CC_SHA256(str, (CC_LONG)strlen(str), buffer); return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH]; } - (NSString *)sha512String { const char *str = self.UTF8String; uint8_t buffer[CC_SHA512_DIGEST_LENGTH]; CC_SHA512(str, (CC_LONG)strlen(str), buffer); return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH]; } #pragma mark - HMAC 散列函数 - (NSString *)hmacMD5StringWithKey:(NSString *)key { const char *keyData = key.UTF8String; const char *strData = self.UTF8String; uint8_t buffer[CC_MD5_DIGEST_LENGTH]; CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer); return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH]; } - (NSString *)hmacSHA1StringWithKey:(NSString *)key { const char *keyData = key.UTF8String; const char *strData = self.UTF8String; uint8_t buffer[CC_SHA1_DIGEST_LENGTH]; CCHmac(kCCHmacAlgSHA1, keyData, strlen(keyData), strData, strlen(strData), buffer); return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH]; } - (NSString *)hmacSHA256StringWithKey:(NSString *)key { const char *keyData = key.UTF8String; const char *strData = self.UTF8String; uint8_t buffer[CC_SHA256_DIGEST_LENGTH]; CCHmac(kCCHmacAlgSHA256, keyData, strlen(keyData), strData, strlen(strData), buffer); return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH]; } - (NSString *)hmacSHA512StringWithKey:(NSString *)key { const char *keyData = key.UTF8String; const char *strData = self.UTF8String; uint8_t buffer[CC_SHA512_DIGEST_LENGTH]; CCHmac(kCCHmacAlgSHA512, keyData, strlen(keyData), strData, strlen(strData), buffer); return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH]; } #pragma mark - 文件散列函数 #define FileHashDefaultChunkSizeForReadingData 4096 - (NSString *)fileMD5Hash { NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self]; if (fp == nil) { return nil; } CC_MD5_CTX hashCtx; CC_MD5_Init(&hashCtx); while (YES) { @autoreleasepool { NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData]; CC_MD5_Update(&hashCtx, data.bytes, (CC_LONG)data.length); if (data.length == 0) { break; } } } [fp closeFile]; uint8_t buffer[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(buffer, &hashCtx); return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH]; } - (NSString *)fileSHA1Hash { NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self]; if (fp == nil) { return nil; } CC_SHA1_CTX hashCtx; CC_SHA1_Init(&hashCtx); while (YES) { @autoreleasepool { NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData]; CC_SHA1_Update(&hashCtx, data.bytes, (CC_LONG)data.length); if (data.length == 0) { break; } } } [fp closeFile]; uint8_t buffer[CC_SHA1_DIGEST_LENGTH]; CC_SHA1_Final(buffer, &hashCtx); return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH]; } - (NSString *)fileSHA256Hash { NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self]; if (fp == nil) { return nil; } CC_SHA256_CTX hashCtx; CC_SHA256_Init(&hashCtx); while (YES) { @autoreleasepool { NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData]; CC_SHA256_Update(&hashCtx, data.bytes, (CC_LONG)data.length); if (data.length == 0) { break; } } } [fp closeFile]; uint8_t buffer[CC_SHA256_DIGEST_LENGTH]; CC_SHA256_Final(buffer, &hashCtx); return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH]; } - (NSString *)fileSHA512Hash { NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self]; if (fp == nil) { return nil; } CC_SHA512_CTX hashCtx; CC_SHA512_Init(&hashCtx); while (YES) { @autoreleasepool { NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData]; CC_SHA512_Update(&hashCtx, data.bytes, (CC_LONG)data.length); if (data.length == 0) { break; } } } [fp closeFile]; uint8_t buffer[CC_SHA512_DIGEST_LENGTH]; CC_SHA512_Final(buffer, &hashCtx); return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH]; } #pragma mark - 助手方法 /** * 返回二进制 Bytes 流的字符串表示形式 * * @param bytes 二进制 Bytes 数组 * @param length 数组长度 * * @return 字符串表示形式 */ - (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length { NSMutableString *strM = [NSMutableString string]; for (int i = 0; i < length; i++) { [strM appendFormat:@"%02x", bytes[i]]; } return [strM copy]; } @end
使用
#import "ViewController.h" #import "NSString+Hash.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } //足够复杂! static NSString * salt = @"(*(*(DS*YFHIUYF(*&DSFHUS(*AD&"; -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //密码 NSString * pwd = @"123456"; //MD5 直接加密 e10adc3949ba59abbe56e057f20f883e //不足:不够安全了。可以反查询! // pwd = pwd.md5String; //MD5 加盐 //弊端: 盐是固定的,写死在程序里面,一旦泄露就不安全了! // pwd = [pwd stringByAppendingString:salt].md5String; /** HMAC * 使用一个密钥加密,并且做两次散列! * 在实际开发中,密钥(KEY)来自于服务器(动态的)! * 一个账号,对应一个KEY,而且还可以跟新! */ pwd = [pwd hmacMD5StringWithKey:@"hank"]; NSLog(@"%@",pwd); } @end
2:对称加密
2.1:对称加密终端命令
加密
echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64
解密
echo -n d1QG4T2tivoi0Kiu3NEmZQ== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d
2.2:CCCrypt 函数,des、aes 加密,2中加密模式,秘钥,11个参数
代码
#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
#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;//CBC 加密! } else { option = kCCOptionPKCS7Padding | kCCOptionECBMode;//ECB加密! } // 设置输出缓冲区 NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0]; size_t bufferSize = [data length] + self.blockSize; void *buffer = malloc(bufferSize); // 开始解密 size_t decryptedSize = 0; /**CCCrypt 对称加密算法的核心函数(加密/解密) 参数: 1、kCCEncrypt 加密/kCCDecrypt 解密 2、加密算法、默认的 AES/DES 3、加密方式的选项 kCCOptionPKCS7Padding | kCCOptionECBMode;//ECB加密! kCCOptionPKCS7Padding;//CBC 加密! 4、加密密钥 5、密钥长度 6、iv 初始化向量,ECB 不需要指定 7、加密的数据 8、加密的数据长度 9、缓冲区(地址),存放密文的 10、缓冲区的大小 11、加密结果大小 */ 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
使用
#import "ViewController.h" #import "EncryptionTools.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; /** AES - ECB */ NSString * key = @"abc"; NSString * encStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:nil]; NSLog(@"加密的结果是:%@",encStr); NSLog(@"解密的结果是:%@",[[EncryptionTools sharedEncryptionTools] decryptString:encStr 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(@"CBC加密:%@",[[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:@"abc" iv:ivData]); NSLog(@"解密:%@",[[EncryptionTools sharedEncryptionTools] decryptString:@"u3W/N816uzFpcg6pZ+kbdg==" keyString:key iv:ivData]); } @end
注意
1:hash算法特点用途(密码加密【hmac双层加密-token】和数字签名)
2:对称加密算法和对称加密模式ecb、cbc