2012-04-13 113 views
1

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

// 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"]; 

回答

0

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

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

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

+0

非常感谢。但我如何正确存储选定的身份? – thomasguenzel 2012-04-13 12:09:53

+0

我不确定。我看到'SecIdentityCopyCertificate()'和'SecCertificateCopyData()',然后在另一个方向'SecCertificateCreateWithData()'和'SecIdentityCreateWithCertificate()'。但是,我对这些东西并不十分熟悉,我不确定是否会通过将证书数据保存为用户默认值来破坏安全性。 'SecIdentityCopyCertificate()'的文档也表示可以将输出转换为'SecKeychainItemRef'并且我看到'SecKeychainItemCreatePersistentReference()',这看起来很有前途。然后,反过来,'SecKeychainItemCopyFromPersistentReference()'。 – 2012-04-13 12:40:16

1

使用SecKeychainItemCreatePersistentReference()SecKeychainItemCopyFromPersistentReference() - 它们可以在进程之间传递或持久化。有一些注意事项,但:

  1. 你不能坚持一个身份,只有一个证书,所以你必须经过额外的啤酒花之间进行转换。
  2. 你会得到NSData与长和不透明的数据块,所以你必须存储它,而不是例如。作为一个字符串;并忘记能够手动编辑此设置。

这就是我在我的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"]; 
} 
相关问题