JHHK

欢迎来到我的个人网站
行者常至 为者常成

非对称秘钥加密

目录

简介

目前主流的加密算法分为两种:对称秘钥加密和非对称秘钥加密。

对称秘钥加密主要包括:DES 3DES AES

非对称秘钥加密又叫公开秘钥加密主要包括:RSA

对称秘钥加密:需要发送方与接收方知 道共享的秘密密钥,最初如何商定密钥( 尤其“素未谋面”)?

该篇主要讲解非对称秘钥加密,对称秘钥加密见上篇

公开秘钥加密:完全不同的方法 [Diffie-Hellman76, RSA78],解决了秘钥传输问题

  • 发送方与接收方无需共享秘密密钥
  • 公开密钥(公钥)完全公开
  • 私有密钥(私钥)只有接收方知道

原理

需求:

  1. 公钥加密 KB+ 和私钥解密KB- 需要满足:KB- ( KB+(m) ) = m
  2. 给定公钥 KB+ 不可能计算得到私钥 KB-

前提条件: 模运算
x mod n = x除以n的余数
事实上:
[(a mod n) + (b mod n)] mod n = (a+b) mod n
[(a mod n) - (b mod n)] mod n = (a-b) mod n
[(a mod n) * (b mod n)] mod n = (a*b) mod n

因此:
(a mod n)d次方 mod n = a的d次方 mod n
例如:x=14, n=10, d=2,则
(x mod n)d次方 mod n = (4*4) mod 10 = 6
x的d次方 = 14*14 = 196
x的d次方 mod 10 = 6

预备知识
报文/信息(message): 仅仅是一个比特模式,每个比特模式可以表示为一个唯一的整数,因此,加密一个报文就等价于加密一个数例如:
m= 10010001,可以唯一地表示为十进制 数145
为了加密m,我们可以加密对应的数(145) ,得到一个新的数(即密文)

生成公钥/私钥对

  1. 选择2个大质数p和q。(e.g., 1024bits的大质数)
  2. 计算n = pq,z = (p-1)(q-1)
  3. 选择e满足e <n 使e与z 之间没有公因子,即e,z互质(relatively prime)
  4. 选择d使得 e的d次方-1 刚好可以被z整除, (即:e的d次方modz =1).
  5. 公钥: (n,e);私钥:(n,d).

加密、解密

  1. 给定公钥 (n,e)和私钥(n,d)
  2. 加密报文m (m<n)时,计算 c = m的e次方 mod n
  3. 解密密文c时,计算 m = c的d次方 mod n

m=(m的e次方mod n)d次方 mod n

举例 img

理论依据 img

另一个重要性质 img

安全性

RSA的安全性建立在“大数分解和素性检测”这个数论难题的基础上 既将两个大素数相乘在计算上容易实现,而将该乘积分解的计算量相当大
假设已知Bob的公钥(n,e),那么有多大难 度确定d,即私钥(n,d)?
本质上需要在不知道两个因子p和q的前提 下,找出n的因子分解一个大数是很困难的!

RSA的幂运算强度很大 DES至少比RSA快100倍。实际应用中:利用公钥加密建立安全连接,然后建立第二个密钥-对称会话密钥,用于加密数据

会话密钥(session key, KS)
Bob与Alice利用RSA交换对称会话密钥KS
一旦双方确认KS,则利用会话密钥加密/解密会话数据

代码实现

LCRSA2.h文件

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LCRSA2 : NSObject


/// 公钥加密
/// @param plainData 要加密的数据
/// @param publicKey 公钥字符串
+(NSData*)encryptData:(NSData*)plainData
            publicKey:(NSString*)publicKey;



/// 私钥解密
/// @param cipherData 要解密的数据
/// @param privateKey 私钥字符串
+(NSData *)decryptData:(NSData *)cipherData
            privateKey:(NSString*)privateKey;



/// 公钥加密
/// @param plainData 要加密的数据
/// @param publicKeyPath 证书路径
+(NSData*)encryptData:(NSData*)plainData
        publicKeyPath:(NSString*)publicKeyPath;



/// 私钥解密
/// @param cipherData 要解密的数据
/// @param privateKeyPath p12文件路径
+(NSData *)decryptData:(NSData *)cipherData
        privateKeyPath:(NSString*)privateKeyPath;

@end

NS_ASSUME_NONNULL_END

LCRSA2.m文件

#import "LCRSA2.h"

@implementation LCRSA2

+(NSData*)encryptData:(NSData*)plainData
            publicKey:(NSString*)publicKey{
    SecKeyRef publicKeyRef = [self __getPublicKeyRef:publicKey];
    return [LCRSA2 __encryptData:plainData publicKeyRef:publicKeyRef padding:kSecPaddingPKCS1];
}


+(NSData *)decryptData:(NSData *)cipherData
            privateKey:(NSString*)privateKey{
    SecKeyRef privateKeyRef = [self __getPrivateKeyRef:privateKey];
    return [LCRSA2 __decryptData:cipherData privateKeyRef:privateKeyRef padding:kSecPaddingPKCS1];
}


+(NSData*)encryptData:(NSData*)plainData
            publicKeyPath:(NSString*)publicKeyPath{
    SecKeyRef publicKeyRef = [self __loadPublicKeyRef:publicKeyPath];
    return [LCRSA2 __encryptData:plainData publicKeyRef:publicKeyRef padding:kSecPaddingPKCS1];
}


+(NSData *)decryptData:(NSData *)cipherData
            privateKeyPath:(NSString*)privateKeyPath{
    SecKeyRef privateKeyRef = [self __loadPrivateKey:privateKeyPath password:@"123456"];
    return [LCRSA2 __decryptData:cipherData privateKeyRef:privateKeyRef padding:kSecPaddingPKCS1];
}



#pragma mark - 内部方法
// !!!: 公钥加密、解密
+(NSData *)__encryptData:(NSData *)plainData
          publicKeyRef:(SecKeyRef)publicKeyRef
               padding:(SecPadding)padding{
   
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSAssert(plainData != nil, @"明文数据为空");
    NSAssert(publicKeyRef != nil, @"公钥为空");
    
    NSData *cipher = nil;
    uint8_t *cipherBuffer = NULL;
    
    // 计算缓冲区大小
    cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
    keyBufferSize = [plainData length];
    
     
    //填充模式
    //kSecPaddingNone 每次加密结果是固定的,
    //kSecPaddingPKCS1是随机变化的
    if (padding == kSecPaddingNone) {
        NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容太大");
    } else {
        NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大");
    }
    
    // 分配缓冲区
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    memset((void *)cipherBuffer, 0x0, cipherBufferSize);
    
    // 使用公钥加密
    sanityCheck = SecKeyEncrypt(publicKeyRef,
                                padding,
                                (const uint8_t *)[plainData bytes],
                                keyBufferSize,
                                cipherBuffer,
                                &cipherBufferSize
                                );
    
    NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %dd", (int)sanityCheck);
    
    // 生成密文数据
    cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
    
    if (cipherBuffer) free(cipherBuffer);
    
    return cipher;
}


+(NSData *)__decryptData:(NSData *)cipherData
         privateKeyRef:(SecKeyRef)privateKeyRef
               padding:(SecPadding)padding{
    
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSData *key = nil;
    uint8_t *keyBuffer = NULL;
    
    NSAssert(privateKeyRef != NULL, @"私钥不存在");
    
    // 计算缓冲区大小
    cipherBufferSize = SecKeyGetBlockSize(privateKeyRef);
    keyBufferSize = [cipherData length];
    
    NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大");
    
    // 分配缓冲区
    keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
    memset((void *)keyBuffer, 0x0, keyBufferSize);
    
    // 使用私钥解密
    sanityCheck = SecKeyDecrypt(privateKeyRef,
                                padding,
                                (const uint8_t *)[cipherData bytes],
                                cipherBufferSize,
                                keyBuffer,
                                &keyBufferSize
                                );
    
    NSAssert1(sanityCheck == noErr, @"解密错误,OSStatus == %d", sanityCheck);
    
    // 生成明文数据
    key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
    
    if (keyBuffer) free(keyBuffer);
    return key;
}


// !!!: 从证书提取SecKeyRef
+(SecKeyRef)__loadPublicKeyRef:(NSString *)publicKeyPath {
    NSAssert(publicKeyPath.length != 0, @"公钥路径为空");
    
    // 从一个 DER 表示的证书创建一个证书对象
    NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
    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, @"信任评估失败");
    
    // 评估之后返回公钥子证书
    SecKeyRef publicKeyRef = SecTrustCopyPublicKey(trustRef);
    NSAssert(publicKeyRef != NULL, @"公钥创建失败");
    
    if (certificateRef) CFRelease(certificateRef);
    if (policyRef) CFRelease(policyRef);
    if (trustRef) CFRelease(trustRef);
    
    return publicKeyRef;
}


+(SecKeyRef)__loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
    
    NSAssert(privateKeyPath.length != 0, @"私钥路径为空");
    
    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 格式数据中的标示和证书
    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, @"信任评估失败");
    
    SecKeyRef privateKeyRef = nil;
    // 提取私钥
    status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
    NSAssert(status == errSecSuccess, @"私钥创建失败");
    
    return privateKeyRef;
}


// !!!: 从公私钥字符串内提取SecKeyRef
+ (SecKeyRef)__getPublicKeyRef:(NSString *)key{
    NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"];
    NSRange epos = [key rangeOfString:@"-----END PUBLIC KEY-----"];
    if(spos.location != NSNotFound && epos.location != NSNotFound){
        NSUInteger s = spos.location + spos.length;
        NSUInteger e = epos.location;
        NSRange range = NSMakeRange(s, e-s);
        key = [key substringWithRange:range];
    }
    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
    
    // This will be base64 encoded, decode it.
    NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
    data = [LCRSA2 __stripPublicKeyHeader:data];
    
    if(!data){
        return nil;
    }
    
    //a tag to read/write keychain storage
    NSString *tag = @"RSAUtil_PubKey";
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
    // Delete any old lingering key with the same tag
    NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
    [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
    SecItemDelete((__bridge CFDictionaryRef)publicKey);
    
    // Add persistent version of the key to system keychain
    [publicKey setObject:data forKey:(__bridge id)kSecValueData];
    [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)
     kSecAttrKeyClass];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
     kSecReturnPersistentRef];
    
    CFTypeRef persistKey = nil;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
    if (persistKey != nil){
        CFRelease(persistKey);
    }
    if ((status != noErr) && (status != errSecDuplicateItem)) {
        return nil;
    }
    
    [publicKey removeObjectForKey:(__bridge id)kSecValueData];
    [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // Now fetch the SecKeyRef version of the key
    SecKeyRef keyRef = nil;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);
    if(status != noErr){
        return nil;
    }
    return keyRef;
}

+ (SecKeyRef)__getPrivateKeyRef:(NSString *)key{
    NSRange spos;
    NSRange epos;
    spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"];
    if(spos.length > 0){
        epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"];
    }else{
        spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"];
        epos = [key rangeOfString:@"-----END PRIVATE KEY-----"];
    }
    if(spos.location != NSNotFound && epos.location != NSNotFound){
        NSUInteger s = spos.location + spos.length;
        NSUInteger e = epos.location;
        NSRange range = NSMakeRange(s, e-s);
        key = [key substringWithRange:range];
    }
    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
    
    // This will be base64 encoded, decode it.
    NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
    data = [LCRSA2 __stripPrivateKeyHeader:data];
    if(!data){
        return nil;
    }
    
    //a tag to read/write keychain storage
    NSString *tag = @"RSAUtil_PrivKey";
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
    // Delete any old lingering key with the same tag
    NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init];
    [privateKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
    [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [privateKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
    SecItemDelete((__bridge CFDictionaryRef)privateKey);
    
    // Add persistent version of the key to system keychain
    [privateKey setObject:data forKey:(__bridge id)kSecValueData];
    [privateKey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)
     kSecAttrKeyClass];
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
     kSecReturnPersistentRef];
    
    CFTypeRef persistKey = nil;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)privateKey, &persistKey);
    if (persistKey != nil){
        CFRelease(persistKey);
    }
    if ((status != noErr) && (status != errSecDuplicateItem)) {
        return nil;
    }
    
    [privateKey removeObjectForKey:(__bridge id)kSecValueData];
    [privateKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // Now fetch the SecKeyRef version of the key
    SecKeyRef keyRef = nil;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKey, (CFTypeRef *)&keyRef);
    if(status != noErr){
        return nil;
    }
    return keyRef;
}


+ (NSData *)__stripPublicKeyHeader:(NSData *)d_key{
    // Skip ASN.1 public key header
    if (d_key == nil) return(nil);
    
    unsigned long len = [d_key length];
    if (!len) return(nil);
    
    unsigned char *c_key = (unsigned char *)[d_key bytes];
    unsigned int  idx     = 0;
    
    if (c_key[idx++] != 0x30) return(nil);
    
    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;
    
    // PKCS #1 rsaEncryption szOID_RSA_RSA
    static unsigned char seqiod[] =
    { 0x30,   0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
        0x01, 0x05, 0x00 };
    if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
    
    idx += 15;
    
    if (c_key[idx++] != 0x03) return(nil);
    
    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;
    
    if (c_key[idx++] != '\0') return(nil);
    
    // Now make a new NSData from this buffer
    return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}


+ (NSData *)__stripPrivateKeyHeader:(NSData *)d_key{
    // Skip ASN.1 private key header
    if (d_key == nil) return(nil);
    
    unsigned long len = [d_key length];
    if (!len) return(nil);
    
    unsigned char *c_key = (unsigned char *)[d_key bytes];
    unsigned int  idx     = 22; //magic byte at offset 22
    
    if (0x04 != c_key[idx++]) return nil;
    
    //calculate length of the key
    unsigned int c_len = c_key[idx++];
    int det = c_len & 0x80;
    if (!det) {
        c_len = c_len & 0x7f;
    } else {
        int byteCount = c_len & 0x7f;
        if (byteCount + idx > len) {
            //rsa length field longer than buffer
            return nil;
        }
        unsigned int accum = 0;
        unsigned char *ptr = &c_key[idx];
        idx += byteCount;
        while (byteCount) {
            accum = (accum << 8) + *ptr;
            ptr++;
            byteCount--;
        }
        c_len = accum;
    }
    
    // Now make a new NSData from this buffer
    return [d_key subdataWithRange:NSMakeRange(idx, c_len)];
}


@end

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.