ideawu / Objective-C-RSA

Doing RSA encryption and decryption with Objective-C on iOS
http://www.ideawu.com/blog/post/132.html
Other
1.15k stars 280 forks source link

RSA 加密线上失败,调试无法重现 #46

Open clayzhu opened 6 years ago

clayzhu commented 6 years ago

你好,我们 App 在发布到 App Store 后,RSA 加密失败率挺高的,但是我们本地开发调试时,没有遇到过,也无法重现。发生错误的机型和系统,基本全覆盖。

需要说明的是,在这之前我们对代码做过一些修改。首先是把 RSA 类改为了 NSString 的分类;其次,由于需要对钥匙串做 Delete -> Add -> Matching 的操作,在多线程环境下发生了错误,就对这一部分代码加了 @synchronized 同步锁,如下:

    @synchronized ([UIApplication sharedApplication]) {
        //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;
    }
detecyang commented 5 years ago

本地测试时,有没有做压力测试,看看会出现失败吗?这个库有点坑

clayzhu commented 5 years ago

做过压力测试的,没有出现失败过,但是线上就很多。只能在上层业务调用处,判断 RSA 加密返回是否为空🤷‍♂️

detecyang commented 5 years ago

做过压力测试的,没有出现失败过,但是线上就很多。只能在上层业务调用处,判断 RSA 加密返回是否为空🤷‍♂️

如果是因为资源抢占的问题导致,我们是把源码中生成 SecKeyRef 的地方改成了通过公钥数据来创建。

xiaoaihhh commented 3 years ago

检查一下锁里面的代码在其它位置是否有访问,如果其它地方也有访问,需要看下是否同样加锁了

I-m-SuperMan commented 3 years ago
[publicDictionary setObject:(__bridge id) kSecAttrAccessibleAlways forKey:(__bridge id)kSecAttrAccessible];

我这边是在后台的时候出现的。加上这句话
wxh841969240 commented 2 years ago
    NSString *tag = @"RSAUtil_PubKey";  把这个改一下 能改善很多。。。 估计你的App里面有其他SDK也用了这个库,tag冲突会导致异步的时候取到别人的公钥
1552612373 commented 2 years ago

“kSecAttrAccessibleAlways” iOS14弃用了,iOS14后有啥好办法?