iOS应用运行时能否自省获取自身证书信息(含过期日期)?
iOS应用运行时读取自身证书过期日期的实现方案
要实现iOS应用在运行时获取自身证书的过期日期,有两种可靠的方案,适合内部应用用来提示用户应用即将失效:
方案一:读取应用签名中的证书信息
通过Security框架提供的API,直接提取应用签名包中的证书链信息,进而获取过期日期。这种方法不需要额外打包证书文件,直接读取应用自身的签名数据。
Swift 实现代码
import Security func fetchAppCertExpirationDate() -> Date? { guard let bundleURL = Bundle.main.bundleURL as CFURL else { return nil } var secCode: SecCode? let codeStatus = SecCodeCreateWithPath(bundleURL, [], &secCode) guard codeStatus == errSecSuccess, let code = secCode else { return nil } var signingInfo: CFDictionary? let infoStatus = SecCodeCopySigningInformation(code, .default, &signingInfo) guard infoStatus == errSecSuccess, let infoDict = signingInfo as? [String: Any] else { return nil } guard let certs = infoDict[kSecCodeInfoCertificates as String] as? [SecCertificate], let targetCert = certs.first else { return nil } var trust: SecTrust? let trustStatus = SecTrustCreateWithCertificates(targetCert, SecPolicyCreateBasicX509(), &trust) guard trustStatus == errSecSuccess, let secTrust = trust else { return nil } let certCount = SecTrustGetCertificateCount(secTrust) for index in 0..<certCount { guard let cert = SecTrustGetCertificateAtIndex(secTrust, index) else { continue } if let expDate = SecCertificateCopyExpirationDate(cert) as Date? { return expDate } } return nil }
Objective-C 实现代码
#import <Security/Security.h> - (NSDate *)fetchAppCertExpirationDate { NSURL *bundleURL = [[NSBundle mainBundle] bundleURL]; SecCodeRef codeRef = NULL; OSStatus codeStatus = SecCodeCreateWithPath((__bridge CFURLRef)bundleURL, NULL, &codeRef); if (codeStatus != errSecSuccess || !codeRef) { return nil; } CFDictionaryRef signingInfoRef = NULL; OSStatus infoStatus = SecCodeCopySigningInformation(codeRef, kSecCSDefaultFlags, &signingInfoRef); if (infoStatus != errSecSuccess || !signingInfoRef) { CFRelease(codeRef); return nil; } NSArray *certificates = (__bridge NSArray *)CFDictionaryGetValue(signingInfoRef, kSecCodeInfoCertificates); if (!certificates || certificates.count == 0) { CFRelease(signingInfoRef); CFRelease(codeRef); return nil; } SecCertificateRef targetCert = (__bridge SecCertificateRef)certificates.firstObject; SecTrustRef trustRef = NULL; OSStatus trustStatus = SecTrustCreateWithCertificates(targetCert, SecPolicyCreateBasicX509(), &trustRef); if (trustStatus != errSecSuccess || !trustRef) { CFRelease(signingInfoRef); CFRelease(codeRef); return nil; } NSDate *expirationDate = nil; CFIndex certCount = SecTrustGetCertificateCount(trustRef); for (CFIndex i = 0; i < certCount; i++) { SecCertificateRef cert = SecTrustGetCertificateAtIndex(trustRef, i); if (cert) { expirationDate = (__bridge NSDate *)SecCertificateCopyExpirationDate(cert); if (expirationDate) { break; } } } CFRelease(trustRef); CFRelease(signingInfoRef); CFRelease(codeRef); return expirationDate; }
注意事项
- 无需额外权限,因为仅读取当前应用的签名数据
- 依赖Security框架,需确保项目已导入该框架
- 通常取证书链中的第一个证书即可(对应应用的签名证书)
方案二:读取嵌入Bundle的证书文件
如果内部应用会将证书文件(如.cer或.p12)打包进App Bundle,可直接读取文件并解析过期日期,这种方法更直接可控。
Swift 实现代码
import Security func fetchEmbeddedCertExpirationDate() -> Date? { // 替换为你的证书文件名和后缀 guard let certPath = Bundle.main.path(forResource: "internal_app_cert", ofType: "cer"), let certData = try? Data(contentsOf: URL(fileURLWithPath: certPath)) else { return nil } guard let certRef = SecCertificateCreateWithData(nil, certData as CFData) else { return nil } var trust: SecTrust? let trustStatus = SecTrustCreateWithCertificates(certRef, SecPolicyCreateBasicX509(), &trust) guard trustStatus == errSecSuccess, let secTrust = trust else { return nil } let certCount = SecTrustGetCertificateCount(secTrust) for index in 0..<certCount { guard let cert = SecTrustGetCertificateAtIndex(secTrust, index) else { continue } if let expDate = SecCertificateCopyExpirationDate(cert) as Date? { return expDate } } return nil }
使用场景
适合内部应用主动管理证书的场景,比如证书更新时只需替换Bundle中的文件即可,无需重新签名应用。
调用上述方法获取到过期日期后,你可以在应用启动、首页加载等时机,检查当前日期与过期日期的差值,提前N天(如7天)向用户弹出提示,告知应用即将无法正常使用。
内容的提问来源于stack exchange,提问作者ocroquette
相关产品推荐
相关产品推荐

