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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 17:27:30