iOS加密

作者: 求长生 | 来源:发表于2020-05-27 16:58 被阅读0次

iOS代码常见的加密方式包括MD5加密、AES加密、BASE64加密,RSA加密。

MD5加密

MD5是不可逆的只有加密没有解密。
因为它可以将任意长度的输入串经过计算得到固定长度的输出,而且只有在明文相同的情况下,才能等到相同的密文,并且这个算法是不可逆的,即便得到了加密以后的密文,也不可能通过解密算法反算出明文。这样就可以把用户的密码以MD5值(或类似的其它算法)的方式保存起来,用户注册的时候,系统是把用户输入的密码计算成 MD5 值,然后再去和系统中保存的 MD5 值进行比较,如果密文相同,就可以认定密码是正确的,否则密码错误。通过这样的步骤,系统在并不知道用户密码明码的情况下就可以确定用户登录系统的合法性。

#import <CommonCrypto/CommonDigest.h>
#define CC_MD5_DIGEST_LENGTH    16          /* digest length in bytes */

#pragma mark - MD5 签名加密
+ (NSString *) md5:(NSString *) input
{
// 1.首先将字符串转换成UTF-8编码,因为MD5加密是基于C语言的,所以要先把字符串转化成C语言的字符串。
    const char *cStr = [input UTF8String];
// 2.然后创建一个字符串数组,接收MD5的值,16位的值。
    unsigned char digest[CC_MD5_DIGEST_LENGTH];
// 3.计算MD5的值,这是官方封装好的加密方法:把我们输入的字符串转换成16进制的32位数,然后存储到digest中。第一个参数:要加密的字符串,第二个参数: 获取要加密字符串的长度,第三个参数: 接收结果的数组。
    CC_MD5( cStr, (unsigned)strlen(cStr), digest );
// 4.创建一个字符串保存加密结果,返回的是32位的加密字符串。
    NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
// 5.从digest 数组中获取加密结果并放到 output中,x表示十六进制,%02X  意思是不足两位将用0补齐,如果多余两位则不影响。
    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [output appendFormat:@"%02x", digest[i]];
    }
    return  output;
}

AES加密对称加密

RSA非对称加密。

RSA 是一种非对称加密算法,常用来对传输数据进行加密,配合上数字摘要算法,也可以进行文字签名。

RSA加密中padding

padding即填充方式,由于RSA加密算法中要加密的明文是要比模数小的,padding就是通过一些填充方式来限制明文的长度。后面会详细介绍padding的几种模式以及分段加密。

加密和加签有什么区别

  • 加密:公钥放在客户端,并使用公钥对数据进行加密,服务端拿到数据后用私钥进行解密。

  • 加签:私钥放在客户端,并使用私钥对数据进行加签,服务端拿到数据后用公钥进行验签。

前者完全为了加密;后者主要是为了防恶意攻击,防止别人模拟我们的客户端对我们的服务器进行攻击,导致服务器瘫痪。

基本原理

RSA使用“密钥对”对数据进行加密解密,在加密解密前需要先生存公钥(Public Key)和私钥(Private Key)。

  • 公钥(Public key)

    用于加密数据. 用于公开, 一般存放在数据提供方, 例如iOS客户端。

  • 私钥(Private key)

    用于解密数据. 必须保密, 私钥泄露会造成安全问题。

iOS中的Security.framework提供了对RSA算法的支持,这种方式需要对密匙对进行处理, 根据public key生成证书, 通过private key生成p12格式的密匙。

RSA密钥生成过程

  • 生成模长为1024bit的私钥

    $ openssl genrsa -out private_key.pem 1024
    
    • openssl:是一个自由的软件组织,专注做加密和解密的框架。
    • genrsa:指定了生成了算法使用RSA。
    • out:后面的参数表示生成的key的输入文件。
    • 1024:表示的是生成key的长度,单位字节(bits)。
  • 创建证书请求

    $ openssl req -new -key private_key.pem -out rsaCertReq.cs
    

    可以拿着这个文件去数字证书颁发机构(即CA)申请一个数字证书。CA会给你一个新的文件cacert.pem,那才是你的数字证书。(要收费的)

  • 生成证书并签名,有效期10年(生成certification 并指定过期时间)

    $ openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt
    

    509是一种非常通用的证书格式,将用上面生成的密钥privkey_key.pem和rsaCertReq.csr证书请求文件生成一个数字证书rsaCert.crt。这个就是公钥。

  • 转换格式 将PEM格式文件转换成DER格式(生成公钥供iOS使用)

    $ openssl x509 -outform der -in rsaCert.crt -out public_key.der
    

    在 iOS开发中,公钥是不能使用base64编码的,上面的命令是将公钥的base64编码字符串转换成二进制数据。

  • 导出 P12 文件(生成私钥供iOS使用 这边会让你输入密码,后期用到在生成secKeyRef的时候会用到这个密码)

    $ openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt
    

    在iOS使用私钥不能直接使用,需要导出一个p12文件。上面命令就是将私钥文件导出为p12文件。

  • 生成pem结尾的公钥

    $ openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout
    
  • 生成pem结尾的私钥

    $ openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
    

    执行完上面的这些,我们现在就得到了四个文件

    image

    把 rsacert.der 和 p.p12 拖进你的Xcode项目里去,然后,我们用这两个文件来使用一下。

生成公钥和私钥的secKeyRef

//根据你的p12文件生成私钥对应的SecKeyRef 这边返回若是nil 请检查你p12文件的生成步骤
- (SecKeyRef)getPrivateKeyRefrenceFromData:(NSData*)p12Data password:(NSString*)password {
    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;
}
//根据你的der文件公钥对应的SecKeyRef
- (SecKeyRef)getPublicKeyRefrenceFromeData:(NSData*)derData {
    
    SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)derData);
    SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
    SecTrustRef myTrust;
    OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
    SecTrustResultType trustResult;
    if (status == noErr) {
        status = SecTrustEvaluate(myTrust, &trustResult);
    }
    SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);
    CFRelease(myCertificate);
    CFRelease(myPolicy);
    CFRelease(myTrust);
    return securityKey;
}

加密和解密

- (NSData*)rsaEncryptData:(NSData*)data usingPublicKey:(SecKeyRef)key {
    
    size_t cipherBufferSize = SecKeyGetBlockSize(key);
    uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    size_t blockSize = cipherBufferSize - 11;
    size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
    NSMutableData *encryptedData = [[NSMutableData alloc] init];
    for (int i=0; i<blockCount; i++) {
        unsigned long bufferSize = MIN(blockSize , [data length] - i * blockSize);
        NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
        OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes], [buffer length], cipherBuffer, &cipherBufferSize);
        
        if (status != noErr) {
            return nil;
        }
        
        NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
        [encryptedData appendData:encryptedBytes];
    }
    
    if (cipherBuffer){
        free(cipherBuffer);
    }
    return encryptedData;
}

- (NSData*)rsaDecryptData:(NSData*)data usingPrivatKey:(SecKeyRef)key {
    size_t cipherBufferSize =   SecKeyGetBlockSize(key);
    size_t blockSize = cipherBufferSize;
    size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
    NSMutableData *decryptedData = [[NSMutableData alloc] init];
    for (int i = 0; i < blockCount; i++) {
        unsigned long bufferSize = MIN(blockSize , [data length] - i * blockSize);
        NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
        size_t cipherLen = [buffer length];
        void *cipher = malloc(cipherLen);
        [buffer getBytes:cipher length:cipherLen];
        size_t plainLen = SecKeyGetBlockSize(key);
        void *plain = malloc(plainLen);
        
        OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen);
        
        if (status != noErr) {
            return nil;
        }
        
        NSData *decryptedBytes = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen];
        [decryptedData appendData:decryptedBytes];
    }
    
    return decryptedData;
}

RSA加密中的Padding

  • RSA_PKCS1_PADDING 填充模式,最常用的模式

    输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11 如果输入的明文过长,必须切割,然后填充。

    输出:和modulus一样长

    根据这个要求,对于1024bit的密钥,block length = 1024/8 – 11 = 117 字节

  • RSA_PKCS1_OAEP_PADDING

    输入:RSA_size(rsa) – 41

    输出:和modulus一样长

  • RSA_NO_PADDING  不填充

    输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割,然后填充。

    输出:和modulus一样长。

签名与验证

//对数据进行sha256签名
- (NSData *)rsaSHA256SignData:(NSData *)plainData usingPrivatKey:(SecKeyRef)key {
    
    size_t signedHashBytesSize = SecKeyGetBlockSize(key);
    uint8_t* signedHashBytes = malloc(signedHashBytesSize);
    memset(signedHashBytes, 0x0, signedHashBytesSize);
    
    size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
    uint8_t* hashBytes = malloc(hashBytesSize);
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
        return nil;
    }
    
    SecKeyRawSign(key,
                  kSecPaddingPKCS1SHA256,
                  hashBytes,
                  hashBytesSize,
                  signedHashBytes,
                  &signedHashBytesSize);
    
    NSData* signedHash = [NSData dataWithBytes:signedHashBytes
                                        length:(NSUInteger)signedHashBytesSize];
    
    if (hashBytes)
        free(hashBytes);
    if (signedHashBytes)
        free(signedHashBytes);
    
    return signedHash;
}
//这边对签名的数据进行验证 验签成功,则返回YES
- (BOOL)rsaSHA256VerifyData:(NSData *)plainData signature:(NSData *)signature usingPublicKey:(SecKeyRef)key {
    
    size_t signedHashBytesSize = SecKeyGetBlockSize(key);
    const void* signedHashBytes = [signature bytes];
    
    size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
    uint8_t* hashBytes = malloc(hashBytesSize);
    if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
        return NO;
    }
    
    OSStatus status = SecKeyRawVerify(key,
                                      kSecPaddingPKCS1SHA256,
                                      hashBytes,
                                      hashBytesSize,
                                      signedHashBytes,
                                      signedHashBytesSize);
    
    return status == errSecSuccess;
}

文章推荐

https://blog.cnbluebox.com/blog/2014/03/19/rsajia-mi/

相关文章

网友评论

      本文标题:iOS加密

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