2017-02-06 70 views
0

我使用NSURLSessionDelegate的(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler具有挑戰性的服務器認證是這樣的:iOS不接受有效的自簽名證書,並接受無效問卷

BOOL trusted = NO; 
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { 

    if (!cert) { 

     NSURL* certURL = [[NSBundle mainBundle] URLForResource: @"cert_new" withExtension: @"der"]; 
     NSData* certData = [NSData dataWithContentsOfURL: certURL]; 
     cert = SecCertificateCreateWithData(kCFAllocatorDefault, CFBridgingRetain(certData)); 

    } 

    SecPolicyRef policyRef = SecPolicyCreateBasicX509(); 
    SecCertificateRef certArray[1] = { cert }; 
    CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)certArray, 1, NULL); 
    SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; 

    // Create a policy that ignores the host name… 

    OSStatus err = SecTrustCreateWithCertificates(CFBridgingRetain((__bridge id _Nullable)(certArrayRef)), policyRef, &serverTrust); 
    CFRelease(policyRef); 
    if (err != noErr) 
    { 
     XLog(@"Error creating trust: %d", (int)err); 
     [challenge.sender cancelAuthenticationChallenge: challenge]; 
     return; 
    } 

    err = SecTrustSetAnchorCertificates(serverTrust, certArrayRef); 
    if (err == noErr) 
    { 
     SecTrustResultType trustResult; 
     err = SecTrustEvaluate(serverTrust, &trustResult); 

     trusted = (err == noErr && (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)); 
    } 
    CFRelease(certArrayRef); 
    CFRelease(policyRef); 
    CFRelease(cert); 

} 
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; 
if (trusted) { 

    [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; 
    return; 


} else { 
    [challenge.sender cancelAuthenticationChallenge: challenge]; 
} 

我必須使用證書是PEM格式。於是我通過OpenSSL的轉換它DER格式是這樣的:

openssl x509 -in pem_file.pem -out cert_new.der -outform DER 

現在好像每個證書(無論是否爲有效與否)一定比特長度將被接受:trusted將被評估爲YES。由於kSecTrustResultRecoverableTrustFailure,位長度較低的有效證書將被評估爲trusted = NO。奇怪的行爲...有人可以向我解釋如何做對嗎?

謝謝!

回答

0

現在我有一個工作版本:

// Build a new trust based on the supplied trust, so that we can set the policy… 
NSURLProtectionSpace* protectionSpace = challenge.protectionSpace; 
SecTrustRef trust = protectionSpace.serverTrust; 

CFIndex numCerts = SecTrustGetCertificateCount(trust); 
NSMutableArray* certs = [NSMutableArray arrayWithCapacity: numCerts]; 
for (CFIndex idx = 0; idx < numCerts; ++idx) 
{ 
    SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, idx); 
    [certs addObject: CFBridgingRelease(cert)]; 

} 

// Create a policy that ignores the host name… 
SecPolicyRef policy = SecPolicyCreateSSL(true, NULL); // SecPolicyCreateBasicX509(); 
OSStatus err = SecTrustCreateWithCertificates(CFBridgingRetain(certs), policy, &trust); 
CFRelease(policy); 
if (err != noErr) 
{ 
    //NSLogDebug(@"Error creating trust: %d", err); 
    [challenge.sender cancelAuthenticationChallenge: challenge]; 
    return; 
} 

// Set the root cert and evaluate the trust… 
NSURL* certURL = [[NSBundle mainBundle] URLForResource: @"doorbird_certificate" withExtension: @"der"]; 
NSData* certData = [NSData dataWithContentsOfURL: certURL]; 
SecCertificateRef rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, CFBridgingRetain(certData)); 

NSArray* rootCerts = @[ CFBridgingRelease(rootCert) ]; 
err = SecTrustSetAnchorCertificates(trust, CFBridgingRetain(rootCerts)); 
if (err == noErr) 
{ 
    SecTrustResultType trustResult; 
    err = SecTrustEvaluate(trust, &trustResult); 
    SecTrustSetAnchorCertificatesOnly(trust, YES); 
    NSURLCredential* credential = [NSURLCredential credentialForTrust: trust]; 

    CFRelease(trust); 

    bool trusted = err == noErr; 
    trusted = trusted && (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified); 
    if (trusted) 
    { 

     [challenge.sender useCredential: credential forAuthenticationChallenge: challenge]; 
     return; 
    } 
} 

// An error occurred, or we don't trust the cert, so disallow it… 

[challenge.sender cancelAuthenticationChallenge: challenge]; 

但只有證書用最少的1024位的都在工作。 512位的證書沒有。有人可以證實嗎?

感謝

編輯:

我創建像這裏所描述的證書:

http://www.akadia.com/services/ssh_test_certificate.html