You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:06:52