macOS下C++/Objective-C获取.app签名证书信息的替代方案
绝对有办法!在macOS上,你可以直接用系统提供的Security.framework和CodeSigning.framework的原生API来实现这个需求,完全不需要调用终端命令,用Objective-C或者混编C++都能搞定。下面是具体的实现思路和代码示例:
核心依赖框架
macOS的代码签名和证书管理都依赖两个系统框架:
- Security.framework:提供证书解析、读取证书字段的核心API
- CodeSigning.framework:用于获取已签名应用的代码签名信息
完整代码示例
下面是一个可以直接运行的Objective-C示例,它会读取指定.app的签名证书,提取主题名称和序列号:
#import <Security/Security.h> #import <CodeSigning/CodeSigning.h> #import <Foundation/Foundation.h> // 定义获取证书信息的函数,兼容C风格调用 BOOL getAppCertificateInfo(const char *appPath, char **outSubjectName, CFStringRef *outSerialNumber) { // 初始化输出参数为NULL *outSubjectName = NULL; *outSerialNumber = NULL; // 将C字符串路径转为CFString和CFURL CFStringRef pathCFStr = CFStringCreateWithCString(NULL, appPath, kCFStringEncodingUTF8); CFURLRef appURL = CFURLCreateWithFileSystemPath(NULL, pathCFStr, kCFURLPOSIXPathStyle, true); CFRelease(pathCFStr); // 创建静态代码对象(代表已签名的.app) SecStaticCodeRef staticCode = NULL; OSStatus status = SecStaticCodeCreateWithPath(appURL, kSecCSDefaultFlags, &staticCode); CFRelease(appURL); if (status != errSecSuccess) { fprintf(stderr, "❌ 无法创建静态代码对象,错误码: %d\n", (int)status); return NO; } // 获取代码签名信息字典 CFDictionaryRef signingInfo = NULL; status = SecCodeCopySigningInformation(staticCode, kSecCSRequirementInformation, &signingInfo); CFRelease(staticCode); if (status != errSecSuccess) { fprintf(stderr, "❌ 无法获取签名信息,错误码: %d\n", (int)status); return NO; } // 从签名信息中提取证书链 CFArrayRef certChain = CFDictionaryGetValue(signingInfo, kSecCodeInfoCertificates); if (!certChain || CFArrayGetCount(certChain) == 0) { fprintf(stderr, "❌ 签名中未找到证书\n"); CFRelease(signingInfo); return NO; } // 取出证书链中的第一个证书(叶子证书,即直接给app签名的证书) SecCertificateRef leafCert = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0); // 1. 获取证书主题名称(友好格式) CFStringRef subjectSummary = SecCertificateCopySubjectSummary(leafCert); if (subjectSummary) { // 将CFString转为C字符串输出 CFIndex maxBufferSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(subjectSummary), kCFStringEncodingUTF8) + 1; *outSubjectName = (char *)malloc(maxBufferSize); if (*outSubjectName) { CFStringGetCString(subjectSummary, *outSubjectName, maxBufferSize, kCFStringEncodingUTF8); } CFRelease(subjectSummary); } // 2. 获取证书序列号(转为十六进制字符串) CFDataRef serialData = SecCertificateCopySerialNumber(leafCert); if (serialData) { NSMutableString *serialHexStr = [NSMutableString stringWithCapacity:CFDataGetLength(serialData) * 2]; const UInt8 *bytes = CFDataGetBytePtr(serialData); for (CFIndex i = 0; i < CFDataGetLength(serialData); i++) { [serialHexStr appendFormat:@"%02X", bytes[i]]; } *outSerialNumber = CFStringCreateCopy(NULL, (__bridge CFStringRef)serialHexStr); CFRelease(serialData); } CFRelease(signingInfo); return YES; } // 测试主函数 int main(int argc, const char * argv[]) { @autoreleasepool { // 替换为你的.app路径 const char *targetAppPath = "/Applications/YourApp.app"; char *subjectName = NULL; CFStringRef serialNumber = NULL; if (getAppCertificateInfo(targetAppPath, &subjectName, &serialNumber)) { printf("✅ 证书主题名称: %s\n", subjectName); printf("✅ 证书序列号: %s\n", CFStringGetCStringPtr(serialNumber, kCFStringEncodingUTF8)); // 记得释放内存 free(subjectName); CFRelease(serialNumber); } else { fprintf(stderr, "❌ 获取证书信息失败\n"); return 1; } } return 0; }
关键细节说明
- 证书选择:代码中取的是证书链的第一个证书(叶子证书),这就是直接给你的.app签名的开发者证书,和终端
codesign -dv --verbose=4 YourApp.app输出的Authority字段中最顶层的证书一致。 - 主题名称:
SecCertificateCopySubjectSummary返回的是友好的摘要格式(比如Apple Development: John Doe (ABC123XYZ)),如果需要解析更详细的主题字段(如CN、O、OU等),可以用SecCertificateCopyValues来读取ASN.1结构中的具体字段。 - 序列号格式:代码中将二进制序列号转为大写十六进制字符串,和终端命令输出的序列号格式完全一致。
- 权限问题:如果你的.app位于系统受保护目录(如
/System/Applications),需要以root权限运行程序才能读取签名信息。
编译与运行
- 在Xcode中:新建Command Line Tool项目,添加Security.framework和CodeSigning.framework到项目依赖,然后粘贴上述代码。
- 用clang编译:在终端执行以下命令(假设代码文件名为
CertInfo.m):
运行时直接执行clang -o CertInfo CertInfo.m -framework Security -framework CodeSigning -framework Foundation./CertInfo即可。
内容的提问来源于stack exchange,提问作者Vuwox
相关产品推荐
相关产品推荐

