验证iOS上的SSL证书

时间:2014-11-09 16:51:56

标签: objective-c security ssl ssl-certificate

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

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

由于

2 个答案:

答案 0 :(得分:14)

要验证指纹/指纹,我在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可用于验证数据:

Safari certificate fingerprint

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

指纹是整个证书的散列,经过任何修改(如使用EV证书),指纹会有所不同,但获取指纹的过程也是一样的。

答案 1 :(得分:3)

对于那些感兴趣的人;这是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
        }
    }
}