美文网首页
iOS客户端加密笔记

iOS客户端加密笔记

作者: 酱油瓶2 | 来源:发表于2018-07-31 16:58 被阅读29次

    原理

    类似HTTPS协议传输原理,客户端用RSA的公钥加密AES的密钥,服务端用私钥解开获得的AES的密钥,客户端再与服务端进行AES加密的数据传输。

    RSA算法原理:

    1. 找出两个“很大”的质数:P & Q(上百位)
      N = P * Q
      M = (P – 1) * (Q – 1)
    2. 找出整数E,E与M互质,即除了1之外,没有其他公约数
    3. 找出整数D,使得 ED 除以 M 余 1,即 (E * D) % M = 1
    4. 经过上述准备工作之后,可以得到:E是公钥,负责加密D是私钥,负责解密N负责公钥和私钥之间的联系
    5. 加密算法,假定对X进行加密(X ^ E) % N = Y(6)解密算法,根据费尔马小定义,可以使用以下公式完成解密(Y ^ D) % N = X

    公钥、私钥生成

    公钥:就是签名机构签完给我们颁发的,放在网站的根目录上,可以分发
    私钥:一般保存在中心服务器

    加密解密使用了两种文件 .p12是私钥 .der是公钥,终端命令生成步骤如下:
    1. 创建私钥,生成安全强度是512(也可以是1024)的RAS私钥,.pem是base64的证书文件
      openssl genrsa -out private.pem 512
    2. 生成一个证书请求,生成证书请求文件.csr
      openssl req -new -key private.pem -out rsacert.csr

    终端提示如下:

    • 国家名字、代码
    • 省的名字
    • 城市的名字
    • 公司的名字
    • 公司的单位
    • 我的名字
    • 电子邮件
    • 以及两个附加信息可以跳过


      image
    1. 签名,找证书颁发机构签名,证明证书合法有效的,也可以自签名一个证书
      生成证书并签名,有效期10年,生成一个.crt的一个base64公钥文件
      openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
      由于iOS开发时使用的时候不能是base64的,必须解成二进制文件!

    2. 解成.der公钥二进制文件,放程序做加密用
      openssl x509 -outform der -in rsacert.crt -out rsacert.der

    3. 生成.p12二进制私钥文件
      .pem 是base64的不能直接使用,必须导成.p12信息交换文件用来传递秘钥
      openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
      输入一个导出密码(框架中loadPrivateKey:方法的password参数需要用的密码):

      image

    加密过程

    1. 加载RSA公钥
      NSString *pubPath = [[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil];
      ZDCryptorTools *tools = [[ZDCryptorTools alloc] init]; [tools loadPublicKeyWithFilePath:pubPath];
    #pragma mark - RSA 加密/解密算法
    + (void)loadPublicKeyWithFilePath:(NSString *)filePath; {
        
        NSAssert(filePath.length != 0, @"公钥路径为空");
        
        // 删除当前公钥
        if (_publicKeyRef) CFRelease(_publicKeyRef);
        
        // 从一个 DER 表示的证书创建一个证书对象
        NSData *certificateData = [NSData dataWithContentsOfFile:filePath];
        SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
        NSAssert(certificateRef != NULL, @"公钥文件错误");
        
        // 返回一个默认 X509 策略的公钥对象,使用之后需要调用 CFRelease 释放
        SecPolicyRef policyRef = SecPolicyCreateBasicX509();
        // 包含信任管理信息的结构体
        SecTrustRef trustRef;
        
        // 基于证书和策略创建一个信任管理对象
        OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
        NSAssert(status == errSecSuccess, @"创建信任管理对象失败");
        
        // 信任结果
        SecTrustResultType trustResult;
        // 评估指定证书和策略的信任管理是否有效
        status = SecTrustEvaluate(trustRef, &trustResult);
        NSAssert(status == errSecSuccess, @"信任评估失败");
        
        // 评估之后返回公钥子证书
        _publicKeyRef = SecTrustCopyPublicKey(trustRef);
        NSAssert(_publicKeyRef != NULL, @"公钥创建失败");
        
        if (certificateRef) CFRelease(certificateRef);
        if (policyRef) CFRelease(policyRef);
        if (trustRef) CFRelease(trustRef);
    }
    

    2、生成16位随机字符串,作为AES的密钥
    NSString *signKey = [ZDCryptorTools randomStringWithLength:16];

    ///生成长度为len的随机字符串
    - (NSString *)randomStringWithLength:(NSInteger)len {
        NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=";
        NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
        
        for (NSInteger i = 0; i < len; i++) {
            [randomString appendFormat: @"%C", [letters characterAtIndex: arc4random_uniform((int)[letters length])]];
        }
        return randomString;
    }
    

    3、使用RSA公钥加密AES的密钥,加密内容最大长度 117
    NSString *result = [tools RSAEncryptString:signKey];

    - (NSString *)RSAEncryptString:(NSString *)string {
        NSData *cipher = [self RSAEncryptData:[string dataUsingEncoding:NSUTF8StringEncoding]];
        
        return [cipher base64EncodedStringWithOptions:0];
    }
    

    4、使用AES密钥加密参数

    NSString *aesString = [XHCryptorTools AESEncryptString:params keyString:signKey iv:[@"1234567812345678" dataUsingEncoding:NSUTF8StringEncoding]];

    + (NSString *)AESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
        NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
        NSData *result = [self AESEncryptData:data keyString:keyString iv:iv];
        
        // BASE 64 编码
        return [result base64EncodedStringWithOptions:0];
    }
    
    + (NSData *)AESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv {
        return [self CCCryptData:data algorithm:kCCAlgorithmAES operation:kCCEncrypt keyString:keyString iv:iv];
    }
    
    + (NSData *)CCCryptData:(NSData *)data algorithm:(CCAlgorithm)algorithm operation:(CCOperation)operation keyString:(NSString *)keyString iv:(NSData *)iv {
        
        int keySize = (algorithm == kCCAlgorithmAES) ? kCCKeySizeAES128 : kCCKeySizeDES;
        int blockSize = (algorithm == kCCAlgorithmAES) ? kCCBlockSizeAES128: kCCBlockSizeDES;
        
        // 设置密钥
        NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
        uint8_t cKey[keySize];
        bzero(cKey, sizeof(cKey));
        [keyData getBytes:cKey length:keySize];
        
        // 设置 IV 向量
        uint8_t cIv[blockSize];
        bzero(cIv, blockSize);
        int option = kCCOptionPKCS7Padding | kCCOptionECBMode;
        if (iv) {
            [iv getBytes:cIv length:blockSize];
            option = kCCOptionPKCS7Padding;
        }
        
        // 设置输出缓冲区
        size_t bufferSize = [data length] + blockSize;
        void *buffer = malloc(bufferSize);
        
        // 加密或解密
        size_t cryptorSize = 0;
        CCCryptorStatus cryptStatus = CCCrypt(operation,
                                              algorithm,
                                              option,
                                              cKey,
                                              keySize,
                                              cIv,
                                              [data bytes],
                                              [data length],
                                              buffer,
                                              bufferSize,
                                              &cryptorSize);
        
        NSData *result = nil;
        if (cryptStatus == kCCSuccess) {
            result = [NSData dataWithBytesNoCopy:buffer length:cryptorSize];
        } else {
            free(buffer);
            NSLog(@"[错误] 加密或解密失败 | 状态编码: %d", cryptStatus);
        }
        
        return result;
    }
    

    5、请求接口数据
    6、AES解密接口返回的数据
    NSData *jsonData = [ZDCryptorTools AESDecryptString:responseObject[@"data"] keyString:signKey iv:[@"1234567812345678" dataUsingEncoding:NSUTF8StringEncoding]];

    + (NSData *)AESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
        // BASE 64 解码
        NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
        NSData *result = [self AESDecryptData:data keyString:keyString iv:iv];
        
        return result;
    }
    
    + (NSData *)AESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv {
        return [self CCCryptData:data algorithm:kCCAlgorithmAES operation:kCCDecrypt keyString:keyString iv:iv];
    }
    

    相关文章

      网友评论

          本文标题:iOS客户端加密笔记

          本文链接:https://www.haomeiwen.com/subject/auhhvftx.html