iOS逆向與安全1.0 :RSA加密

摘自百度百科-RSA:

RSA加密算法是一種非對稱加密算法。在公開密鑰加密和電子商業(yè) 中RSA被廣泛使用。 RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們?nèi)硕荚诼槭±砉W院工作。RSA就是他們?nèi)诵帐祥_頭字母拼在一起組成的。
在公開密鑰密碼體制中,加密密鑰(即公開密鑰)PK是公開信息,而解密密鑰(即秘密密鑰)SK是需要保密的。加密算法E和解密算法D也都是公開的。雖然解密密鑰SK是由公開密鑰PK決定的,但卻不能根據(jù)PK計算出SK。
正是基于這種理論,1978年出現(xiàn)了著名的RSA算法,它通常是先生成一對RSA 密鑰,其中之一是保密密鑰,由用戶保存;另一個為公開密鑰,可對外公開,甚至可在網(wǎng)絡服務器中注冊。為提高保密強度,RSA密鑰至少為500位長,一般推薦使用1024位。這就使加密的計算量很大。為減少計算量,在傳送信息時,常采用傳統(tǒng)加密方法與公開密鑰加密方法相結合的方式,即信息采用改進的DES或IDEA密鑰加密,然后使用RSA密鑰加密對話密鑰和信息摘要。對方收到信息后,用不同的密鑰解密并可核對信息摘要。

重點:為減少計算量,在傳送信息時,常采用傳統(tǒng)加密方法與公開密鑰加密方法相結合的方式,即信息采用改進的DES或IDEA密鑰加密,然后使用RSA密鑰加密對話密鑰和信息摘要。對方收到信息后,用不同的密鑰解密并可核對信息摘要。

iOS中OpenSSL使用RSA

由于Mac系統(tǒng)內(nèi)置OpenSSL(開源加密庫),所以我們可以直接在終端上使用命令來玩RSA. OpenSSL中RSA算法常用指令主要有三個:

image.png

1、生成私鑰/公鑰

// 生成強度是 512 的 RSA 私鑰
$ openssl genrsa -out private.pem 512

//從私鑰中提取公鑰
$ openssl rsa -in private.pem -out public.pem -outform PEM -pubout
// 或
$ openssl rsa -in private.pem -out public.pem -pubout

// 以明文輸出私鑰內(nèi)容
$ openssl rsa -in private.pem -text -out private.txt

// 以明文輸出公鑰內(nèi)容
$ openssl rsa -in public.pem -out public.txt -pubin -pubout -text

2、生成簽名證書

上一步OpenSSL默認生成的都是PEM格式的證書,iOS中不能使用PEM格式的證書,需要轉(zhuǎn)成二進制的 DER 格式的證書

// 1、生成csr證書請求文件(使用上一步中生成的私鑰private.pem)
openssl req -new -key private.pem -out rsacert.csr
// 2、生成證書并簽名(rsacert.crt證書),需要秘鑰文件private.pem
openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
// 3、轉(zhuǎn)換證書格式(rsacert.crt)——> 二進制證書(rsacert.der證書)
openssl x509 -outform der -in rsacert.crt -out rsacert.der
// 4、導出p12格式證書(需要用到私鑰private.pem)
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt

生成csr證書請求文件:


image.png

導出p12文件:


image.png

3、使用證書

RSAtool類的使用

 //1.加載公鑰
    [[RSACryptor sharedRSACryptor] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]];
    //2.加載私鑰
    [[RSACryptor sharedRSACryptor] loadPrivateKey: [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"];

  NSData * result = [[RSACryptor sharedRSACryptor] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
    //base64編碼
    NSString * base64 = [result base64EncodedStringWithOptions:0];
    NSLog(@"加密之后:%@\n",base64);
    
    //解密
    NSData * dcStr = [[RSACryptor sharedRSACryptor] decryptData:result];
    NSLog(@"%@",[[NSString alloc] initWithData:dcStr encoding:NSUTF8StringEncoding]);

資料圖

2018053117022165.png

PS:來源:kangpp


RSACryptor.h

@interface RSACryptor : NSObject
    
+ (instancetype)sharedRSACryptor;
    
    /**
     *  生成密鑰對
     *
     *  @param keySize 密鑰尺寸,可選數(shù)值(512/1024/2048)
     */
- (void)generateKeyPair:(NSUInteger)keySize;
    
    /**
     *  加載公鑰
     *
     *  @param publicKeyPath 公鑰路徑
     *
     @code
     # 生成證書
     $ openssl genrsa -out ca.key 1024
     # 創(chuàng)建證書請求
     $ openssl req -new -key ca.key -out rsacert.csr
     # 生成證書并簽名
     $ openssl x509 -req -days 3650 -in rsacert.csr -signkey ca.key -out rsacert.crt
     # 轉(zhuǎn)換格式
     $ openssl x509 -outform der -in rsacert.crt -out rsacert.der
     @endcode
     */
- (void)loadPublicKey:(NSString *)publicKeyPath;
    
    /**
     *  加載私鑰
     *
     *  @param privateKeyPath p12文件路徑
     *  @param password       p12文件密碼
     *
     @code
     openssl pkcs12 -export -out p.p12 -inkey ca.key -in rsacert.crt
     @endcode
     */
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password;
    
    /**
     *  加密數(shù)據(jù)
     *
     *  @param plainData 明文數(shù)據(jù)
     *
     *  @return 密文數(shù)據(jù)
     */
- (NSData *)encryptData:(NSData *)plainData;
    
    /**
     *  解密數(shù)據(jù)
     *
     *  @param cipherData 密文數(shù)據(jù)
     *
     *  @return 明文數(shù)據(jù)
     */
- (NSData *)decryptData:(NSData *)cipherData;
    
    @end

RSACryptor.m

#import "RSACryptor.h"

// 填充模式
#define kTypeOfWrapPadding        kSecPaddingPKCS1

// 公鑰/私鑰標簽
#define kPublicKeyTag            "com.logic.EncryptDemo.publickey"
#define kPrivateKeyTag            "com.logic.EncryptDemo.privatekey"

static const uint8_t publicKeyIdentifier[]        = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[]        = kPrivateKeyTag;

@interface RSACryptor() {
    SecKeyRef publicKeyRef;                             // 公鑰引用
    SecKeyRef privateKeyRef;                            // 私鑰引用
}
    
    @property (nonatomic, retain) NSData *publicTag;        // 公鑰標簽
    @property (nonatomic, retain) NSData *privateTag;       // 私鑰標簽
    
    @end

@implementation RSACryptor
    
+ (instancetype)sharedRSACryptor {
    static id instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
    
- (instancetype)init {
    self = [super init];
    if (self) {
        // 查詢密鑰的標簽
        _privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
        _publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
    }
    return self;
}
    
#pragma mark - 加密 & 解密數(shù)據(jù)
- (NSData *)encryptData:(NSData *)plainData {
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSAssert(plainData != nil, @"明文數(shù)據(jù)為空");
    NSAssert(publicKeyRef != nil, @"公鑰為空");
    
    NSData *cipher = nil;
    uint8_t *cipherBuffer = NULL;
    
    // 計算緩沖區(qū)大小
    cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
    keyBufferSize = [plainData length];
    
    if (kTypeOfWrapPadding == kSecPaddingNone) {
        NSAssert(keyBufferSize <= cipherBufferSize, @"加密內(nèi)容太大");
    } else {
        NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密內(nèi)容太大");
    }
    
    // 分配緩沖區(qū)
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    memset((void *)cipherBuffer, 0x0, cipherBufferSize);
    
    // 使用公鑰加密
    sanityCheck = SecKeyEncrypt(publicKeyRef,
                                kTypeOfWrapPadding,
                                (const uint8_t *)[plainData bytes],
                                keyBufferSize,
                                cipherBuffer,
                                &cipherBufferSize
                                );
    
    NSAssert(sanityCheck == noErr, @"加密錯誤,OSStatus == %d", sanityCheck);
    
    // 生成密文數(shù)據(jù)
    cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
    
    if (cipherBuffer) free(cipherBuffer);
    
    return cipher;
}
    
- (NSData *)decryptData:(NSData *)cipherData {
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSData *key = nil;
    uint8_t *keyBuffer = NULL;
    
    SecKeyRef privateKey = NULL;
    
    privateKey = [self getPrivateKeyRef];
    NSAssert(privateKey != NULL, @"私鑰不存在");
    
    // 計算緩沖區(qū)大小
    cipherBufferSize = SecKeyGetBlockSize(privateKey);
    keyBufferSize = [cipherData length];
    
    NSAssert(keyBufferSize <= cipherBufferSize, @"解密內(nèi)容太大");
    
    // 分配緩沖區(qū)
    keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
    memset((void *)keyBuffer, 0x0, keyBufferSize);
    
    // 使用私鑰解密
    sanityCheck = SecKeyDecrypt(privateKey,
                                kTypeOfWrapPadding,
                                (const uint8_t *)[cipherData bytes],
                                cipherBufferSize,
                                keyBuffer,
                                &keyBufferSize
                                );
    
    NSAssert1(sanityCheck == noErr, @"解密錯誤,OSStatus == %d", sanityCheck);
    
    // 生成明文數(shù)據(jù)
    key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
    
    if (keyBuffer) free(keyBuffer);
    
    return key;
}
    
#pragma mark - 密鑰處理
    /**
     *  生成密鑰對
     */
- (void)generateKeyPair:(NSUInteger)keySize {
    OSStatus sanityCheck = noErr;
    publicKeyRef = NULL;
    privateKeyRef = NULL;
    
    NSAssert1((keySize == 512 || keySize == 1024 || keySize == 2048), @"密鑰尺寸無效 %tu", keySize);
    
    // 刪除當前密鑰對
    [self deleteAsymmetricKeys];
    
    // 容器字典
    NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
    
    // 設置密鑰對的頂級字典
    [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
    
    // 設置私鑰字典
    [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [privateKeyAttr setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    
    // 設置公鑰字典
    [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [publicKeyAttr setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    
    // 設置頂級字典屬性
    [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
    [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
    
    // SecKeyGeneratePair 返回密鑰對引用
    sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
    NSAssert((sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL), @"生成密鑰對失敗");
}
    
    /**
     *  加載公鑰
     */
- (void)loadPublicKey:(NSString *)publicKeyPath {
    
    NSAssert(publicKeyPath.length != 0, @"公鑰路徑為空");
    
    // 刪除當前公鑰
    if (publicKeyRef) CFRelease(publicKeyRef);
    
    // 從一個 DER 表示的證書創(chuàng)建一個證書對象
    NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
    SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
    NSAssert(certificateRef != NULL, @"公鑰文件錯誤");
    
    // 返回一個默認 X509 策略的公鑰對象,使用之后需要調(diào)用 CFRelease 釋放
    SecPolicyRef policyRef = SecPolicyCreateBasicX509();
    // 包含信任管理信息的結構體
    SecTrustRef trustRef;
    
    // 基于證書和策略創(chuàng)建一個信任管理對象
    OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
    NSAssert(status == errSecSuccess, @"創(chuàng)建信任管理對象失敗");
    
    // 信任結果
    SecTrustResultType trustResult;
    // 評估指定證書和策略的信任管理是否有效
    status = SecTrustEvaluate(trustRef, &trustResult);
    NSAssert(status == errSecSuccess, @"信任評估失敗");
    
    // 評估之后返回公鑰子證書
    publicKeyRef = SecTrustCopyPublicKey(trustRef);
    NSAssert(publicKeyRef != NULL, @"公鑰創(chuàng)建失敗");
    
    if (certificateRef) CFRelease(certificateRef);
    if (policyRef) CFRelease(policyRef);
    if (trustRef) CFRelease(trustRef);
}
    
    /**
     *  加載私鑰
     */
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
    
    NSAssert(privateKeyPath.length != 0, @"私鑰路徑為空");
    
    // 刪除當前私鑰
    if (privateKeyRef) CFRelease(privateKeyRef);
    
    NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath];
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    CFStringRef passwordRef = (__bridge CFStringRef)password;
    
    // 從 PKCS #12 證書中提取標示和證書
    SecIdentityRef myIdentity;
    SecTrustRef myTrust;
    const void *keys[] =   {kSecImportExportPassphrase};
    const void *values[] = {passwordRef};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    
    // 返回 PKCS #12 格式數(shù)據(jù)中的標示和證書
    OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
    
    if (status == noErr) {
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
        myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
        myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
    }
    
    if (optionsDictionary) CFRelease(optionsDictionary);
    
    NSAssert(status == noErr, @"提取身份和信任失敗");
    
    SecTrustResultType trustResult;
    // 評估指定證書和策略的信任管理是否有效
    status = SecTrustEvaluate(myTrust, &trustResult);
    NSAssert(status == errSecSuccess, @"信任評估失敗");
    
    // 提取私鑰
    status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
    NSAssert(status == errSecSuccess, @"私鑰創(chuàng)建失敗");
}
    
    /**
     *  刪除非對稱密鑰
     */
- (void)deleteAsymmetricKeys {
    OSStatus sanityCheck = noErr;
    NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
    
    // 設置公鑰查詢字典
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // 設置私鑰查詢字典
    [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // 刪除私鑰
    sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPrivateKey);
    NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"刪除私鑰錯誤,OSStatus == %d", sanityCheck);
    
    // 刪除公鑰
    sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPublicKey);
    NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"刪除公鑰錯誤,OSStatus == %d", sanityCheck);
    
    if (publicKeyRef) CFRelease(publicKeyRef);
    if (privateKeyRef) CFRelease(privateKeyRef);
}
    
    /**
     *  獲得私鑰引用
     */
- (SecKeyRef)getPrivateKeyRef {
    OSStatus sanityCheck = noErr;
    SecKeyRef privateKeyReference = NULL;
    
    if (privateKeyRef == NULL) {
        NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
        
        // 設置私鑰查詢字典
        [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
        [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
        [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
        
        // 獲得密鑰
        sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference);
        
        if (sanityCheck != noErr) {
            privateKeyReference = NULL;
        }
    } else {
        privateKeyReference = privateKeyRef;
    }
    
    return privateKeyReference;
}
    
 @end
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容