iOS - Safe iOS 加密安全

1、Base64 编码

  • 简介:

    • Base64 是一种基于64个可打印字符来表示二进制数据的表示方法,可打印字符包括字母 A-Z、a-z、0-9,共 62 个字符,另外两个符号在不同的系统不同 +,/。
    • Base 64 编码后的结果能够反算,不够安全。
    • Base 64 是所有现代加密算法的基础算法。
    • 由于现代密码学是基于二进制数据进行加密的,因此经常会使用 Base64 对加密结果进行编码,以便于在网络上传输。
  • 原理:

    • 原本 8 bit 一组,改为 6 bit 一组,不足的补零,每两个 0 用一个 = 表示。

      sign1

      sign2

  • 缺点:

    • Base 64 编码后的结果能够反算,非常不安全。
    • 用 base64 编码之后,结果会变大,增加了约 1/3。
    • 用 base64 编码的结果有非常明显的特点,末尾有 = 。
  • 终端使用:

        $ base64 123.png -o 123.txt           编码,将文件 123.png 编码为 123.txt
        $ base64 123.txt -o 321.png -D        解码,将文件 123.txt 解码为 321.png
    
        $ echo -n "A" | base64                编码,将字符串 A 编码
        $ echo -n "QQ==" | base64 -D          解码,将字符串 QQ== 解码

2、对称算法

  • 对称算法有时又叫传统密码算法,加密和解密使用相同密钥的算法,又称私钥加密、或者共享密钥。

  • 算法公开、计算量小、加密速度快、加密效率高,可以对大数据进行加密。
  • 双方使用相同钥匙,安全性得不到保证。秘钥的安全性非常重要,普遍采用的方法是使用 RSA 的加密算法加密给对称加密算法的秘钥进行加密。
  • 对称加密的速度比公钥加密快很多,在很多场合都需要对称加密。

  • 加密方法:

    • DES :数据加密标准。
      • 是一种分组数据加密技术,先将数据分成固定长度的小数据块,之后进行加密。
      • 速度较快,适用于大量数据加密。
    • 3DES:使用三组密钥做三次加密。
      • 是一种基于 DES 的加密算法,使用 3 个不同密钥对同一个分组数据块进行 3 次加密,如此以使得密文强度更高。
    • AES :高级加密标准。
      • 是美国联邦政府采用的一种区块加密标准。
      • 相较于 DES 和 3DES 算法而言,AES 算法有着更高的速度和资源使用效率,安全级别也较之更高了,被称为下一代加密标准。
  • 加密技术:

    • ECB :电子代码本,就是说每个块都是独立加密的。
    • CBC :密码块链,使用一个密钥和一个初始化向量(IV)对数据执行加密转换。
      • CBC 加密可以有效地保证密文的完整性,也就是说如果有一个块在传送时丢失了(或被敌人改变了),就会导致后面所有的块无法正常解密这个特性可以用来防范一些窃听技巧。

3、非对称算法

  • 非对称算法是指加密和解密使用不同密钥的算法,又称公钥加密。

  • 非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey),公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。

  • 非对称密码体制的特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快,适合对小数据加密。

  • 对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。

  • 加密方法:

    • RSA :
      • 由于 RSA 算法的加密解密速度要比对称算法的速度慢很多,在实际应用中,通常采取数据本身的加密解密使用对称加密算法(AES/DES3),用 RSA 算法加密并传输对称算法所需的秘钥。
      • RSA 算法还在身份认证(或称鉴权)以及数字签名方面得到广泛的使用。

4、散列算法

  • 散列算法又称散列函数,哈希(HASH)函数,该函数将数据打乱混合,重新创建一个叫做散列值的指纹。

  • 任意二进制数据进行 "散列",即对不同长度的输入消息,产生固定长度的输出。这个固定长度的输出称为原输入消息的 “散列” 或 “消息摘要”。
  • 对任意一个二进制数据进行加密,可以得到定长的字符串结果。相同的字符串,使用相同的算法,每次加密的结果是固定的。
  • 散列不能逆运算,常用在用户密码上,服务器不需要知道用户的准确密码。

  • 加密方法:

    • MD5 :加密结果只有 32 个字符,因为数据长度不够,现在国外基本上已经不怎么用了,国内用的很普遍。
      • MD5 不能反算,但 MD5 已经被破解了,用碰撞算法,可以将两个不同的文件生成出相同的 MD5 结果。
      • MD5 在线加密解密网站 http://www.cmd5.com ,该网站破解原理:大量的常见数据被生成 md5 码,用户提交的数据与数据库中的 md5 数据进行比对查找。

      • 终端命令:

            $ echo -n hello | openssl md5
            $ echo -n hello | openssl sha1
            $ echo -n hello | openssl sha -sha256
            $ echo -n hello | openssl sha -sha512
      • MD5 常用加密方式:

        • 直接使用 MD5 加密:

          • 在 http://cmd5.com 上很容易被破解。
        • MD5 + 盐 加密:

          • 早期方案。关于盐,随机添加的字符串,要够长,够复杂。
          • 在 http://cmd5.com 不易被破解。
        • MD5 + HMAC 加密:

          • HMAC 是一个结合了散列函数的加密算法。给定一个 "密钥",分别作两次加密和散列,密钥强度要求不那么高。国外用的比较多,国内还可以。
          • 在 http://cmd5.com 无法破解。
        • MD5 + HMAC + 时间戳 加密:

          • 相同的加密算法+相同的密码明文,每分钟的结果是不一样的。只有每次都不一样,黑客才不好猜。加时间戳,需要客户端和服务器端采用相同的加密算法。
          • 在 http://cmd5.com 无法破解。

          • 用户注册时发送 pwd.hmac 密码给服务器记录。关于用户第一次的密码安全,被黑客拦截到的几率非常非常低,增加附加安全手段,如 IP 辅助,手机绑定等。

            sign3

          • 用户登录时发送经过时间戳加密的密码给服务器端,服务器端根据用户名从数据库中取出记录的 pwd.hmac 密码,加上当前时间(时分)计算一次时间戳密码,加上当前时间(时(分-1))计算一次时间戳密码,分别与用户发过来的密码进行比对,只要有一个相同,便认为用户是合法的。

            sign4

    • SHA1 :理论上已经被破解

    • SHA256:美国国家安全局、苹果等在使用的

    • SHA512:

5、OpenSSL

  • OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。

  • Mac 系统自带 OpenSSL 环境。

6、钥匙串

  • 钥匙串(英文:Keychain)是苹果公司 Mac OS 中的密码管理系统。它在 Mac OS 8.6 中被导入,并且包括在了所有后续的 Mac OS 版本中,包括 Mac OS X。一个钥匙串可以包含多种类型的数据:密码(包括网站,FTP 服务器,SSH 帐户,网络共享,无线网络,群组软件,加密磁盘镜像等),私钥,电子证书和加密笔记等。
  • 苹果的 "生态圈",钥匙串功能可以协助记忆繁琐的个人账户信息。
  • iCloud 钥匙串使用 AES 256 加密算法,能够保证用户密码的安全。使用的时候,直接传递密码明文即可。
  • 钥匙串访问 SDK,是苹果在 iOS 7.0.3 版本以后公布的。
  • 钥匙串访问的密码保存在哪里?只有苹果知道,是为了进一步保障用户的密码安全。
  • 钥匙串访问的接口是纯 C 语言的,钥匙串访问的第三方框架 SSKeyChain,是对 C 框架的封装,使用相当简单。

7、iOS 上 Base64 编解码

  • 具体实现代码见 GitHub 源码 QExtension

  • NSString+Base64.h

        @interface NSString (Base64)
    
        /**
         *  从 iOS 7.0 开始,apple 提供了 base64 的编码解码的支持。
         */
    
        /**
         *  对 ASCII 编码的字符串进行 base64 编码
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | base64
         *  base64 fileName1 -o fileName2
         *  @endcode
         *
         *  @return base64 编码的字符串
         */
        - (NSString *)q_base64Encode NS_AVAILABLE(10_9, 7_0);
    
        /**
         *  对 base64 编码的字符串进行解码
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | base64 -D
         *  base64 fileName2 -o fileName1 -D
         *  @endcode
         *
         *  @return ASCII 编码的字符串
         */
        - (NSString *)q_base64Decode NS_AVAILABLE(10_9, 7_0);
    
        /**
         *  生成服务器 base64 编码授权字符串
         *
         *  示例代码格式:
         *  @code
         *  输入字符串为 @"username:password" 格式。
         *  [request setValue:[@"username:password" q_basic64AuthEncode]
         *                          forHTTPHeaderField:@"Authorization"];
         *  @endcode
         *
         *  @return @"BASIC (username:password).base64" 格式的字符串
         */
        - (NSString *)q_basic64AuthEncode NS_AVAILABLE(10_9, 7_0);
    
        @end
  • NSString+Base64.m

        @implementation NSString (Base64)
    
        /// 对 ASCII 编码的字符串进行 base64 编码
        - (NSString *)q_base64Encode {
    
            NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
            return [data base64EncodedStringWithOptions:0];
        }
    
        /// 对 base64 编码的字符串进行解码
        - (NSString *)q_base64Decode {
    
            NSData *data = [[NSData alloc] initWithBase64EncodedString:self options:0];
            return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }
    
        /// 生成服务器基本授权字符串
        - (NSString *)q_basic64AuthEncode {
    
            return [@"BASIC " stringByAppendingString:[self q_base64Encode]];
        }
    
        @end
  • ViewController.m

        NSString *str = @"hello world";
    
        // Base64 编码
        NSString *base64Str = [str q_base64Encode];
        NSLog(@"base64Str: %@", base64Str);
    
        // Base64 解码
        NSString *asciiStr = [base64Str q_base64Decode];
        NSLog(@"asciiStr: %@", asciiStr);
    
        // 服务器基本授权字符串编码
        NSString *authStr = [str q_basic64AuthEncode];
        NSLog(@"authStr: %@", authStr);

8、iOS 上 对称加密算法

  • 。。。

9、iOS 上 非对称加密算法

  • 具体实现代码见 GitHub 源码 QExtension

  • 在 iOS 中使用 RSA 加密解密,需要用到 .der 和 .p12 后缀格式的文件,其中 .der 格式的文件存放的是公钥(Public key)用于加密,.p12 格式的文件存放的是私钥(Private key)用于解密. 首先需要先生成这些文件,然后再将文件导入工程使用。

  • 首先按照本文章中的《11.2 生成 x509 格式的证书请求文件》说明生成一组公钥私钥证书文件。

  • 接下来是创建加密解密用的类,並且把刚刚生成的证书文件添加到工程中。

  • QRSAEncryptor.h

        @interface QRSAEncryptor : NSObject
    
        /**
         *  使用 .der 公钥证书文件 加密字符串
         *
         *  @param string   需要加密的字符串
         *  @param path     .der 格式的公钥文件路径
         *
         *  @return 经过 RSA 证书加密的字符串
         */
        + (NSString *)q_encryptWithString:(NSString *)string publicKeyFilePath:(NSString *)path;
    
        /**
         *  使用 .p12 私钥证书文件 解密字符串
         *
         *  @param string       需要解密的字符串
         *  @param path         .p12 格式的私钥文件路径
         *  @param password     私钥文件密码
         *
         *  @return 经过 RSA 证书解密的字符串
         */
        + (NSString *)q_decryptWithString:(NSString *)string privateKeyFilePath:(NSString *)path password:(NSString *)password;
    
        /**
         *  使用 公钥字符串 加密字符串
         *
         *  <p> Xcode8+ 需要在 TARGET -> Capabitilies 中开启 Keychain Sharing 开关 <p>
         *
         *  @param string   需要加密的字符串
         *  @param pubKey   公钥字符串,PKCS#8 格式
         *
         *  @return 经过公钥字符串加密的字符串
         */
        + (NSString *)q_encryptWithString:(NSString *)string publicKey:(NSString *)pubKey;
    
        /**
         *  使用 私钥字符串 解密字符串
         *
         *  <p> Xcode8+ 需要在 TARGET -> Capabitilies 中开启 Keychain Sharing 开关 <p>
         *
         *  @param string       需要解密的字符串
         *  @param privateKey   私钥字符串,PKCS#8 格式
         *
         *  @return 经过私钥字符串解密的字符串
         */
        + (NSString *)q_decryptWithString:(NSString *)string privateKey:(NSString *)privateKey;
    
        @end
  • QRSAEncryptor.m

        #import <Security/Security.h>
    
        @implementation QRSAEncryptor
    
        #pragma mark - 使用 .der 公钥证书文件 加密
    
        /// 使用 .der 公钥证书文件 加密
        + (NSString *)q_encryptWithString:(NSString *)string publicKeyFilePath:(NSString *)path {
    
            if (!string || !path) {
                return nil;
            }
    
            return [self encryptString:string publicKeyRef:[self getPublicKeyRefWithContentsOfFile:path]];
        }
    
        /// 获取公钥
        + (SecKeyRef)getPublicKeyRefWithContentsOfFile:(NSString *)filePath {
    
            NSData *certData = [NSData dataWithContentsOfFile:filePath];
            if (!certData) {
                return nil;
            }
            SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)certData);
            SecKeyRef key = NULL;
            SecTrustRef trust = NULL;
            SecPolicyRef policy = NULL;
            if (cert != NULL) {
                policy = SecPolicyCreateBasicX509();
                if (policy) {
                    if (SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust) == noErr) {
                        SecTrustResultType result;
                        if (SecTrustEvaluate(trust, &result) == noErr) {
                            key = SecTrustCopyPublicKey(trust);
                        }
                    }
                }
            }
            if (policy) CFRelease(policy);
            if (trust) CFRelease(trust);
            if (cert) CFRelease(cert);
            return key;
        }
    
        /// 使用公钥加密字符串
        + (NSString *)encryptString:(NSString *)str publicKeyRef:(SecKeyRef)publicKeyRef {
    
            if (![str dataUsingEncoding:NSUTF8StringEncoding]) {
                return nil;
            }
            if (!publicKeyRef) {
                return nil;
            }
            NSData *data = [self encryptData:[str dataUsingEncoding:NSUTF8StringEncoding] withKeyRef:publicKeyRef];
            NSString *ret = base64_encode_data(data);
            return ret;
        }
    
        #pragma mark - 使用 .p12 私钥证书文件 解密
    
        /// 使用 .p12 私钥证书文件 解密
        + (NSString *)q_decryptWithString:(NSString *)string privateKeyFilePath:(NSString *)path password:(NSString *)password {
    
            if (!string || !path) {
                return nil;
            }
    
            if (!password) {
                password = @"";
            }
    
            return [self decryptString:string privateKeyRef:[self getPrivateKeyRefWithContentsOfFile:path password:password]];
        }
    
        /// 获取私钥
        + (SecKeyRef)getPrivateKeyRefWithContentsOfFile:(NSString *)filePath password:(NSString*)password {
    
            NSData *p12Data = [NSData dataWithContentsOfFile:filePath];
            if (!p12Data) {
                return nil;
            }
            SecKeyRef privateKeyRef = NULL;
            NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
            [options setObject: password forKey:(__bridge id)kSecImportExportPassphrase];
            CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
            OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data, (__bridge CFDictionaryRef)options, &items);
            if (securityError == noErr && CFArrayGetCount(items) > 0) {
                CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
                SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
                securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
                if (securityError != noErr) {
                    privateKeyRef = NULL;
                }
            }
            CFRelease(items);
    
            return privateKeyRef;
        }
    
        /// 使用私钥解密字符串
        + (NSString *)decryptString:(NSString *)str privateKeyRef:(SecKeyRef)privKeyRef {
    
            NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
            if (!privKeyRef) {
                return nil;
            }
            data = [self decryptData:data withKeyRef:privKeyRef];
            NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            return ret;
        }
    
        #pragma mark - 使用 公钥字符串 加密
    
        /// 使用 公钥字符串 加密
        + (NSString *)q_encryptWithString:(NSString *)string publicKey:(NSString *)pubKey {
    
            NSData *data = [self encryptData:[string dataUsingEncoding:NSUTF8StringEncoding] publicKey:pubKey];
            NSString *ret = base64_encode_data(data);
    
            return ret;
        }
    
        /// 使用公钥字符串加密数据
        + (NSData *)encryptData:(NSData *)data publicKey:(NSString *)pubKey {
    
            if (!data || !pubKey) {
                return nil;
            }
            SecKeyRef keyRef = [self addPublicKey:pubKey];
            if (!keyRef) {
                return nil;
            }
            return [self encryptData:data withKeyRef:keyRef];
        }
    
        /// 添加公钥
        + (SecKeyRef)addPublicKey:(NSString *)key {
    
            NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"];
            NSRange epos = [key rangeOfString:@"-----END PUBLIC KEY-----"];
            if (spos.location != NSNotFound && epos.location != NSNotFound) {
                NSUInteger s = spos.location + spos.length;
                NSUInteger e = epos.location;
                NSRange range = NSMakeRange(s, e-s);
                key = [key substringWithRange:range];
            }
            key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
            key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
            key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
            key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
    
            // This will be base64 encoded, decode it.
            NSData *data = base64_decode(key);
            data = [self stripPublicKeyHeader:data];
            if (!data) {
                return nil;
            }
    
            //a tag to read/write keychain storage
            NSString *tag = @"RSAUtil_PubKey";
            NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
            // Delete any old lingering key with the same tag
            NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
            [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
            [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
            [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
            SecItemDelete((__bridge CFDictionaryRef)publicKey);
    
            // Add persistent version of the key to system keychain
            [publicKey setObject:data forKey:(__bridge id)kSecValueData];
            [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)
             kSecAttrKeyClass];
            [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
             kSecReturnPersistentRef];
    
            CFTypeRef persistKey = nil;
            OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
            if (persistKey != nil) {
                CFRelease(persistKey);
            }
            if ((status != noErr) && (status != errSecDuplicateItem)) {
                return nil;
            }
    
            [publicKey removeObjectForKey:(__bridge id)kSecValueData];
            [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
            [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
            [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
            // Now fetch the SecKeyRef version of the key
            SecKeyRef keyRef = nil;
            status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);
            if (status != noErr) {
                return nil;
            }
            return keyRef;
        }
    
        /// 去掉公钥头
        + (NSData *)stripPublicKeyHeader:(NSData *)d_key {
    
            // Skip ASN.1 public key header
            if (d_key == nil) return(nil);
    
            unsigned long len = [d_key length];
            if (!len) return(nil);
    
            unsigned char *c_key = (unsigned char *)[d_key bytes];
            unsigned int  idx     = 0;
    
            if (c_key[idx++] != 0x30) return(nil);
    
            if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
            else idx++;
    
            // PKCS #1 rsaEncryption szOID_RSA_RSA
            static unsigned char seqiod[] =
            { 0x30,   0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
                0x01, 0x05, 0x00 };
            if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
    
            idx += 15;
    
            if (c_key[idx++] != 0x03) return(nil);
    
            if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
            else idx++;
    
            if (c_key[idx++] != '\0') return(nil);
    
            // Now make a new NSData from this buffer
            return ([NSData dataWithBytes:&c_key[idx] length:len - idx]);
        }
    
        #pragma mark - 使用 私钥字符串 解密
    
        /// 使用 私钥字符串 解密
        + (NSString *)q_decryptWithString:(NSString *)string privateKey:(NSString *)privateKey {
    
            if (!string) {
                return nil;
            }
    
            NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:NSDataBase64DecodingIgnoreUnknownCharacters];
            data = [self decryptData:data privateKey:privateKey];
            NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
            return ret;
        }
    
        /// 使用私钥字符串解密数据
        + (NSData *)decryptData:(NSData *)data privateKey:(NSString *)privKey {
    
            if (!data || !privKey) {
                return nil;
            }
            SecKeyRef keyRef = [self addPrivateKey:privKey];
            if (!keyRef) {
                return nil;
            }
            return [self decryptData:data withKeyRef:keyRef];
        }
    
        /// 添加私钥
        + (SecKeyRef)addPrivateKey:(NSString *)key {
    
            NSRange spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"];
            NSRange epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"];
            if (spos.location != NSNotFound && epos.location != NSNotFound) {
                NSUInteger s = spos.location + spos.length;
                NSUInteger e = epos.location;
                NSRange range = NSMakeRange(s, e-s);
                key = [key substringWithRange:range];
            }
            key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
            key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
            key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
            key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
    
            // This will be base64 encoded, decode it.
            NSData *data = base64_decode(key);
            data = [self stripPrivateKeyHeader:data];
            if (!data) {
                return nil;
            }
    
            //a tag to read/write keychain storage
            NSString *tag = @"RSAUtil_PrivKey";
            NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
            // Delete any old lingering key with the same tag
            NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init];
            [privateKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
            [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
            [privateKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
            SecItemDelete((__bridge CFDictionaryRef)privateKey);
    
            // Add persistent version of the key to system keychain
            [privateKey setObject:data forKey:(__bridge id)kSecValueData];
            [privateKey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)
             kSecAttrKeyClass];
            [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
             kSecReturnPersistentRef];
    
            CFTypeRef persistKey = nil;
            OSStatus status = SecItemAdd((__bridge CFDictionaryRef)privateKey, &persistKey);
            if (persistKey != nil){
                CFRelease(persistKey);
            }
            if ((status != noErr) && (status != errSecDuplicateItem)) {
                return nil;
            }
    
            [privateKey removeObjectForKey:(__bridge id)kSecValueData];
            [privateKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
            [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
            [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
            // Now fetch the SecKeyRef version of the key
            SecKeyRef keyRef = nil;
            status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKey, (CFTypeRef *)&keyRef);
            if (status != noErr) {
                return nil;
            }
            return keyRef;
        }
    
        /// 去掉私钥头
        + (NSData *)stripPrivateKeyHeader:(NSData *)d_key {
    
            // Skip ASN.1 private key header
            if (d_key == nil) return(nil);
    
            unsigned long len = [d_key length];
            if (!len) return(nil);
    
            unsigned char *c_key = (unsigned char *)[d_key bytes];
            unsigned int  idx     = 22; //magic byte at offset 22
    
            if (0x04 != c_key[idx++]) return nil;
    
            //calculate length of the key
            unsigned int c_len = c_key[idx++];
            int det = c_len & 0x80;
            if (!det) {
                c_len = c_len & 0x7f;
            } else {
                int byteCount = c_len & 0x7f;
                if (byteCount + idx > len) {
                    //rsa length field longer than buffer
                    return nil;
                }
                unsigned int accum = 0;
                unsigned char *ptr = &c_key[idx];
                idx += byteCount;
                while (byteCount) {
                    accum = (accum << 8) + *ptr;
                    ptr++;
                    byteCount--;
                }
                c_len = accum;
            }
    
            // Now make a new NSData from this buffer
            return [d_key subdataWithRange:NSMakeRange(idx, c_len)];
        }
    
        #pragma mark - 辅助方法
    
        /// 使用公钥加密数据
        + (NSData *)encryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef {
    
            const uint8_t *srcbuf = (const uint8_t *)[data bytes];
            size_t srclen = (size_t)data.length;
    
            size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);
            void *outbuf = malloc(block_size);
            size_t src_block_size = block_size - 11;
    
            NSMutableData *ret = [[NSMutableData alloc] init];
            for (int idx=0; idx<srclen; idx+=src_block_size) {
                //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);
                size_t data_len = srclen - idx;
                if (data_len > src_block_size) {
                    data_len = src_block_size;
                }
    
                size_t outlen = block_size;
                OSStatus status = noErr;
                status = SecKeyEncrypt(keyRef,
                                       kSecPaddingPKCS1,
                                       srcbuf + idx,
                                       data_len,
                                       outbuf,
                                       &outlen
                                       );
                if (status != 0) {
                    NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
                    ret = nil;
                    break;
                } else {
                    [ret appendBytes:outbuf length:outlen];
                }
            }
    
            free(outbuf);
            CFRelease(keyRef);
            return ret;
        }
    
        /// 使用私钥解密数据
        + (NSData *)decryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef {
    
            const uint8_t *srcbuf = (const uint8_t *)[data bytes];
            size_t srclen = (size_t)data.length;
    
            size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);
            UInt8 *outbuf = malloc(block_size);
            size_t src_block_size = block_size;
    
            NSMutableData *ret = [[NSMutableData alloc] init];
            for (int idx=0; idx<srclen; idx+=src_block_size) {
                //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);
                size_t data_len = srclen - idx;
                if(data_len > src_block_size){
                    data_len = src_block_size;
                }
    
                size_t outlen = block_size;
                OSStatus status = noErr;
                status = SecKeyDecrypt(keyRef,
                                       kSecPaddingNone,
                                       srcbuf + idx,
                                       data_len,
                                       outbuf,
                                       &outlen
                                       );
                if (status != 0) {
                    NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
                    ret = nil;
                    break;
                } else {
                    //the actual decrypted data is in the middle, locate it!
                    int idxFirstZero = -1;
                    int idxNextZero = (int)outlen;
                    for ( int i = 0; i < outlen; i++ ) {
                        if ( outbuf[i] == 0 ) {
                            if ( idxFirstZero < 0 ) {
                                idxFirstZero = i;
                            } else {
                                idxNextZero = i;
                                break;
                            }
                        }
                    }
    
                    [ret appendBytes:&outbuf[idxFirstZero+1] length:idxNextZero-idxFirstZero-1];
                }
            }
    
            free(outbuf);
            CFRelease(keyRef);
            return ret;
        }
    
        /// base64 编码
        static NSString * base64_encode_data(NSData *data) {
    
            data = [data base64EncodedDataWithOptions:0];
            NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            return ret;
        }
    
        /// base64 解码
        static NSData * base64_decode(NSString *str) {
    
            NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
            return data;
        }
    
        @end
  • 1、使用秘钥证书文件进行加密解密

    • 使用 .der 和 .p12 秘钥文件进行加密、解密。

          // 原始数据
          NSString *originalString = @"这是一段将要使用 '.der' 文件加密的字符串!";
          NSLog(@"加密前: %@", originalString);
      
          // 秘钥证书文件 .der 和 .p12 路径
          NSString *public_key_path = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];
          NSString *private_key_path = [[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"];
      
          // 加密
          NSString *encryptStr = [QRSAEncryptor q_encryptWithString:originalString publicKeyFilePath:public_key_path];
          NSLog(@"加密后: %@", encryptStr);
      
          // 解密
          NSString *DencryptStr = [QRSAEncryptor q_decryptWithString:encryptStr privateKeyFilePath:private_key_path password:@"qianchia"];
          NSLog(@"解密后: %@", DencryptStr);
    • 效果

      safe20

  • 2、使用秘钥字符串进行加密解密

    • 秘钥字符串可以来这里:http://web.chacuo.net/netrsakeypair, 这是一个在线生成 RSA 秘钥的网站, 生成公钥和秘钥后, 复制出来用于测试。

          // 原始数据
          NSString *originalString = @"这是一段将要使用 '秘钥字符串' 进行加密的字符串!";
          NSLog(@"加密前: %@", originalString);
      
          // 加密
          NSString *publicKeyStr = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDsQ44uzMg83T6z7/dvNn2B1KHlzGwccgo055PeimXdBbzUVBECE0nQeNGb9tkO3mVnu8R4Iu5faoX7MY/muiTVZ3NDAvtk+WBjXfNqHmWvlMfj5jwxnITosnHMLVgrqDFc9q1yfmbTLhd8cJhMXsVBlduCSYbdNitA2z4B3hKS5wIDAQAB";
      
          NSString *encryptStr = [QRSAEncryptor q_encryptWithString:originalString publicKey:publicKeyStr];
          NSLog(@"加密后: %@", encryptStr);
      
          // 解密
          NSString *privateKeyStr = @"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOxDji7MyDzdPrPv9282fYHUoeXMbBxyCjTnk96KZd0FvNRUEQITSdB40Zv22Q7eZWe7xHgi7l9qhfsxj+a6JNVnc0MC+2T5YGNd82oeZa+Ux+PmPDGchOiyccwtWCuoMVz2rXJ+ZtMuF3xwmExexUGV24JJht02K0DbPgHeEpLnAgMBAAECgYB1cuPEihJkh0t7YagsRfdASjatKOD5hwth31kXwM8Af7CuEJhf4rzIALeag6zFgnMAjUwOuLatAiRWif3SIejapMaY/DcXWM/5ugYNi1exS1U8BeBjAOyZuQf/onOn0c0eBqT912CFnjEO5iNuNDkheRQK/FBv2XuMpnAI1FbGQQJBAPmmJkXtEDoM90PxPcL/+ecoNCe2aabiN/D9JlHtOE64DJzRQG4HHpizsvzxMQ00+ItTsG089BjpZPPHuLMO3AcCQQDyRjwPri2lyRC7GHgkgjB03NFL16ENkNER5/7X6TE15uqH/kdrKwrVUNNFwq9a11CHKtJqZOSgy0iN6rKF1JohAkBihAR6d7B9l/xDnYFn4Ce35o+eVEehCYhV2zAyCFC+D7c6cwDf6oNScydg1bUrpwmlwaLPmMwiwIeMA/aJAoYlAkEAoFjJwZsHDTWRBDNCuO8NgRrwzuBs8FyLcu135pCpCELHsLAjtpMrPVmcKwyaIGZnHr7BurcB9kX0xDC0bQzz4QJBAKKCz52lxwWboqo5h4lmk0F3R17O7bUNOaSn1kauX5ADBoQ2zsfl3LrPNB2Tt+97+wRyjnF9Gkwjg2okUc4V1MY=";
      
          NSString *dencryptStr = [QRSAEncryptor q_decryptWithString:encryptStr privateKey:privateKeyStr];
          NSLog(@"解密后: %@", dencryptStr);
    • 效果

      safe21

10、iOS 上 散列算法

  • 具体实现代码见 GitHub 源码 QExtension

  • NSString+Hash.h

        @interface NSString (Hash)
    
        #pragma mark - 散列函数
    
        /**
         *  计算 MD5 散列结果
         *
         *  终端测试命令:
         *  @code
         *  md5 -s "string"
         *  @endcode
         *
         *  <p>提示:随着 MD5 碰撞生成器的出现,MD5 算法不应被用于任何软件完整性检查或代码签名的用途。<p>
         *
         *  @return 32 个字符的 MD5 散列字符串
         */
        - (NSString *)q_md5String;
    
        /**
         *  计算 SHA1 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha1
         *  @endcode
         *
         *  @return 40 个字符的 SHA1 散列字符串
         */
        - (NSString *)q_sha1String;
    
        /**
         *  计算 SHA224 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha224
         *  @endcode
         *
         *  @return 56 个字符的 SHA224 散列字符串
         */
        - (NSString *)q_sha224String;
    
        /**
         *  计算 SHA256 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha256
         *  @endcode
         *
         *  @return 64 个字符的 SHA256 散列字符串
         */
        - (NSString *)q_sha256String;
    
        /**
         *  计算 SHA384 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha384
         *  @endcode
         *
         *  @return 96 个字符的 SHA384 散列字符串
         */
        - (NSString *)q_sha384String;
    
        /**
         *  计算 SHA512 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha512
         *  @endcode
         *
         *  @return 128 个字符的 SHA512 散列字符串
         */
        - (NSString *)q_sha512String;
    
        #pragma mark - HMAC 散列函数
    
        /**
         *  计算 HMAC MD5 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl dgst -md5 -hmac "key"
         *  @endcode
         *
         *  @return 32 个字符的 HMAC MD5 散列字符串
         */
        - (NSString *)q_hmacMD5StringWithKey:(NSString *)key;
    
        /**
         *  计算 HMAC SHA1 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha1 -hmac "key"
         *  @endcode
         *
         *  @return 40 个字符的 HMAC SHA1 散列字符串
         */
        - (NSString *)q_hmacSHA1StringWithKey:(NSString *)key;
    
        /**
         *  计算 HMAC SHA224 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha224 -hmac "key"
         *  @endcode
         *
         *  @return 56 个字符的 HMAC SHA224 散列字符串
         */
        - (NSString *)q_hmacSHA224StringWithKey:(NSString *)key;
    
        /**
         *  计算 HMAC SHA256 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha256 -hmac "key"
         *  @endcode
         *
         *  @return 64 个字符的 HMAC SHA256 散列字符串
         */
        - (NSString *)q_hmacSHA256StringWithKey:(NSString *)key;
    
        /**
         *  计算 HMAC SHA384 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha384 -hmac "key"
         *  @endcode
         *
         *  @return 96 个字符的 HMAC SHA384 散列字符串
         */
        - (NSString *)q_hmacSHA384StringWithKey:(NSString *)key;
    
        /**
         *  计算 HMAC SHA512 散列结果
         *
         *  终端测试命令:
         *  @code
         *  echo -n "string" | openssl sha -sha512 -hmac "key"
         *  @endcode
         *
         *  @return 128 个字符的 HMAC SHA512 散列字符串
         */
        - (NSString *)q_hmacSHA512StringWithKey:(NSString *)key;
    
        #pragma mark - 时间戳散列函数
    
        /**
         *  计算时间戳的 HMAC 散列结果
         *
         *  <p>提示:同样的密码,同样的加密算法,每分钟加密的结果都不一样。<p>
         *
         *  @param key 秘钥
         *
         *  @return 32 个字符的 HMAC 散列字符串
         */
        - (NSString *)q_timeMD5StringWithKey:(NSString *)key;
    
        #pragma mark - 文件散列函数
    
        /**
         *  计算文件的 MD5 散列结果
         *
         *  终端测试命令:
         *  @code
         *  md5 file.dat
         *  @endcode
         *
         *  @return 32 个字符的 MD5 散列字符串
         */
        - (NSString *)q_fileMD5Hash;
    
        /**
         *  计算文件的 SHA1 散列结果
         *
         *  终端测试命令:
         *  @code
         *  openssl sha -sha1 file.dat
         *  @endcode
         *
         *  @return 40 个字符的 SHA1 散列字符串
         */
        - (NSString *)q_fileSHA1Hash;
    
        /**
         *  计算文件的 SHA256 散列结果
         *
         *  终端测试命令:
         *  @code
         *  openssl sha -sha256 file.dat
         *  @endcode
         *
         *  @return 64 个字符的 SHA256 散列字符串
         */
        - (NSString *)q_fileSHA256Hash;
    
        /**
         *  计算文件的 SHA512 散列结果
         *
         *  终端测试命令:
         *  @code
         *  openssl sha -sha512 file.dat
         *  @endcode
         *
         *  @return 128 个字符的 SHA512 散列字符串
         */
        - (NSString *)q_fileSHA512Hash;
    
        @end
  • NSString+Hash.m

        #import <CommonCrypto/CommonCrypto.h>
    
        @implementation NSString (Hash)
    
        #pragma mark - 散列函数
    
        - (NSString *)q_md5String {
            const char *str = self.UTF8String;
            uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    
            CC_MD5(str, (CC_LONG)strlen(str), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
        }
    
        - (NSString *)q_sha1String {
            const char *str = self.UTF8String;
            uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
    
            CC_SHA1(str, (CC_LONG)strlen(str), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
        }
    
        - (NSString *)q_sha224String {
            const char *str = self.UTF8String;
            uint8_t buffer[CC_SHA224_DIGEST_LENGTH];
    
            CC_SHA224(str, (CC_LONG)strlen(str), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA224_DIGEST_LENGTH];
        }
    
        - (NSString *)q_sha256String {
            const char *str = self.UTF8String;
            uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
    
            CC_SHA256(str, (CC_LONG)strlen(str), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
        }
    
        - (NSString *)q_sha384String {
            const char *str = self.UTF8String;
            uint8_t buffer[CC_SHA384_DIGEST_LENGTH];
    
            CC_SHA384(str, (CC_LONG)strlen(str), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA384_DIGEST_LENGTH];
        }
    
        - (NSString *)q_sha512String {
            const char *str = self.UTF8String;
            uint8_t buffer[CC_SHA512_DIGEST_LENGTH];
    
            CC_SHA512(str, (CC_LONG)strlen(str), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
        }
    
        #pragma mark - HMAC 散列函数
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
        }
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
        }
    
        - (NSString *)q_hmacSHA224StringWithKey:(NSString *)key {
            const char *keyData = key.UTF8String;
            const char *strData = self.UTF8String;
            uint8_t buffer[CC_SHA224_DIGEST_LENGTH];
    
            CCHmac(kCCHmacAlgSHA224, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA224_DIGEST_LENGTH];
        }
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
        }
    
        - (NSString *)q_hmacSHA384StringWithKey:(NSString *)key {
            const char *keyData = key.UTF8String;
            const char *strData = self.UTF8String;
            uint8_t buffer[CC_SHA384_DIGEST_LENGTH];
    
            CCHmac(kCCHmacAlgSHA384, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
            return [self q_stringFromBytes:buffer length:CC_SHA384_DIGEST_LENGTH];
        }
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
        }
    
        #pragma mark - 时间戳散列函数
    
        - (NSString *)q_timeMD5StringWithKey:(NSString *)key {
            NSString *hmacKey = key.q_md5String;
            NSString *hmacStr = [self q_hmacMD5StringWithKey:hmacKey];
    
            NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
            fmt.dateFormat = @"yyyy-MM-ddHH:mm";
            NSString *dateStr = [fmt stringFromDate:[NSDate date]];
    
            hmacStr = [hmacStr stringByAppendingString:dateStr];
    
            return [hmacStr q_hmacMD5StringWithKey:hmacKey];
        }
    
        #pragma mark - 文件散列函数
    
        #define FileHashDefaultChunkSizeForReadingData 4096
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
        }
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
        }
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
        }
    
        - (NSString *)q_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 q_stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
        }
    
        #pragma mark - 助手方法
    
        /**
         *  返回二进制 Bytes 流的字符串表示形式
         *
         *  @param bytes  二进制 Bytes 数组
         *  @param length 数组长度
         *
         *  @return 字符串表示形式
         */
        - (NSString *)q_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
  • ViewController.m

        NSString *str = @"hello world";
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Info.plist" ofType:nil];
    
        // 散列
    
        NSString *md5Str = [str q_md5String];
        NSLog(@"md5Str: %@", md5Str);
    
        NSString *sha1Str = [str q_sha1String];
        NSLog(@"sha1Str: %@", sha1Str);
    
        NSString *sha224Str = [str q_sha224String];
        NSLog(@"sha224Str: %@", sha224Str);
    
        NSString *sha256Str = [str q_sha256String];
        NSLog(@"sha256Str: %@", sha256Str);
    
        NSString *sha384Str = [str q_sha384String];
        NSLog(@"sha384Str: %@", sha384Str);
    
        NSString *sha512Str = [str q_sha512String];
        NSLog(@"sha512Str: %@\n\n", sha512Str);
    
        // hmac 散列
    
        NSString *hmacMD5Str = [str q_hmacMD5StringWithKey:@"yourKey"];
        NSLog(@"hmacMD5Str: %@", hmacMD5Str);
    
        NSString *hmacSHA1Str = [str q_hmacSHA1StringWithKey:@"yourKey"];
        NSLog(@"hmacSHA1Str: %@", hmacSHA1Str);
    
        NSString *hmacSHA224Str = [str q_hmacSHA224StringWithKey:@"yourKey"];
        NSLog(@"hmacSHA224Str: %@", hmacSHA224Str);
    
        NSString *hmacSHA256Str = [str q_hmacSHA256StringWithKey:@"yourKey"];
        NSLog(@"hmacSHA256Str: %@", hmacSHA256Str);
    
        NSString *hmacSHA384Str = [str q_hmacSHA384StringWithKey:@"yourKey"];
        NSLog(@"hmacSHA384Str: %@", hmacSHA384Str);
    
        NSString *hmacSHA512Str = [str q_hmacSHA512StringWithKey:@"yourKey"];
        NSLog(@"hmacSHA512Str: %@\n\n", hmacSHA512Str);
    
        // 时间戳 MD5 散列
    
        NSString *timeStr = [str q_timeMD5StringWithKey:@"yourKey"];
        NSLog(@"timeStr: %@\n\n", timeStr);
    
        // 文件 散列
    
        NSString *fileMD5Str = [filePath q_fileMD5Hash];
        NSLog(@"fileMD5Str: %@", fileMD5Str);
    
        NSString *fileSHA1Str = [filePath q_fileSHA1Hash];
        NSLog(@"fileSHA1Str: %@", fileSHA1Str);
    
        NSString *fileSHA256Str = [filePath q_fileSHA256Hash];
        NSLog(@"fileSHA256Str: %@", fileSHA256Str);
    
        NSString *fileSHA512Str = [filePath q_fileSHA512Hash];
        NSLog(@"fileSHA512Str: %@", fileSHA512Str);

11、iOS 上 OpenSSL

11.1 OpenSSL 生成公钥和私钥

  • 1、进入 OpenSSL 环境

    • Mac 自带 OpenSSL,所以我们不需要自己装 OpenSSL,直接打开终端,输入如下命令,回车。

          $ OpenSSL

      safe5

  • 2、创建私钥

    • 输入如下命令,rsa_private_key.pem 为私钥文件名,1024 为密钥长度,觉得不够安全的话可以用 2048,但是代价也相应增大。

          # genrsa -out 输出私钥文件名.文件格式 密钥长度
          $ genrsa -out rsa_private_key.pem 1024

      safe6

  • 3、将把 RSA 私钥转换成 PKCS8 格式

    • 输入如下命令,PEM 为输出的文件格式,这一步会提示给私钥文件设置密码,直接输入想要设置密码即可,然后敲回车,然后再验证刚才设置的密码,再次输入密码,然后敲回车,完毕。在解密时,私钥文件需要和这里设置的密码配合使用,因此需要牢记此密码。

          # pkcs8 -topk8 -inform 输入文件格式 -in 要转换的文件名.文件格式 -outform 输出文件格式 –nocrypt
          $ pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt

      safe7

  • 4、生成公钥

    • 输入如下命令。rsa_private_key.pem 为私钥文件名,rsa_public_key.pem 为公钥文件名。输出 writing RSA key 表示生成公钥成功。

          # rsa -in 私钥文件名.文件格式 -pubout -out 公钥文件名.文件格式 
          $ rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

      safe8

  • 5、退出

    • 输入如下命令退出 OpenSSL 环境,或者直接退出终端。

          $ exit ##

      safe9

  • 6、导出生成的私钥和公钥文件

    • 打开系统下的 电脑用户名 文件夹,即可看到名为 rsa_private_key.pemrsa_public_key.pem 两个文件,将文件拖拽到桌面或者你指定的文件夹内使用即可。

      safe10

11.2 生成 x509 格式的证书请求文件

  • 生成过程中会要你輸入一些地理位置信息,可以不填。如果没问题的话,在 电脑用户名 文件夹下会产生两个文件 private_key.pempublic_key.der,这两个分别是我们的私钥和公钥证书文件。

        # openssl req -证书格式 -out 公钥文件名.文件格式 -outform 输出文件格式 -new -newkey rsa:密钥长度 -keyout 私钥文件名.文件格式 -days 有效时间 -nodes
        $ openssl req -x509 -out public_key.der -outform der -new -newkey rsa:2048 -keyout private_key.pem -days 3650 -nodes
  • 分开写步骤如下,生成环境是在 Mac 系统下,使用 openssl 进行生成,首先打开终端,按下面这些步骤依次来做。

  • 1、生成模长为 1024bit 的私钥文件 private_key.pem

        # openssl genrsa -out 输出的私钥文件名.文件格式 密钥长度
        $ openssl genrsa -out private_key.pem 1024

    safe11

  • 2、生成证书请求文件 rsaCertReq.csr

        # openssl req -new -key 私钥文件名.文件格式 -out 输出的证书请求文件.文件格式
        $ openssl req -new -key private_key.pem -out rsaCerReq.csr

    safe12

    • 注意:这一步会提示输入国家、省份、mail 等信息,可以根据实际情况填写,或者全部不用填写,直接全部敲回车。
  • 3、生成证书 rsaCert.crt,并设置有效时间为 10 年

        # openssl 证书格式 -req -days 有效时间 -in 证书请求文件.文件格式 -signkey 私钥文件名.文件格式 -out 输出的证书文件.文件格式
        $ openssl x509 -req -days 3650 -in rsaCerReq.csr -signkey private_key.pem -out rsaCert.crt

    safe13

  • 4、生成供 iOS 使用的公钥证书文件 public_key.der

        # openssl 证书格式 -outform 输出文件格式 -in 证书文件.文件格式 -out 输出的公钥证书文件名.文件格式
        $ openssl x509 -outform der -in rsaCert.crt -out public_key.der

    safe14

  • 5、生成供 iOS 使用的私钥证书文件 private_key.p12

        # openssl 输出文件格式 -export -out 输出的私钥证书文件名.文件格式 -inkey 私钥文件名.文件格式 -in 证书文件.文件格式
        $ openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt

    safe15

    • 注意:这一步会提示给私钥文件设置密码,直接输入想要设置密码即可,然后敲回车,然后再验证刚才设置的密码,再次输入密码,然后敲回车,完毕。在解密时,private_key.p12 文件需要和这里设置的密码配合使用,因此需要牢记此密码。
  • 6、生成供 Java 使用的公钥证书文件 rsa_public_key.pem

        # openssl rsa -in 私钥文件名.文件格式 -out 输出的公钥证书文件名.文件格式 -pubout
        $ openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout

    safe16

  • 7、生成供 Java 使用的私钥证书文件 pkcs8_private_key.pem

        # openssl 输出文件格式 -topk8 -in 私钥文件名.文件格式 -out 输出的私钥证书文件名.文件格式 -nocrypt
        $ openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt

    safe17

  • 全部执行成功后,会生成如下文件,其中 public_key.derprivate_key.p12 就是 iOS 需要用到的文件,如下图。

    safe18

12、iOS 上 钥匙串

  • GitHub 封装 SSKeychain/SAMKeychain

  • Objective-C

        // 添加第三方库文件
        SSKeychain
    
        // 包含头文件
        #import "SSKeychain.h"
    
        // 获取所有的账户信息
    
            // 只有同一个开发者开发的应用程序,才能够互相看到账号
            NSArray *allAccounts = [SSKeychain allAccounts];
    
        // 获取指定服务名的账户信息
    
            NSArray *accounts = [SSKeychain accountsForService:[NSBundle mainBundle].bundleIdentifier];
    
        // 将密码保存到钥匙串中
        /*
            + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
    
            参数:
                password   :密码明文
                serviceName:服务名,可以随便写,建议使用 bundleId,应用程序的唯一标示,每一个上架的应用程序,都有一个唯一的 bundleId
                account    :账户名(用户名)
        */
    
            // 保存 NSString 格式的密码
            [SSKeychain setPassword:self.pwdText.text 
                         forService:[NSBundle mainBundle].bundleIdentifier 
                            account:self.usernameText.text];    
    
            NSData *pwdData = [self.pwdText.text dataUsingEncoding:NSUTF8StringEncoding];
    
            // 保存 NSData 格式的密码
            [SSKeychain setPasswordData:pwdData 
                             forService:[NSBundle mainBundle].bundleIdentifier 
                                account:self.usernameText.text];          
    
        // 将密码从钥匙串中取出
    
            // 获取 NSString 格式的密码
            self.pwdText.text = [SSKeychain passwordForService:[NSBundle mainBundle].bundleIdentifier 
                                                       account:self.usernameText.text];
    
            // 获取 NSData 格式的密码
            NSData *pwdData1 = [SSKeychain passwordDataForService:[NSBundle mainBundle].bundleIdentifier 
                                                          account:self.usernameText.text];   
    
        // 将密码从钥匙串中删除
    
            [SSKeychain deletePasswordForService:[NSBundle mainBundle].bundleIdentifier 
                                         account:self.usernameText.text];
posted @ 2016-09-09 17:47  QianChia  阅读(...)  评论(...编辑  收藏