Safe 加密安全

1、Base64 编码

  • 简介:

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

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

  • 缺点:

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

    1
    2
    3
    4
    5
    $ 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 数据进行比对查找。

      • 终端命令:

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

        • 直接使用 MD5 加密:

        • MD5 + 盐 加密:

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

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

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

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

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

    • 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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    @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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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 上 对称加密算法

null

9、iOS 上 非对称加密算法

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

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

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

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

  • QRSAEncryptor.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    @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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    #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 秘钥文件进行加密、解密。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // 原始数据
      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);
    • 效果

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

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // 原始数据
      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);
    • 效果

10、iOS 上 散列算法

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

  • NSString+Hash.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    @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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    #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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    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,直接打开终端,输入如下命令,回车。

      1
      $ OpenSSL

  • 2、创建私钥

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

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

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

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

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

  • 4、生成公钥

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

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

  • 5、退出

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

      1
      $ exit ##

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

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

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

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

    1
    2
    # 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

12、iOS 上 钥匙串

  • GitHub 封装 SSKeychain/SAMKeychain

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    // 添加第三方库文件
    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];
文章目录
  1. 1. 1、Base64 编码
  2. 2. 2、对称算法
  3. 3. 3、非对称算法
  4. 4. 4、散列算法
  5. 5. 5、OpenSSL
  6. 6. 6、钥匙串
  7. 7. 7、iOS 上 Base64 编解码
  8. 8. 8、iOS 上 对称加密算法
  9. 9. 9、iOS 上 非对称加密算法
  10. 10. 10、iOS 上 散列算法
  11. 11. 11、iOS 上 OpenSSL
    1. 11.1. 11.1 OpenSSL 生成公钥和私钥
    2. 11.2. 11.2 生成 x509 格式的证书请求文件
  12. 12. 12、iOS 上 钥匙串
隐藏目录