iOS使用Security.framework進行RSA 加密解密簽名和驗證簽名

iOS 上 Security.framework爲咱們提供了安全方面相關的api;html

Security框架提供的RSA在iOS上使用的一些小結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 相關的接口使用和示例;git

1. 主要接口有github

/*!
    
//生成密鑰對

*/
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. 使用上面生成的密鑰對進行加密解密測試api

//公鑰加密私鑰密鑰測試
/** 三種填充方式區別
 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算法app

  以下我封裝了一個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

相關文章
相關標籤/搜索