将SecIdentityRef存储到NSUserDefaults中

时间:2012-04-13 11:49:19

标签: cocoa openssl nsuserdefaults apple-push-notifications

我发现使用APNS和可可的一个很棒的解释。 APNS Pusher 现在我不想每次都选择我的SecIdentityRef(因为我很懒)我尝试将SecIdentityRef放入NSData并将其保存为默认值。下次应用程序启动时我再次加载它,它总是得到exc_bad_access。这是我添加的代码:

// For saving
NSData *secRefData = [NSData dataWithBytes:[SFChooseIdentityPanel sharedChooseIdentityPanel].identity length:sizeof([SFChooseIdentityPanel sharedChooseIdentityPanel].identity)];
[[NSUserDefaults standardUserDefaults] setValue:secRefData forKey:@"identity"];

//For loading
NSData *secRefData = [[NSUserDefaults standardUserDefaults] valueForKey:@"identity"];
if([secRefData length] != 0) {
     [[APNS sharedAPNS] setIdentity:(SecIdentityRef)CFRetain([secRefData bytes])];
}

我怎样才能让它发挥作用?我还有另一种存储身份的方式吗?

修改

所以我通过保存身份名称找到了解决方案,在启动应用程序时,它会查看哪些可用身份具有此名称并使用具有正确名称的身份。这是代码:

//For loading
    NSString *lastIdentityName = [[NSUserDefaults standardUserDefaults] valueForKey:@"identityName"];
    if([lastIdentityName length] != 0) {
        NSArray *allIdentities = [self identities];
        for (id object in allIdentities) {
            NSString *theName = [[[X509Certificate extractCertDictFromIdentity:(SecIdentityRef)object] valueForKey:@"Subject"] objectForKey:@"CommonName"];
            if([theName isEqualToString:lastIdentityName]) {
                [[APNS sharedAPNS] setIdentity:(SecIdentityRef)CFRetain((__bridge_retained SecIdentityRef)object)];
                [[NSUserDefaults standardUserDefaults] setValue:[self identityName] forKey:@"identityName"];
                // KVO trigger
                [self willChangeValueForKey:@"identityName"];
                [self didChangeValueForKey:@"identityName"];
            }
        }
    }

//For saving
    [[NSUserDefaults standardUserDefaults] setValue:[self identityName] forKey:@"identityName"];

2 个答案:

答案 0 :(得分:1)

使用SecKeychainItemCreatePersistentReference()SecKeychainItemCopyFromPersistentReference() - 它们可以在进程之间传递或持久化。但是有一些警告:

  1. 你不能坚持身份,只能坚持证书,所以你必须通过额外的啤酒花才能在两者之间进行转换。
  2. 你会得到NSData长而不透明的数据块,所以你必须将它存储起来,而不是像作为一个字符串;并且忘记了能够手动编辑此设置。
  3. 这就是我在我的PDF签名应用程序中的表现方式,到目前为止它完美无瑕。以下是代码的相关部分:

    - (NSData*) identityToPersistent:(SecIdentityRef)ident
    {
        OSStatus status;
        SecCertificateRef cert;
        CFDataRef data = nil;
    
        status = SecIdentityCopyCertificate(ident, &cert);
        if (status != noErr)
            return nil;
        status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert, &data);
        CFRelease(cert);
        if (status != noErr)
            return nil;
    
        return CFBridgingRelease(data);
    }
    
    - (SecIdentityRef) identityFromPersistent:(NSData*)data
    {
        OSStatus status;
        SecKeychainItemRef cert;
        SecIdentityRef ident = nil;
    
        status = SecKeychainItemCopyFromPersistentReference((__bridge CFDataRef)data, &cert);
        if (status != noErr)
            return nil;
        status = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)cert, &ident);
        CFRelease(cert);
    
        return ident;
    }
    
    
    - (SecIdentityRef) getPreferredIdentity
    {
        NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:@"SigningIdentity"];
        if (!data)
            return nil;
        return [self identityFromPersistent:data];
    }
    
    - (void) setPreferredIdentity:(SecIdentityRef)ident
    {
        NSData *data = [self identityToPersistent:ident];
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"SigningIdentity"];
    }
    

答案 1 :(得分:0)

这一切都搞砸了。 SecIdentityRef是对内部对象的不透明引用。你把它看作是指向那个对象的指针,但它可能只是一个表或其他什么的索引。

其次,您不知道内部对象的大小。您的sizeof表达式产生指针的大小,而不是它引用的对象。没有办法获得实际尺寸,因为类型是不透明的。

最后,没有理由相信该对象是写入文件然后读出并保持完整的“标量”数据。很可能,该对象包含内部指针,这些指针在另一个进程的地址空间中没有意义。更不用说你没有保存和恢复那些指针可能指向的事实。