4

ファイルとして提供されたssl証明書(.cer)があります。バンドルに追加し、サーバーと通信して使用したい。

私はアップルが提供するコードを使用しました:

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
    DLog(@"didReceiveAuthenticationChallenge : %@",challenge);
    if ([challenge.protectionSpace.authenticationMethod
         isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];
        NSData *certData = [NSData dataWithContentsOfFile:filePath];
        CFDataRef myCertData = (__bridge CFDataRef)certData; 
        SecCertificateRef myCert = SecCertificateCreateWithData(NULL,
                                                                myCertData);
        SecPolicyRef myPolicy = SecPolicyCreateBasicX509();         // 3
        SecCertificateRef certArray[1] = { myCert };
        CFArrayRef myCerts = CFArrayCreate(NULL,
                                           (void *)certArray,
                                           1,
                                           NULL);
        SecTrustRef myTrust;
        OSStatus status = SecTrustCreateWithCertificates(
                                                         myCerts,
                                                         myPolicy,
                                                         &myTrust);  // 4
        SecTrustResultType trustResult = 0;
        if (status == noErr) {
            status = SecTrustEvaluate(myTrust, &trustResult);       // 5
        }
        // If the trust result is kSecTrustResultInvalid, kSecTrustResultDeny, kSecTrustResultFatalTrustFailure, you cannot proceed and should fail gracefully.
        BOOL proceed = NO;
        switch (trustResult) {
            case kSecTrustResultProceed: // 1
                DLog(@"Proceed");
                proceed = YES;
                break;
            case kSecTrustResultConfirm: // 2
                DLog(@"Confirm");
                proceed = YES;
                break;
            case kSecTrustResultUnspecified: // 4
                DLog(@"Unspecified");
                break;
            case kSecTrustResultRecoverableTrustFailure:  // 5
                DLog(@"TrustFailure");
                proceed = [self recoverFromTrustFailure:myTrust];
                break;
            case kSecTrustResultDeny: // 3
                DLog(@"Deny");
                break;
            case kSecTrustResultFatalTrustFailure: // 6
                DLog(@"FatalTrustFailure");
                break;
            case kSecTrustResultOtherError: // 7
                DLog(@"OtherError");
                break;
            case kSecTrustResultInvalid: // 0
                DLog(@"Invalid");
                break;
            default:
                DLog(@"Default");
                break;
        }
        if (myPolicy)
            CFRelease(myPolicy);
        if (proceed) {
            [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
        }else{
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
    }
}

- (BOOL) recoverFromTrustFailure:(SecTrustRef) myTrust
{
    SecTrustResultType trustResult;
    OSStatus status = SecTrustEvaluate(myTrust, &trustResult);  // 1
    //Get time used to verify trust
    CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
    CFDateRef newDate;
    if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
        trustTime = SecTrustGetVerifyTime(myTrust);             // 3
        timeIncrement = 31536000;                               // 4
        currentTime = CFAbsoluteTimeGetCurrent();               // 5
        newTime = currentTime - timeIncrement;                  // 6
        if (trustTime - newTime){                               // 7
            newDate = CFDateCreate(NULL, newTime);              // 8
            SecTrustSetVerifyDate(myTrust, newDate);            // 9
            status = SecTrustEvaluate(myTrust, &trustResult);   // 10
        }
    }
    if (trustResult != kSecTrustResultProceed) {
        DLog(@"Failed with status : %li",trustResult);               // 11
        return NO;
    }else{
        DLog(@"Procced");
        return YES;
    }
}

ただし、kSecTrustResultRecoverableTrustFailureを取得しています。この状況ではリンゴのサンプルも使用しましたが、役に立ちませんでした。

多分誰かがこれについて私を助けることができますか?

ありがとうございました。

4

1 に答える 1

11

これがサーバー信頼認証で使用される自己署名証明書である場合は、次の手順を実行する必要があります。

  1. .CRT でエンコードされた証明書を .DER でエンコードされた証明書に変換します。端末タイプで:

    $: openssl x509 -in certificate.crt -outform der -out "com.server.trust_cert.der"

    (意味のある名前を選んでください)

    .DER でエンコードされた証明書をバンドルに入れます。

  2. メソッドを次のように実装connection:didReceiveAuthenticationChallenge:します。重要: 常にエラーをチェックして救済し、何か問題がある場合は認証を失敗させてください!!

    徹底的にテストしてください!

- (void)connection:(NSURLConnection *)connection
    didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
    {
        do
        {
            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
            if (serverTrust == nil)
                break; // failed

            SecTrustResultType trustResult;
            OSStatus status = SecTrustEvaluate(serverTrust, &trustResult);
            if (!(errSecSuccess == status))
                break; // fatal error in trust evaluation -> failed

            if (!((trustResult == kSecTrustResultProceed) 
               || (trustResult == kSecTrustResultUnspecified)))
            {
                break; // see "Certificate, Key, and Trust Services Reference" 
                       // for explanation of result codes.
            }

            SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
            if (serverCertificate == nil)
                break; // failed

            CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate);
            if (serverCertificateData == nil)
                break; // failed

            const UInt8* const data = CFDataGetBytePtr(serverCertificateData);
            const CFIndex size = CFDataGetLength(serverCertificateData);
            NSData* server_cert = [NSData dataWithBytes:data length:(NSUInteger)size];
            CFRelease(serverCertificateData);

            NSString* file = [[NSBundle mainBundle] pathForResource:@"com.server.trust_cert"
                                                             ofType:@"der"];
            NSData* my_cert = [NSData dataWithContentsOfFile:file];

            if (server_cert == nil || my_cert == nil)
                break; // failed

            const BOOL equal = [server_cert isEqualToData:my_cert];
            if (!equal)
                break; // failed 

            // Athentication succeeded:
            return [[challenge sender] useCredential:[NSURLCredential credentialForTrust:serverTrust]
                          forAuthenticationChallenge:challenge];
        } while (0);

        // Authentication failed: 
        return [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

ノート:

上記の手法の可能な改善は、「公開鍵の固定」を使用することです。

必読:

HTTPS サーバーの信頼性評価(Apple の公式ドキュメント、テクニカル ノート TN2232)

証明書、鍵、および信頼サービスのリファレンス(Apple の公式リファレンス ドキュメント)

于 2013-06-17T11:34:46.110 に答える