Programing

개발 목적으로 iOS 7의 NSURLSession 및 위임 메서드 제품군을 사용하여 자체 서명 된 SSL 인증서를 수락하려면 어떻게해야합니까?

lottogame 2020. 12. 14. 07:41
반응형

개발 목적으로 iOS 7의 NSURLSession 및 위임 메서드 제품군을 사용하여 자체 서명 된 SSL 인증서를 수락하려면 어떻게해야합니까?


iPhone 앱을 개발 중입니다. 개발 중에 자체 서명 된 SSL 인증서를 사용하는 서버에 연결해야합니다. 나는 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler이것을 허용하는 몇 가지 예외 코드를 작성할 기회가 있다고 확신 합니다. 그러나이 작업을 수행하는 방법을 알려주는 리소스를 찾을 수 없습니다. 로그에서 다음 오류를 볼 수 있습니다.

NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

이 외에도 NSLog(@"error = %@", error);위의 대리자 메서드 내에서 다음을 얻습니다.

오류 도메인 = NSURLErrorDomain 코드 = -1202 "이 서버의 인증서가 유효하지 않습니다."api.mydevelopmenturl.com "인 것처럼 가장하는 서버에 연결하여 기밀 정보를 위험에 빠뜨릴 수 있습니다." UserInfo = 0x10cbdbcf0 {NSUnderlyingError = 0x112ec9730 "이 서버에 대한 인증서가 유효하지 않습니다."api.mydevelopmenturl.com "인 것처럼 가장하는 서버에 연결하여 기밀 정보를 위험에 빠뜨릴 수 있습니다.", NSErrorFailingURLStringKey = https : //api.mydevelopmenturl.com/posts , NSErrorFailingURLKey = https://api.mydevelopmenturl.com/posts, NSLocalizedRecoverySuggestion = 그래도 서버에 연결 하시겠습니까?, NSURLErrorFailingURLPeerTrustErrorKey =, NSLocalizedDescription =이 서버의 인증서가 유효하지 않습니다. 기밀 정보를 위험에 빠뜨릴 수있는 "api.mydevelopmenturl.com"인 것처럼 가장하는 서버에 연결하고있을 수 있습니다.}

이 문제를 해결하는 방법에 대한 아이디어가 있습니까? 개념 문서를 읽었는데 이해가 안되므로 코드를 게시 해주세요. 다음은 저를 넘어선 예입니다 : https://developer.apple.com/library/content/technotes/tn2232/_index.html


이것은 나를 위해 작동합니다.

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:Nil];
...
...
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
    if([challenge.protectionSpace.host isEqualToString:@"mydomain.com"]){
      NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
      completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }
  }
}

Apple에는 매우 유익하고 HTTPS 서버 신뢰 평가에 대해 자세히 설명 하는 Technical Note 2232있습니다.

이 경우 NSURLErrorDomain도메인의 오류 -1202 NSURLErrorServerCertificateUntrusted이며 이는 서버 신뢰 평가가 실패했음을 의미합니다. 또한 다양한 기타 오류를 수신 할 수 있습니다. 부록 A : 일반적인 서버 신뢰 평가 오류 에는 가장 일반적인 오류가 나열되어 있습니다.

기술 노트에서 :

대부분의 경우 서버 신뢰 평가 실패를 해결하는 가장 좋은 방법은 서버를 수정하는 것입니다. 이는 두 가지 이점이 있습니다. 최고의 보안을 제공하고 작성해야하는 코드의 양을 줄입니다. 이 기술 노트의 나머지 부분에서는 서버 신뢰 평가 실패를 진단하는 방법과 서버를 수정할 수없는 경우 사용자의 보안을 완전히 손상시키지 않고 연결을 진행할 수 있도록 서버 신뢰 평가를 사용자 정의하는 방법에 대해 설명합니다.

이 질문과 관련된 특정 부분은 NSURLSession 서버 신뢰 평가 섹션입니다 .

NSURLSession-URLSession:didReceiveChallenge:completionHandler:위임 메소드 를 구현하여 HTTPS 서버 신뢰 평가를 사용자 정의 할 수 있습니다 . HTTPS 서버 신뢰 평가를 사용자 정의하려면 보호 공간에 인증 방법이있는 챌린지를 찾으십시오 NSURLAuthenticationMethodServerTrust. 이러한 문제에 대해서는 아래 설명 된대로 해결하십시오. 신경 쓰지 않는 다른 문제의 경우 NSURLSessionAuthChallengePerformDefaultHandling처리 및 NULL 자격 증명을 사용하여 완료 처리기 블록을 호출합니다 .

NSURLAuthenticationMethodServerTrust 인증 챌린지를 처리 ​​할 때 -serverTrust 메서드를 호출하여 챌린지의 보호 공간에서 신뢰 개체를 가져올 수 있습니다. 신뢰 개체를 사용하여 사용자 지정 HTTPS 서버 신뢰 평가를 수행 한 후 다음 두 가지 방법 중 하나로 문제를 해결해야합니다.

연결을 거부하려면 NSURLSessionAuthChallengeCancelAuthenticationChallenge처리 및 NULL 자격 증명을 사용하여 완료 처리기 블록을 호출합니다 .

연결을 허용하려면 트러스트 개체에서 자격 증명을 만들고 (사용 +[NSURLCredential credentialForTrust:]) 해당 자격 증명과 NSURLSessionAuthChallengeUseCredential처리를 사용 하여 완료 처리기 블록을 호출합니다 .

이 모든 것의 결론은 다음 위임 메서드를 구현하면 특정 서버에 대한 서버 신뢰를 재정의 할 수 있다는 것입니다.

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    if([challenge.protectionSpace.authenticationMethod
                           isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        if([challenge.protectionSpace.host
                           isEqualToString:@"domaintoverride.com"])
        {
            NSURLCredential *credential = 
                          [NSURLCredential credentialForTrust:
                                          challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }
        else
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}

재정의하려는 호스트 다른 모든 경우를 모두 처리해야합니다 . "기타 모든 경우"부분을 처리하지 않으면 동작 결과가 정의되지 않습니다.


새 인증서에 대한 무료 90 일 평가판을 제공하는 신뢰할 수있는 SSL 인증 기관을 온라인에서 찾으십시오. 서버에 인증서를 설치하십시오. 이제 90 일 동안 인증서를 "갱신"하기 위해 돈을 지불 할 가치가 있는지 여부를 결정할 수있는 시점까지 앱을 개발해야합니다. 자체 서명 된 인증서를 사용하기로 결정한 것이 재정적으로 동기가되었고 90 일 동안 SSL 인증서에 돈을 쓸 가치가 있는지 결정할 수있을 정도로 앱을 개발하는 데 충분한 시간이 주어 졌기 때문에 이것이 저에게 가장 좋은 대답입니다. . 이 접근 방식을 사용하면 자체 서명 된 인증서를 허용하도록 조정 된 코드베이스 실행의 보안 관련 문제를 처리 할 필요가 없습니다. 단! 부트 스트랩에 대한 예이!


자신에게 호의를 베풀고하지 마십시오.

용지를 읽어 시작 검증하는 SSL 인증서를 브라우저가 아닌 소프트웨어에서 : 세계에서 가장 위험한 코드를 , 특히 10 절, "속보 또는 인증서 확인을 실행 중지". 구체적으로 요청한 작업을 수행하는 방법을 구체적으로 설명하는 Cocoa 관련 블로그를 호출합니다.

그러나하지 마십시오. SSL 인증서 검사를 비활성화하는 것은 앱에 시한 폭탄을 도입하는 것과 같습니다. 언젠가는 우연히 활성화 된 채로 남겨져 빌드가 야생에 들어갈 것입니다. 그리고 그날 사용자는 심각한 위험에 처하게됩니다.

대신 특정 장치에 설치하고 신뢰할 수있는 중간 인증서로 서명 된 인증서를 사용해야합니다. 그러면 SSL 유효성 검사가 자신의 장치가 아닌 다른 장치를 위험에 빠뜨리지 않고 성공할 수 있습니다 (그런 다음에 만 일시적으로).


friherd의 솔루션과 동일하지만 신속하게 :

func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{
        let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential);
    }
}

Swift 3.0 / 4 용

모든 종류의 자체 서명 인증서를 허용하려는 경우 다음 접근 방식을 사용하여 URLSessionDelegate를 구현할 수 있습니다. Apple은 모든 종류의 인증 방법에 대해 URLSessionDelegate를 사용하는 방법에 대한 추가 정보를 제공합니다. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html

먼저 델리게이트 메서드를 구현하고 그에 따라 델리게이트를 할당합니다.

let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
let task = urlSession.dataTask(with: urlRequest).resume()

이제 대리인의 메서드 https://developer.apple.com/documentation/foundation/nsurlsessiondelegate/1409308-urlsession?language=objc를 구현합니다.

func urlSession(_ session: URLSession, 
     didReceive challenge: URLAuthenticationChallenge, 
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard challenge.previousFailureCount == 0 else {
        challenge.sender?.cancel(challenge)
        // Inform the user that the user name and password are incorrect
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    // Within your authentication handler delegate method, you should check to see if the challenge protection space has an authentication type of NSURLAuthenticationMethodServerTrust
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
       // and if so, obtain the serverTrust information from that protection space.
       && challenge.protectionSpace.serverTrust != nil
       && challenge.protectionSpace.host == "yourdomain.com" {
        let proposedCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
        completionHandler(URLSession.AuthChallengeDisposition.useCredential, proposedCredential)
    }
}

그래도 제공된 도메인에 대한 자체 서명 된 인증서의 수락을 매우 구체적인 도메인과 일치하도록 조정할 수 있습니다. 빌드 대상 번들에 이전에이 인증서를 추가했는지 확인하십시오. 여기서 이름을 "cert.cer"로 지정했습니다.

func urlSession(_ session: URLSession, 
     didReceive challenge: URLAuthenticationChallenge, 
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard challenge.previousFailureCount == 0 else {
        challenge.sender?.cancel(challenge)
        // Inform the user that the user name and password are incorrect
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
       && challenge.protectionSpace.serverTrust != nil
       && challenge.protectionSpace.host == "yourdomain.com" {

        if let trust = challenge.protectionSpace.serverTrust,
           let pem = Bundle.main.url(forResource:"cert", withExtension: "cer"),
           let data = NSData(contentsOf: pem),
           let cert = SecCertificateCreateWithData(nil, data) {
            let certs = [cert]
            SecTrustSetAnchorCertificates(trust, certs as CFArray)
            var result=SecTrustResultType.invalid
            if SecTrustEvaluate(trust,&result)==errSecSuccess {
              if result==SecTrustResultType.proceed || result==SecTrustResultType.unspecified {
                let proposedCredential = URLCredential(trust: trust)
                completionHandler(.useCredential,proposedCredential)
                return
              }
            }

        }
    }
    completionHandler(.performDefaultHandling, nil)
}

SecTrust에 .cer를 추가하면 ATS에 전달됩니다.

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let trust = challenge.protectionSpace.serverTrust,
               let pem = Bundle.main.path(forResource: "https", ofType: "cer"),
               let data = NSData(contentsOfFile: pem),
               let cert = SecCertificateCreateWithData(nil, data) {
                let certs = [cert]
                SecTrustSetAnchorCertificates(trust, certs as CFArray)

                completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: trust))
                return
            }
        }

        // Pinning failed
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }
}

이것은 자체 서명을 통과하여 잘 작동합니다.

Delegate : NSURLSessionDelegate

- (void)URLSession:(NSURLSession *)session **task**:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}

아마도 더 나은 방법은 사용자에게 액세스중인 서비스에 대한 URL이 정확한지 (시각적으로) 확인하는 인증서를 수락 할 수있는 기회를 제공하는 것입니다. 예를 들어 호스트가 일부 앱 설정에 입력 된 경우 사용자 항목에서 테스트하고 사용자가 바로 결정할 수 있도록합니다.

이 "사용자 확인"전술이 사파리에서 사용된다는 점을 고려하면 애플은이를 다른 앱에 논리적으로 사용하는 것이 합리적입니다.

Suggest digging into NSErrorRecoveryAttempting (am doing no myself) http://apple.co/22Au1GR

Get the host confirmed, then take the individual URL exclusion route mentioned herewithin. Depending upon the implementation it may also make sense to store the host as an exclusion for future reference.

This seems like something Apple would have implemented by nature in Cocoa but as of yet, I have not found an 'easy button'. Would have liked a "kLetUserDecide" flag on something in NSURL or NSURLSession instead of everyone having to implement the delegate method as well as the NSErrorRecoveryAttempting protocol.


Here is the solution that worked for me. You need to accept the connection in through the connection's delegate including both messages:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

Please note that with doing this, you're not checking the trustability of the certificate, so only the SSL encryption of the HTTPS connection is interesting, but the signing authority is not taking into consideration here, which can decrease security.


update xcode 9

    var result:(message:String, data:Data?) = (message: "Fail", data: nil)
    var request = URLRequest(url: url)

    let sessionDelegate = SessionDelegate()
    let session = URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: nil)
    let task = session.dataTask(with: request){(data, response, error) in


    }
    task.resume()

the delegate task

    class SessionDelegate:NSObject, URLSessionDelegate
    {

        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
            {
                print(challenge.protectionSpace.host) 
                if(challenge.protectionSpace.host == "111.11.11.11")
                {
                    let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                   completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential)
                }
            }

        }
    }

참고URL : https://stackoverflow.com/questions/19507207/how-do-i-accept-a-self-signed-ssl-certificate-using-ios-7s-nsurlsession-and-its

반응형