RSA加密(下)

一. 证书生成步骤

使用RSA加密(上)终端生成的公钥、私钥(属于自己签名)

证书申请步骤
  • 申请CSR文件:keychain -> 证书助理 -> 从证书颁发机构请求证书
  • 生成CSR请求文件(证书颁发机构信息 + 公钥)
$ cd /Users/wn/Desktop/密码学-RSA/备课代码/ 加密
$ openssl req -new -key private.pem -out rsacert.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----  //这里填写证书颁发机构信息
Country Name (2 letter code) []:cn
State or Province Name (full name) []:shanxi
...
  • 生成CRT证书(自己签名,没有认证的)
// 3650表示10年
$ openssl x509 -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
  • 转成DER证书 (把上面CTR证书转成DER格式的证书)
$ openssl x509 -outform der -in rsacert.crt -out rsacert.der
  • 生成p12文件(即私钥)
$ openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
小结

.p12文件.der证书是一对,有了这两个文件,就可以在代码中进行加解密。代码中只使用der证书

二. Base64

base64编码由0-9、a-z、A-Z + / = 组成的文本,=(等号表示补零)

// 创建并编写内容
$ vi message.txt
$ cat message.txt
hello
// 对message.txt进行base64编码,生成abc.txt
$ base64 message.txt -o abc.txt
$ cat abc.txt
aGVsbG8gCg==
// 对abc.txt进行base64解码,生成123.txt
$ base64 abc.txt -o 123.txt -D
$ cat 123.txt
hello
Base64代码演示
// ViewController.m文件内容如下
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"%@", [self base64Encode:@"A"]);
    NSLog(@"%@", [self base64Decode:@"QQ=="]);
}

//编码
- (NSString *)base64Encode:(NSString *)string{
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    return [data base64EncodedStringWithOptions: 0];
}
//解码
- (NSString *)base64Decode:(NSString *)string{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}


QQ==
A
base64说明
  • base64只适用于表示二进制文件
  • base64编码后,文件会变大(大原文件的1/3),不适合对大型数据进行编码
  • bse64和数据是一一对应的

三. RSA代码演示

通过上面证书生成步骤,准备好·p12der文件,创建工程RSADemo,工程中创建RSA加解密类:RSACryptor


#import 

@interface RSACryptor : NSObject
    
+ (instancetype)sharedRSACryptor;
    
    /**
     *  生成密钥对
     *
     *  @param keySize 密钥尺寸,可选数值(512/1024/2048)
     */
- (void)generateKeyPair:(NSUInteger)keySize;
    
    /**
     *  加载公钥
     *
     *  @param publicKeyPath 公钥路径
     *
     @code
     # 生成证书
     $ openssl genrsa -out ca.key 1024
     # 创建证书请求
     $ openssl req -new -key ca.key -out rsacert.csr
     # 生成证书并签名
     $ openssl x509 -req -days 3650 -in rsacert.csr -signkey ca.key -out rsacert.crt
     # 转换格式
     $ openssl x509 -outform der -in rsacert.crt -out rsacert.der
     @endcode
     */
- (void)loadPublicKey:(NSString *)publicKeyPath;
    
    /**
     *  加载私钥
     *
     *  @param privateKeyPath p12文件路径
     *  @param password       p12文件密码
     *
     @code
     openssl pkcs12 -export -out p.p12 -inkey ca.key -in rsacert.crt
     @endcode
     */
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password;
    
    /**
     *  加密数据
     *
     *  @param plainData 明文数据
     *
     *  @return 密文数据
     */
- (NSData *)encryptData:(NSData *)plainData;
    
    /**
     *  解密数据
     *
     *  @param cipherData 密文数据
     *
     *  @return 明文数据
     */
- (NSData *)decryptData:(NSData *)cipherData;
@end


#import "RSACryptor.h"

// 填充模式
#define kTypeOfWrapPadding        kSecPaddingPKCS1
//kSecPaddingNone  不填充! 密文每次都不会变化.

// 公钥/私钥标签
#define kPublicKeyTag            "com.logic.EncryptDemo.publickey"
#define kPrivateKeyTag            "com.logic.EncryptDemo.privatekey"

static const uint8_t publicKeyIdentifier[]        = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[]        = kPrivateKeyTag;

@interface RSACryptor() {
    SecKeyRef publicKeyRef;                             // 公钥引用
    SecKeyRef privateKeyRef;                            // 私钥引用
}
    
    @property (nonatomic, retain) NSData *publicTag;        // 公钥标签
    @property (nonatomic, retain) NSData *privateTag;       // 私钥标签
    
    @end

@implementation RSACryptor
    
+ (instancetype)sharedRSACryptor {
    static id instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
    
- (instancetype)init {
    self = [super init];
    if (self) {
        // 查询密钥的标签
        _privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
        _publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
    }
    return self;
}
    
#pragma mark - 加密 & 解密数据
- (NSData *)encryptData:(NSData *)plainData {
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSAssert(plainData != nil, @"明文数据为空");
    NSAssert(publicKeyRef != nil, @"公钥为空");
    
    NSData *cipher = nil;
    uint8_t *cipherBuffer = NULL;
    
    // 计算缓冲区大小
    cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
    keyBufferSize = [plainData length];
    
    if (kTypeOfWrapPadding == kSecPaddingNone) {
        NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容太大");
    } else {
        NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大");
    }
    
    // 分配缓冲区
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    memset((void *)cipherBuffer, 0x0, cipherBufferSize);
    
    // 使用公钥加密
    sanityCheck = SecKeyEncrypt(publicKeyRef,
                                kTypeOfWrapPadding,
                                (const uint8_t *)[plainData bytes],
                                keyBufferSize,
                                cipherBuffer,
                                &cipherBufferSize
                                );
    
    NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %d", sanityCheck);
    
    // 生成密文数据
    cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
    
    if (cipherBuffer) free(cipherBuffer);
    
    return cipher;
}
    
- (NSData *)decryptData:(NSData *)cipherData {
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSData *key = nil;
    uint8_t *keyBuffer = NULL;
    
    SecKeyRef privateKey = NULL;
    
    privateKey = [self getPrivateKeyRef];
    NSAssert(privateKey != NULL, @"私钥不存在");
    
    // 计算缓冲区大小
    cipherBufferSize = SecKeyGetBlockSize(privateKey);
    keyBufferSize = [cipherData length];
    
    NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大");
    
    // 分配缓冲区
    keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
    memset((void *)keyBuffer, 0x0, keyBufferSize);
    
    // 使用私钥解密
    sanityCheck = SecKeyDecrypt(privateKey,
                                kTypeOfWrapPadding,
                                (const uint8_t *)[cipherData bytes],
                                cipherBufferSize,
                                keyBuffer,
                                &keyBufferSize
                                );
    
    NSAssert1(sanityCheck == noErr, @"解密错误,OSStatus == %d", sanityCheck);
    
    // 生成明文数据
    key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
    
    if (keyBuffer) free(keyBuffer);
    
    return key;
}
    
#pragma mark - 密钥处理
    /**
     *  生成密钥对
     */
- (void)generateKeyPair:(NSUInteger)keySize {
    OSStatus sanityCheck = noErr;
    publicKeyRef = NULL;
    privateKeyRef = NULL;
    
    NSAssert1((keySize == 512 || keySize == 1024 || keySize == 2048), @"密钥尺寸无效 %tu", keySize);
    
    // 删除当前密钥对
    [self deleteAsymmetricKeys];
    
    // 容器字典
    NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
    
    // 设置密钥对的顶级字典
    [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
    
    // 设置私钥字典
    [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [privateKeyAttr setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    
    // 设置公钥字典
    [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [publicKeyAttr setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    
    // 设置顶级字典属性
    [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
    [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
    
    // SecKeyGeneratePair 返回密钥对引用
    sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
    NSAssert((sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL), @"生成密钥对失败");
}
    
    /**
     *  加载公钥
     */
- (void)loadPublicKey:(NSString *)publicKeyPath {
    
    NSAssert(publicKeyPath.length != 0, @"公钥路径为空");
    
    // 删除当前公钥
    if (publicKeyRef) CFRelease(publicKeyRef);
    
    // 从一个 DER 表示的证书创建一个证书对象
    NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
    SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
    NSAssert(certificateRef != NULL, @"公钥文件错误");
    
    // 返回一个默认 X509 策略的公钥对象,使用之后需要调用 CFRelease 释放
    SecPolicyRef policyRef = SecPolicyCreateBasicX509();
    // 包含信任管理信息的结构体
    SecTrustRef trustRef;
    
    // 基于证书和策略创建一个信任管理对象
    OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
    NSAssert(status == errSecSuccess, @"创建信任管理对象失败");
    
    // 信任结果
    SecTrustResultType trustResult;
    // 评估指定证书和策略的信任管理是否有效
    status = SecTrustEvaluate(trustRef, &trustResult);
    NSAssert(status == errSecSuccess, @"信任评估失败");
    
    // 评估之后返回公钥子证书
    publicKeyRef = SecTrustCopyPublicKey(trustRef);
    NSAssert(publicKeyRef != NULL, @"公钥创建失败");
    
    if (certificateRef) CFRelease(certificateRef);
    if (policyRef) CFRelease(policyRef);
    if (trustRef) CFRelease(trustRef);
}
    
    /**
     *  加载私钥
     */
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
    
    NSAssert(privateKeyPath.length != 0, @"私钥路径为空");
    
    // 删除当前私钥
    if (privateKeyRef) CFRelease(privateKeyRef);
    
    NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath];
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    CFStringRef passwordRef = (__bridge CFStringRef)password;
    
    // 从 PKCS #12 证书中提取标示和证书
    SecIdentityRef myIdentity;
    SecTrustRef myTrust;
    const void *keys[] =   {kSecImportExportPassphrase};
    const void *values[] = {passwordRef};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    
    // 返回 PKCS #12 格式数据中的标示和证书
    OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
    
    if (status == noErr) {
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
        myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
        myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
    }
    
    if (optionsDictionary) CFRelease(optionsDictionary);
    
    NSAssert(status == noErr, @"提取身份和信任失败");
    
    SecTrustResultType trustResult;
    // 评估指定证书和策略的信任管理是否有效
    status = SecTrustEvaluate(myTrust, &trustResult);
    NSAssert(status == errSecSuccess, @"信任评估失败");
    
    // 提取私钥
    status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
    NSAssert(status == errSecSuccess, @"私钥创建失败");
}
    
    /**
     *  删除非对称密钥
     */
- (void)deleteAsymmetricKeys {
    OSStatus sanityCheck = noErr;
    NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
    
    // 设置公钥查询字典
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // 设置私钥查询字典
    [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // 删除私钥
    sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPrivateKey);
    NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"删除私钥错误,OSStatus == %d", sanityCheck);
    
    // 删除公钥
    sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPublicKey);
    NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"删除公钥错误,OSStatus == %d", sanityCheck);
    
    if (publicKeyRef) CFRelease(publicKeyRef);
    if (privateKeyRef) CFRelease(privateKeyRef);
}
    
    /**
     *  获得私钥引用
     */
- (SecKeyRef)getPrivateKeyRef {
    OSStatus sanityCheck = noErr;
    SecKeyRef privateKeyReference = NULL;
    
    if (privateKeyRef == NULL) {
        NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
        
        // 设置私钥查询字典
        [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
        [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
        [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
        
        // 获得密钥
        sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference);
        
        if (sanityCheck != noErr) {
            privateKeyReference = NULL;
        }
    } else {
        privateKeyReference = privateKeyRef;
    }
    
    return privateKeyReference;
}
@end

RSADemo工程导入上面准备好的·p12der文件,通过代码加载公钥der文件和私钥p12文件


- (void)viewDidLoad {
    [super viewDidLoad];
    //1\加载公钥
    [[RSACryptor sharedRSACryptor] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]];
    
    //2.加载私钥
    [[RSACryptor sharedRSACryptor] loadPrivateKey:[[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"];
}

使用RSA进行加解密

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //加密
    NSData * result = [[RSACryptor sharedRSACryptor] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
    NSString * base64 =  [result base64EncodedStringWithOptions:0];
    NSLog(@"%@",base64);
    
    //解密
    NSData * jiemi = [[RSACryptor sharedRSACryptor] decryptData:result];
    NSLog(@"解密:%@",[[NSString alloc] initWithData:jiemi encoding:NSUTF8StringEncoding]);
}


t+UBNI0w7IgzG3QBbmBaM0U/4aHabbvaUzxXI0/flWwrel/RGQNc7uwVDhNiAM4udT+Z9F0yXfIGy0JTzGI9gp8l5wu+31DPn6UgYUvaD2CsVB+GeLIfNh1yrvXY48UQnUci2kAknm+7aV9jRVTLOQcG1HTteU31g7qQSMiagu0=
解密:hello
RSA加解密结果

从结果中可以发现,每次RSA加密结果不一样,原因是因为RSA有个填充模式,导致每次结果不一样

  • kSecPaddingNone 不填充,密文每次不变
  • kSecPaddingPKCS1 填充,密文随机变化
RSA加解密场景
  • 用于服务端(私钥)与客户端(公钥)传递数据,客户端安装app时直接带有公钥。使用对称加密传递大数据,使用RSA加密传递对称加密使用到的key
  • 用于签名
小结:RSA代码加解密是通过Security框架
  • 加密:使用SecKeyEncrypt函数
  • 解密:使用SecKeyDecrypt函数

四. Hash概述

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

常见的hash算法主要有MD5SHA-1SHA-256SHA-512

Hash的特点
  • 算法是公开的
  • 对相同数据运算,得到的结果是一样的
  • 对不同数据运算,如MD5得到的结果默认是128位,32个字符(16进制标识)。
  • 这玩意没法逆运算
  • 信息摘要,信息“指纹”,是用来做数据识别,完整性检查等
散列碰撞

数据是无限的,hash的结果是16^32个有限的。所以会存在两个或多个不同的数据,拥有相同的hash值,这就是散列碰撞

Hash用途
  • 用户密码的加密
  • 搜索引擎
  • 版权
  • 数字签名
用户密码加密

数据传输/存储原则:网络传输数据 + 本地保存数据 + 服务端保存数据等 ,这些是隐私数据,绝对不能明文,一定是密文传输/存储。

为什么用户密码加密不用RSA加密?

RSA加密算法需要公钥、私钥,对开发人员依赖太高。假如开发人员带着公钥、私钥离职,对公司造成的损失可能非常高。服务器的数据库中不能保存用户的明文密码,最好是开发者都不知道密码才能保证相对安全,这个时候使用Hash算法最合适。

创建工程密码加密,来进行演示Hash用途 -> 用户密码加密,使用封装好的Hash算法


#import 

@interface NSString (Hash)
    
#pragma mark - 散列函数
    /**
     *  计算MD5散列结果
     *
     *  终端测试命令:
     *  @code
     *  md5 -s "string"
     *  @endcode
     *
     *  

提示:随着 MD5 碰撞生成器的出现,MD5 算法不应被用于任何软件完整性检查或代码签名的用途。

* * @return 32个字符的MD5散列字符串 */ - (NSString *)md5String; /** * 计算SHA1散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl -sha1 * @endcode * * @return 40个字符的SHA1散列字符串 */ - (NSString *)sha1String; /** * 计算SHA256散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl -sha256 * @endcode * * @return 64个字符的SHA256散列字符串 */ - (NSString *)sha256String; /** * 计算SHA 512散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl -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 -sha1 -hmac "key" * @endcode * * @return 40个字符的HMAC SHA1散列字符串 */ - (NSString *)hmacSHA1StringWithKey:(NSString *)key; /** * 计算HMAC SHA256散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl -sha256 -hmac "key" * @endcode * * @return 64个字符的HMAC SHA256散列字符串 */ - (NSString *)hmacSHA256StringWithKey:(NSString *)key; /** * 计算HMAC SHA512散列结果 * * 终端测试命令: * @code * echo -n "string" | openssl -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 @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

方式一:直接使用MD5
// 终端演示md5加密
$ md5 -s "123456"
MD5 ("123456") = e10adc3949ba59abbe56e057f20f883e

// 代码演示md5加密
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //传递数据
    pwd = pwd.md5String;
    NSLog(@"现在的密码是 - %@", pwd);
}


 现在的密码是 - e10adc3949ba59abbe56e057f20f883e

注意:直接使用MD5不安全,大部分MD5现在通过网站都可以还原,比如cmd5网站

方式二:MD5加固定盐
// 在方式一的基础上,加上一个固定的字符串,代码演示如下
//加盐
static NSString *salt = @"LKSJDFLKJ&^&@@";
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //传递数据
    pwd = [pwd stringByAppendingString:salt].md5String;
    NSLog(@"现在的密码是 - %@", pwd);
}


现在的密码是:2359298f49af5695a4b846e95bdea467

注意:加固定盐,现在也不安全,因为如果盐泄漏了,也会有数据泄漏的风险

方式三:HMAC加密

HMAC加密,其原理就是判断是否有key,这个key是由服务器动态提供的,保存在手机端。简单来说,就相当于现在的授权设备,HMAC主要就是检测你的设备是否有授权。其代码演示如下

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //key是动态的,由服务器提供(即一个账号一个key)
    pwd = [pwd hmacMD5StringWithKey:@"HK"];
    NSLog(@"现在的密码是 - %@", pwd);
}


现在的密码是:e5cb93c6eef4431f3574c6ca100b61de

注意:现在这种加密方式,用户密码确实安全了。但是用于服务器登录验证是一个hash值,而不是密码。所以这个hash值也有被拦截的风险,也是不安全的。这里只是防住了密码,并没有防住登陆

方式四:HMAC + 时间戳

基于方式三的基础上,我们增加时间戳一起加密。对于密码来说,加密方式还是采用HMAC加密。只是验证方式发生了变化。原本只是验证hash值即可。现在是在hash值的基础上加上了一个时间期限。例如hash值的有效时间最多是2分钟,超过这个时间就失效了,类似于验证码验证。这样就能有效防住登陆篡改

流程如下:

  • 客户端:对密码使用HMAC加密,得到一个hash值A,hash值带上服务器给的时间戳,求得另一个hash值B
  • 将客户端得到的Hash值A、Hash值B发送到服务器进行验证
  • 服务器:将传过来的Hash值A,带上服务器的时间戳,得到一个Hash值C,判断Hash值C是否等于传过来的Hash值B,如果不等于,则表示不通过,在时间范围内,再继续验证上一分钟

五. 数字签名

数字签名目的:验证二进制数据是不是原始颁发机构颁发的
早期只是给一个数据,扔给我,我就用,这是不安全的

  • 数据报文,即原始数据

后来的数字签名,为了安全,不仅给数据,还给数据的Hash值,但是还是不安全,因为数据、hash值都有可能同时篡改

  • 数据报文,即原始数据
  • 原始数据的Hash值

现在的数字签名,除了给数据,同时还给原始数据Hash值进行RSA加密后得到的hash值,其中原始数据HASH值RSA加密后得到的值称为原始数据的数字签名

  • 数据报文,即原始数据
  • 原始数据的Hash值
  • 数字签名:原始数据Hash值通过RSA加密后得到的结果
数字签名小结

算法:RSA + HASH
目的:验证数据的完整性,不被篡改
主要逻辑:

  • 原始数据进行HASH
  • 使用RSA加密 HASH值(这部分数据就是原始数据的签名信息)
  • 将原始数据hash值 + 数字签名 一起打包发送传递

你可能感兴趣的:(RSA加密(下))