如何在macOS原生应用中通过API获取签名身份列表?
在macOS原生应用中通过API获取可用的代码签名身份列表
完全可以用macOS原生的Security.framework实现这个需求,不用再依赖命令行解析结果啦!下面是具体的实现思路和代码示例:
核心API说明
你需要用到系统原生的Security框架,它专门处理钥匙串、证书、密钥相关的安全操作,关键API包括:
SecIdentitySearchCreate:创建身份搜索查询,指定筛选条件(比如代码签名用途)和搜索范围SecIdentitySearchCopyNext:遍历搜索结果,逐个获取身份对象(SecIdentityRef,包含证书+私钥的组合)SecIdentityCopyCertificate/SecIdentityCopyPrivateKey:从身份对象中提取对应的证书和私钥SecCertificateCopySubjectSummary:获取证书的可读名称(就是你命令行里看到的类似"iPhone Developer: John Doe (GTHESFW12)"的字符串)- 通过SHA-1哈希计算获取证书的唯一标识(对应命令行里的那串十六进制字符串)
代码实现示例
Objective-C 版本
#import <Security/Security.h> - (void)fetchValidCodeSigningIdentities { // 创建搜索查询:指定代码签名用途,搜索所有可访问的钥匙串 SecIdentitySearchRef searchRef; OSStatus status = SecIdentitySearchCreate(NULL, kSecCodeSigning, &searchRef); if (status != errSecSuccess) { NSLog(@"身份搜索创建失败:%d", (int)status); return; } SecIdentityRef identityRef; // 遍历所有符合条件的身份 while (SecIdentitySearchCopyNext(searchRef, &identityRef) == errSecSuccess) { // 获取对应证书 SecCertificateRef certRef; status = SecIdentityCopyCertificate(identityRef, &certRef); if (status == errSecSuccess) { // 提取证书可读名称 CFStringRef subjectSummary = SecCertificateCopySubjectSummary(certRef); NSString *certName = (__bridge_transfer NSString *)subjectSummary; // 计算证书SHA-1哈希(对应命令行里的标识串) CFDataRef certData = SecCertificateCopyData(certRef); unsigned char digest[CC_SHA1_DIGEST_LENGTH]; CC_SHA1(CFDataGetBytePtr(certData), (CC_LONG)CFDataGetLength(certData), digest); CFRelease(certData); // 转换为十六进制字符串 NSMutableString *hashString = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { [hashString appendFormat:@"%02X", digest[i]]; } NSLog(@"[标识] %@ | [名称] %@", hashString, certName); CFRelease(certRef); } // 验证私钥是否可用(身份对象本身已保证证书+私钥配对,可额外确认) SecKeyRef privateKeyRef; status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef); if (status == errSecSuccess) { // 私钥存在且可用,可做后续操作 CFRelease(privateKeyRef); } CFRelease(identityRef); } CFRelease(searchRef); }
Swift 版本
import Security func fetchValidCodeSigningIdentities() { var searchRef: SecIdentitySearchRef? let status = SecIdentitySearchCreate(nil, kSecCodeSigning, &searchRef) guard status == errSecSuccess, let search = searchRef else { print("身份搜索创建失败:\(status)") return } defer { CFRelease(search) } // 确保释放资源 var identityRef: SecIdentityRef? while SecIdentitySearchCopyNext(search, &identityRef) == errSecSuccess { guard let identity = identityRef else { continue } defer { CFRelease(identity) } // 获取证书对象 var certRef: SecCertificateRef? let certStatus = SecIdentityCopyCertificate(identity, &certRef) guard certStatus == errSecSuccess, let cert = certRef else { continue } defer { CFRelease(cert) } // 提取证书可读名称 if let subjectSummary = SecCertificateCopySubjectSummary(cert) { let certName = subjectSummary as String print("证书名称:\(certName)") CFRelease(subjectSummary) } // 计算证书SHA-1哈希标识 let certData = SecCertificateCopyData(cert) as Data var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) certData.withUnsafeBytes { _ = CC_SHA1($0.baseAddress, CC_LONG(certData.count), &digest) } let hashString = digest.map { String(format: "%02X", $0) }.joined() print("证书标识:\(hashString)") // 验证私钥可用性 var privateKeyRef: SecKeyRef? let keyStatus = SecIdentityCopyPrivateKey(identity, &privateKeyRef) if keyStatus == errSecSuccess, let _ = privateKeyRef { // 私钥可用,可执行后续逻辑 CFRelease(privateKeyRef!) } } }
关键注意事项
- 沙盒权限:如果你的应用开启了沙盒,需要在
Entitlements文件中配置钥匙串访问权限(比如添加keychain-access-groups),否则可能无法访问所有签名身份。 - 错误处理:务必妥善处理
OSStatus返回值,比如errSecAuthFailed表示需要用户授权访问钥匙串,errSecItemNotFound表示遍历结束。 - 内存管理:Core Foundation对象需要手动调用
CFRelease释放,避免内存泄漏(Swift中部分桥接对象会自动管理,但仍需注意显式释放)
内容的提问来源于stack exchange,提问作者sha
相关产品推荐
相关产品推荐

