添加SecAccessControlRef后SecItemAdd返回errSecParam错误求助
解决SecItemAdd使用kSecAttrAccessControl返回errSecParam的问题
代码中的核心错误点
- 可访问性规则冲突:
kSecAttrAccessControl对象已经通过SecAccessControlCreateWithFlags的第二个参数指定了kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,但你又在后续的update字典中添加了kSecAttrAccessibleAlways。这两个字段不能同时存在,会直接触发errSecParam错误。 - 对象所有权错误转移:
kSecUseAuthenticationUIAllow是系统提供的常量,不需要用__bridge_transfer转移所有权,这种操作会导致对象被错误释放,进而引发参数异常,应改用__bridge进行类型转换。 - 未校验Access Control对象创建结果:
SecAccessControlCreateWithFlags可能因设备未设置锁屏密码(你指定的规则要求必须有密码)等原因返回nil,直接将nil传入kSecAttrAccessControl会导致errSecParam。
修正后的代码
1. 修正查询字典生成方法
static NSDictionary * AFKeyChainQueryDictionaryWithIdentifier(NSString *identifier){ NSCParameterAssert(identifier); NSError *error = nil; SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, &error); // 校验Access Control对象是否创建成功 if (!sacObject) { NSLog(@"创建SecAccessControlRef失败: %@", error); return nil; } return @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: @"AFOAuthCredentialService", (__bridge id)kSecAttrAccount: identifier, (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject, (__bridge id)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUIAllow, }; }
2. 修正存储方法
+ (OSStatus)storeCredential:(AFOAuthCredential *)credential withIdentifier:(NSString *)identifier withAccessbility:(id)accessbility { NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy]; if (!queryDictionary) { return errSecParam; } // 使用iOS 12+推荐的安全编码方式归档数据 NSError *archiveError = nil; NSData *credentialData = [NSKeyedArchiver archivedDataWithRootObject:credential requiringSecureCoding:YES error:&archiveError]; if (!credentialData) { NSLog(@"归档凭证失败: %@", archiveError); return errSecParam; } NSMutableDictionary *update = [NSMutableDictionary dictionary]; update[(__bridge id)kSecValueData] = credentialData; // 移除单独的kSecAttrAccessible设置,避免与Access Control规则冲突 // update[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAlways; [queryDictionary addEntriesFromDictionary:update]; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)queryDictionary, NULL); return status; }
额外注意事项
- 确保设备已设置锁屏密码,因为你使用的
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly规则要求设备必须有密码,否则SecAccessControlCreateWithFlags会返回nil。 - 如需调整可访问性规则,直接修改
SecAccessControlCreateWithFlags的第二个参数即可,不要单独添加kSecAttrAccessible字段。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

