【问题标题】:Verifying the SSL certificate on iOS在 iOS 上验证 SSL 证书
【发布时间】:2014-11-09 16:51:56
【问题描述】:

是否可以在 iOS 上查看 SSL 证书指纹?

奖金回合:当我延长证书时,指纹会改变吗?如果验证指纹,在扩展证书时是否有任何特殊注意事项?

谢谢

【问题讨论】:

    标签: objective-c security ssl ssl-certificate


    【解决方案1】:

    为了验证指纹/指纹,我使用NSURLAuthenticationChallenge 上的一个类别。您不必使用类别或可以使用不同的输入,但获取证书指纹的代码实际上是相同的。

    NSURLAuthenticationChallenge+Fingerprint.h

    @import Foundation;
    
    @interface NSURLAuthenticationChallenge (Fingerprint)
    
    - (NSString *)SHA1Fingerprint;
    - (NSString *)MD5Fingerprint;
    
    @end
    

    NSURLAuthenticationChallenge+Fingerprint.m

    #import "NSURLAuthenticationChallenge+Fingerprint.h"
    #import <CommonCrypto/CommonCrypto.h>
    
    typedef NS_ENUM(NSInteger, kFingerprintType) {
        kFingerprintTypeSHA1,
        kFingerprintTypeMD5
    };
    
    @implementation NSURLAuthenticationChallenge (Fingerprint)
    
    - (NSString *)SHA1Fingerprint
    {
        return [self fingerprintWithType:kFingerprintTypeSHA1];
    }
    
    - (NSString *)MD5Fingerprint
    {
        return [self fingerprintWithType:kFingerprintTypeMD5];
    }
    
    - (NSString *)fingerprintWithType:(kFingerprintType)type
    {
        SecTrustRef serverTrust = [[self protectionSpace] serverTrust];
        SecTrustResultType trustResultType;
        SecTrustEvaluate(serverTrust, &trustResultType);
    
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, (SecTrustGetCertificateCount(serverTrust) - 1));
        NSData *data = CFBridgingRelease(SecCertificateCopyData(certificate));
    
        const NSUInteger length = [self lengthWithType:type];
        unsigned char buffer[length];
    
        switch (type) {
            case kFingerprintTypeSHA1: {
                CC_SHA1(data.bytes, (CC_LONG)data.length, buffer);
                break;
            }
            case kFingerprintTypeMD5: {
                CC_MD5(data.bytes, (CC_LONG)data.length, buffer);
                break;
            }
        }
    
        NSMutableString *fingerprint = [NSMutableString stringWithCapacity:length * 3];
    
        for (int i = 0; i < length; i++) {
            [fingerprint appendFormat:@"%02x ",buffer[i]];
        }
    
        return [fingerprint stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    }
    
    - (NSUInteger)lengthWithType:(kFingerprintType)type
    {
        switch (type) {
            case kFingerprintTypeSHA1: {
                return CC_SHA1_DIGEST_LENGTH;
            }
            case kFingerprintTypeMD5: {
                return CC_MD5_DIGEST_LENGTH;
            }
        }
    }
    

    使用示例代码:

    #pragma mark - UIViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        NSURL *url = [NSURL URLWithString:@"YOUR_HTTPS_URL"];
    
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    
        NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            // Do something meaningful
        }];
    
        [task resume];
    }
    
    #pragma mark - NSURLSessionDelegate
    
    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            NSLog(@"%@", challenge.SHA1Fingerprint);
            NSLog(@"%@", challenge.MD5Fingerprint);
        }
    
        // Do something meaningful
    }
    

    我会得到输出:

    2014-11-17 00:09:10.880 test[48237:2922518] f9 d5 24 c2 08 6b bf 12 6f 48 cd 8a f0 4d ca 3e 7c f0 3f bc
    2014-11-17 00:09:10.880 test[48237:2922518] bf 30 1a 8d f9 cb 15 bd 51 73 c8 22 a5 54 62 8a
    

    Safari可用于验证数据:

    关于扩展验证证书,它们不是不同类型的证书,它们具有相同的机制,但证书策略字段将使用特定的证书策略标识符。

    指纹是整个证书的哈希,经过任何修改(如使用 EV 证书),指纹会有所不同,但获取指纹的过程是相同的。

    【讨论】:

    • 您好,感谢您的回复。关于扩展的问题是关于证书过期并使用相同的密钥和 CSR 扩展时会发生什么,而不是关于 EV 证书。
    • 嗬,我的错,没有意识到你在谈论更新,所以是的,如果你用相同的密钥和 CSR 更新,你更新的证书会有不同的指纹。
    • 这方面有什么好的惯例吗?如果我将指纹硬编码到应用程序中,我需要在证书过期之前提交一个新应用程序以供审核。
    • 关于这个特殊情况,我从来没有找到任何建议或最佳实践可以遵循。我喜欢做的是在应用程序的定期更新期间包含新值。在过渡期间,我为旧值和新值都提供了到期日期,以便应用程序可以在过渡期间回退到旧值(如果未过期),从而为最终用户留出时间。如果两个值都无法验证,我会警告用户有可用的更新并且应该安装。
    • 这对我有用,但您正在检查根 CA。你能告诉我如何检查中级和叶级证书吗?
    【解决方案2】:

    对于那些感兴趣的人;这是用 Swift 翻译的扩展:

    enum FingerprintType {
        case SHA1
        case MD5
    }
    
    extension NSURLAuthenticationChallenge {
    
        func getSHA1Fingerprint() -> String {
            return getFingerprintWithType(.SHA1)
        }
    
        func getMD5Fingerprint() -> String {
            return getFingerprintWithType(.MD5)
        }
    
        private func getFingerprintWithType(type : FingerprintType) -> String {
            var serverTrust = self.protectionSpace.serverTrust
            SecTrustEvaluate(serverTrust, nil)
    
            var certificate = SecTrustGetCertificateAtIndex(serverTrust, 0).takeUnretainedValue()
            var data = SecCertificateCopyData(certificate).takeUnretainedValue() as NSData
    
            let length = self.lengthWityType(type)
            var buffer = [UInt8](count:Int(length), repeatedValue: 0)
    
            switch(type) {
                case .SHA1:
                    CC_SHA1(data.bytes, CC_LONG(data.length), &buffer)
                    break
                case .MD5:
                    CC_MD5(data.bytes, CC_LONG(data.length), &buffer)
            }
    
            var fingerPrint = NSMutableString()
            for byte in buffer {
                fingerPrint.appendFormat("%02x ", byte)
            }
    
            return fingerPrint.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
        }
    
        private func lengthWityType(type : FingerprintType) -> Int32 {
            switch type {
                case .SHA1:
                    return CC_SHA1_DIGEST_LENGTH
                case .MD5:
                    return CC_MD5_DIGEST_LENGTH
                default:
                return 0
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-06-13
      • 2019-03-29
      • 2018-08-19
      • 2014-09-24
      • 1970-01-01
      • 2012-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多