• iOS使用Security.framework进行RSA 加密解密签名和验证签名


    iOS 上 Security.framework为我们提供了安全方面相关的api;

    Security框架提供的RSA在iOS上使用的一些小结

    • 支持的RSA keySize 大小有:512,768,1024,2048位
    • 支持的RSA 填充方式有三种:NOPadding,PKCS1,OAEP 三种方式 ,填充方式影响最大分组加密数据块的大小
    • 签名使用的填充方式PKCS1, 支持的签名算法有 sha1,sha256,sha224,sha384,sha512
    • Nopadding填充最大数据块为 下面接口 SecKeyGetBlockSize 大小; 
    • PKCS1 填充方式最大数据为 SecKeyGetBlockSize大小 减去11
    • OAEP 填充方式最大数据为 SecKeyGetBlockSize 大小减去 42
    • RSA加密解密签名,适合小块的数据处理,大量数量需要处理分组逻辑;密码学中推荐使用对称加密进行数据加密,使用RSA来加密对称密钥
    • iOS10,以及mac 10.12中新增加了几个接口,以下测试用的是老接口,支持iOS2.0+

    在这里说明一下RSA 相关的接口使用和示例;

    1. 主要接口有

    /*!
        
    //生成密钥对
    
    */
    OSStatus SecKeyGeneratePair(CFDictionaryRef parameters, SecKeyRef * _Nullable CF_RETURNS_RETAINED publicKey,
        SecKeyRef * _Nullable CF_RETURNS_RETAINED privateKey) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
    
    
    
    //加密
    OSStatus SecKeyEncrypt(
        SecKeyRef           key,
        SecPadding          padding,
        const uint8_t        *plainText,
        size_t              plainTextLen,
        uint8_t             *cipherText,
        size_t              *cipherTextLen)
        __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
    
    
    
    //解密
    OSStatus SecKeyDecrypt(
        SecKeyRef           key,                /* Private key */
        SecPadding          padding,            /* kSecPaddingNone,
                                                   kSecPaddingPKCS1,
                                                   kSecPaddingOAEP */
        const uint8_t       *cipherText,
        size_t              cipherTextLen,        /* length of cipherText */
        uint8_t             *plainText,    
        size_t              *plainTextLen)        /* IN/OUT */
        __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
    
    
    //签名
    OSStatus SecKeyRawSign(
        SecKeyRef           key,
        SecPadding          padding,
        const uint8_t       *dataToSign,
        size_t              dataToSignLen,
        uint8_t             *sig,
        size_t              *sigLen)
        __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
    
    
    
    //验证签名
    OSStatus SecKeyRawVerify(
        SecKeyRef           key,
        SecPadding          padding,
        const uint8_t       *signedData,
        size_t              signedDataLen,
        const uint8_t       *sig,
        size_t              sigLen)
        __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
    
    
    
    //分组加密的数据块大小
    size_t SecKeyGetBlockSize(SecKeyRef key)
        __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);

    2. 首先生成RSA密钥对,生成1024位,即是 128字节

    #define kRSA_KEY_SIZE 1024
    
    @interface ViewController : UIViewController
    {
        SecKeyRef publicKeyRef; //公钥
        SecKeyRef privateKeyRef;//私钥
    }
    
    
    
    
    //生成RSA密钥对,公钥和私钥,支持的SIZE有
    // sizes for RSA keys are: 512, 768, 1024, 2048.
    - (void)generateRSAKeyPair:(int )keySize
    {
        
        OSStatus ret = 0;
        publicKeyRef = NULL;
        privateKeyRef = NULL;
        ret = SecKeyGeneratePair((CFDictionaryRef)@{(id)kSecAttrKeyType:(id)kSecAttrKeyTypeRSA,(id)kSecAttrKeySizeInBits:@(keySize)}, &publicKeyRef, &privateKeyRef);
        NSAssert(ret==errSecSuccess, @"密钥对生成失败:%d",ret);
        
        NSLog(@"%@",publicKeyRef);
        NSLog(@"%@",privateKeyRef);
        NSLog(@"max size:%lu",SecKeyGetBlockSize(privateKeyRef));
        
    }

    3. 使用上面生成的密钥对进行加密解密测试

    //公钥加密私钥密钥测试
    /** 三种填充方式区别
     kSecPaddingNone      = 0,   要加密的数据块大小<=SecKeyGetBlockSize的大小,如这里128
     kSecPaddingPKCS1     = 1,   要加密的数据块大小<=128-11
     kSecPaddingOAEP      = 2,   要加密的数据块大小<=128-42
      密码学中的设计原则,一般用RSA来加密 对称密钥,用对称密钥加密大量的数据
      非对称加密速度慢,对称加密速度快
     */
    - (void)testRSAEncryptAndDecrypt
    {
        [self generateRSAKeyPair:kRSA_KEY_SIZE];
    
        NSData *srcData = [@"0123456789" dataUsingEncoding:NSUTF8StringEncoding];
        NSLog(@"%@",srcData);
        uint8_t encData[kRSA_KEY_SIZE/8] = {0};
        uint8_t decData[kRSA_KEY_SIZE/8] = {0};
        size_t blockSize = kRSA_KEY_SIZE / 8 ;
        OSStatus ret;
        
        
        ret = SecKeyEncrypt(publicKeyRef, kSecPaddingNone, srcData.bytes, srcData.length, encData, &blockSize);
        NSAssert(ret==errSecSuccess, @"加密失败");
        
        
        ret = SecKeyDecrypt(privateKeyRef, kSecPaddingNone, encData, blockSize, decData, &blockSize);
        NSAssert(ret==errSecSuccess, @"解密失败");
        
        NSData *dedData = [NSData dataWithBytes:decData length:blockSize];
        NSLog(@"dec:%@",dedData);
        if (memcmp(srcData.bytes, dedData.bytes, srcData.length)==0) {
            NSLog(@"PASS");
        }
    }

    4. 使用公钥密钥进行数据签名和验证签名

       对数据签名:首先对原始数据进行hash计算,可以得到数据的hash值;然后对hash值进行签名;

    - (void)testSignAndVerify
    {
        [self generateRSAKeyPair:kRSA_KEY_SIZE];
    
        
        NSString *tpath = [[NSBundle mainBundle] pathForResource:@"src.txt" ofType:nil];
        NSData *ttDt = [NSData dataWithContentsOfFile:tpath];
      //使用了下面封装的hash接口 NSData
    *sha1dg = [ttDt hashDataWith:CCDIGEST_SHA1]; OSStatus ret; //私钥签名,公钥验证签名 size_t siglen = SecKeyGetBlockSize(privateKeyRef); uint8_t *sig = malloc(siglen); bzero(sig, siglen); ret = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1SHA256, sha1dg.bytes, sha1dg.length, sig, &siglen); NSAssert(ret==errSecSuccess, @"签名失败"); ret = SecKeyRawVerify(publicKeyRef, kSecPaddingPKCS1SHA256, sha1dg.bytes, sha1dg.length,sig, siglen); NSAssert(ret==errSecSuccess, @"验证签名失败"); if (ret==errSecSuccess) { NSLog(@"SIGN VERIFY PASS"); } }

    5. 另外 iOS上 CommonCrypto/CommonDigest.h 中提供了密码学中常用的hash算法

      如下我封装了一个NSData的分类,可以在签名中直接使用

      支持的hash算法有 md2,md4,md5,sha1,sha224,sha256,sha384,sha512

    //
    //  NSData+KKHASH.h
    //  SecurityiOS
    //
    //  Created by cocoa on 16/12/15.
    //  Copyright © 2016年 dev.keke@gmail.com. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    typedef enum : NSUInteger {
        //md2 16字节长度
        CCDIGEST_MD2 = 1000,
        //md4 16字节长度
        CCDIGEST_MD4,
        //md5 16字节长度
        CCDIGEST_MD5,
        //sha1 20字节长度
        CCDIGEST_SHA1,
        //SHA224 28字节长度
        CCDIGEST_SHA224,
        //SHA256 32字节长度
        CCDIGEST_SHA256,
        //SHA384 48字节长度
        CCDIGEST_SHA384,
        //SHA512 64字节长度
        CCDIGEST_SHA512,
    } CCDIGESTAlgorithm;
    
    @interface NSData (KKHASH)
    
    /**
        计算数据的hash值,根据不同的算法
     */
    - (NSData *)hashDataWith:(CCDIGESTAlgorithm )ccAlgorithm;
    
    
    /**
        返回 hex string的 data
     */
    - (NSString *)hexString;
    
    
    @end
    View Code
    //
    //  NSData+KKHASH.m
    //  SecurityiOS
    //
    //  Created by cocoa on 16/12/15.
    //  Copyright © 2016年 dev.keke@gmail.com. All rights reserved.
    //
    
    #import "NSData+KKHASH.h"
    #include <CommonCrypto/CommonDigest.h>
    
    @implementation NSData (KKHASH)
    - (NSData *)hashDataWith:(CCDIGESTAlgorithm )ccAlgorithm
    {
        NSData *retData = nil;
        if (self.length <1) {
            return nil;
        }
        
        unsigned char *md;
        
        switch (ccAlgorithm) {
            case CCDIGEST_MD2:
            {
                md = malloc(CC_MD2_DIGEST_LENGTH);
                bzero(md, CC_MD2_DIGEST_LENGTH);
                CC_MD2(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_MD2_DIGEST_LENGTH];
            }
                break;
            case CCDIGEST_MD4:
            {
                md = malloc(CC_MD4_DIGEST_LENGTH);
                bzero(md, CC_MD4_DIGEST_LENGTH);
                CC_MD4(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_MD4_DIGEST_LENGTH];
    
            }
                break;
            case CCDIGEST_MD5:
            {
                md = malloc(CC_MD5_DIGEST_LENGTH);
                bzero(md, CC_MD5_DIGEST_LENGTH);
                CC_MD5(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_MD5_DIGEST_LENGTH];
    
            }
                break;
            case CCDIGEST_SHA1:
            {
                md = malloc(CC_SHA1_DIGEST_LENGTH);
                bzero(md, CC_SHA1_DIGEST_LENGTH);
                CC_SHA1(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
                
            }
                break;
            case CCDIGEST_SHA224:
            {
                md = malloc(CC_SHA224_DIGEST_LENGTH);
                bzero(md, CC_SHA224_DIGEST_LENGTH);
                CC_SHA224(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_SHA224_DIGEST_LENGTH];
                
            }
                break;
            case CCDIGEST_SHA256:
            {
                md = malloc(CC_SHA256_DIGEST_LENGTH);
                bzero(md, CC_SHA256_DIGEST_LENGTH);
                CC_SHA256(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_SHA256_DIGEST_LENGTH];
                
            }
                break;
            case CCDIGEST_SHA384:
            {
                md = malloc(CC_SHA384_DIGEST_LENGTH);
                bzero(md, CC_SHA384_DIGEST_LENGTH);
                CC_SHA384(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_SHA384_DIGEST_LENGTH];
                
            }
                break;
            case CCDIGEST_SHA512:
            {
                md = malloc(CC_SHA512_DIGEST_LENGTH);
                bzero(md, CC_SHA512_DIGEST_LENGTH);
                CC_SHA512(self.bytes, (CC_LONG)self.length, md);
                retData = [NSData dataWithBytes:md length:CC_SHA512_DIGEST_LENGTH];
                
            }
                break;
                
            default:
                md = malloc(1);
                break;
        }
        
        free(md);
        md = NULL;
        
        return retData;
        
    }
    
    
    - (NSString *)hexString
    {
        NSMutableString *result = nil;
        if (self.length <1) {
            return nil;
        }
        result = [[NSMutableString alloc] initWithCapacity:self.length * 2];
        for (size_t i = 0; i < self.length; i++) {
            [result appendFormat:@"%02x", ((const uint8_t *) self.bytes)[i]];
        }
        return result;
    }
    
    
    @end
    View Code

    6. 另外如果密钥由服务器生成,可以生成p12文件,来在ios上调用接口导入开发

    OSStatus SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options,
        CFArrayRef * __nonnull CF_RETURNS_RETAINED items) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);

    7. 另外为了配合验证,可以用openssl 命令对数据进行RSA加密解密

        //使用公钥加密 1024位密钥 ,128字节
        //openssl rsautl  -encrypt -out pubenc.txt  -in src.txt  -inkey public.pem -pubin
        
        //使用私钥解密
        //openssl rsautl -decrypt -in pubenc.txt -inkey private.pem -out pridec.txt

    总结:

    另外RSA密钥,私钥保存在手机上是不安全的;

     一般在非越狱的手机上,我们可以把生成的SecKeyRef 保存在 keychain中;

     但是在越狱的手机上,也是不安全的,因为可以导出keychain中的所有数据;

     没有绝对的安全,根据业务场景来实际开发吧

     封装工具:https://github.com/cocoajin/Security-iOS

    参考:

    https://developer.apple.com/library/content/documentation/Security/Conceptual/cryptoservices/CryptographyConcepts/CryptographyConcepts.html#//apple_ref/doc/uid/TP40011172-CH8-SW1

     

    https://developer.apple.com/library/content/samplecode/CryptoExercise/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008019

    https://developer.apple.com/library/content/samplecode/CryptoCompatibility/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013654

  • 相关阅读:
    oracle怎样查询注释
    kettle安装,启动spoon之后一闪就没了问题
    查看oracle当前的连接数
    在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能
    配置tomcat部署路径(Server Location是灰色怎么修改)
    怎样查询oracle的连接数
    64位系统安装ODBC驱动的方法
    前端日志实践
    webpack5之模块联邦
    关于前端技术、经验、团队、流程的总结
  • 原文地址:https://www.cnblogs.com/cocoajin/p/6183443.html
Copyright © 2020-2023  润新知