iOS客户端加密笔记

原理

类似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

终端提示如下:

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


    iOS客户端加密笔记_第1张图片
    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客户端加密笔记)