Swift SSL自签名证书错误

y1aodyip  于 2023-01-16  发布在  Swift
关注(0)|答案(4)|浏览(186)

此代码尝试访问在浏览器中工作的SSL URL,但失败:

let path = "https://localhost:8443/greeting"
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let session = NSURLSession.sharedSession()

let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
    let json:JSON = JSON(data: data!)
    if let c = json["content"].string {
        print(c)
    }
})
task.resume()

失败,错误为:
可选(错误域=NSURLErrorDomain Code=-1200“发生SSL错误,无法与服务器建立安全连接。”用户信息={NSURLErrorFailingURLPeerTrustErrorKey=,
允许应用程序接受此证书需要什么?
有问题的证书是自签名的。阅读SO上的一些解决方案没有成功。
运行Xcode 7.2

cyej8jka

cyej8jka1#

@Ashish Kakkad说得很对。

class Blah: NSURLSessionDelegate {

    func rest() {
        let path = "https://localhost:8443/greeting"
        let request = NSMutableURLRequest(URL: NSURL(string: path)!)
        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
        let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
            let json:JSON = JSON(data: data!)
            if let c = json["content"].string {
                print(c)
            }
        })
        task.resume()
    }

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

在Info.plist文件中包含以下内容:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>localhost</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>
n1bvdmb6

n1bvdmb62#

Sai Reddy的解决方案允许您接受具有完整链的自签名证书,但它也接受其他证书。
Marcus Leon的解决方案是完全重写--基本上忽略所有证书。
我更喜欢这个。

雨燕4.1,iOS 11.4.1

首先,在信息列表中:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

其次,无论在何处使用NSURLSession,都不要使用URLSession.shared进行设置,而是使用类似以下的内容:

session = URLSession(configuration: .default, delegate: APIURLSessionTaskDelegate(isSSLPinningEnabled: isSSLPinningEnabled), delegateQueue: nil)

然后添加此类来处理固定:

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

        print("*** received SESSION challenge...\(challenge)")
        let trust = challenge.protectionSpace.serverTrust!
        let credential = URLCredential(trust: trust)

        guard isSSLPinningEnabled else {
            print("*** SSL Pinning Disabled -- Using default handling.")
            completionHandler(.useCredential, credential)
            return
        }

        let myCertName = "my_certificate_to_pin"
        var remoteCertMatchesPinnedCert = false
        if let myCertPath = Bundle.main.path(forResource: myCertName, ofType: "der") {
            if let pinnedCertData = NSData(contentsOfFile: myCertPath) {
                // Compare certificate data
                let remoteCertData: NSData = SecCertificateCopyData(SecTrustGetCertificateAtIndex(trust, 0)!)
                if remoteCertData.isEqual(to: pinnedCertData as Data) {
                    print("*** CERTIFICATE DATA MATCHES")
                    remoteCertMatchesPinnedCert = true
                }
                else {
                    print("*** MISMATCH IN CERT DATA.... :(")
                }

            } else {
                print("*** Couldn't read pinning certificate data")
            }
        } else {
            print("*** Couldn't load pinning certificate!")
        }

        if remoteCertMatchesPinnedCert {
            print("*** TRUSTING CERTIFICATE")
            completionHandler(.useCredential, credential)
        } else {
            print("NOT TRUSTING CERTIFICATE")
            completionHandler(.rejectProtectionSpace, nil)
        }
    }
}

此类检查您是否启用了证书固定。如果您启用了,它将完全忽略正常的证书验证,并与我们在应用中包含的证书进行精确比较。这样,它接受您的自签名证书,而不接受其他任何证书。
此解决方案要求您在项目中的Resources文件夹中放置一个“my_certificate_to_pin.der”文件。如果您还没有Resources文件夹,只需添加一个即可。
该证书应采用DER格式。
要为服务器创建自签名证书,通常需要执行以下操作:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mycert.key -out mycert.cer

这将生成两个文件--一个mycert.key私钥文件和一个mycert.cer-证书本身。这两个文件都是X509格式。对于iOS,您需要DER格式的证书,因此执行以下操作:

openssl x509 -outform der -in mycert.cer -out my_certificate_to_pin.der

这将生成您在iOS上需要的文件。

pxiryf3j

pxiryf3j3#

你可以使用你自己的证书代替我的证书(fullchain.pem)

class AccessingServer: NSObject,URLSessionDelegate {

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

        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            // First load our extra root-CAs to be trusted from the app bundle.
            let trust = challenge.protectionSpace.serverTrust

            let rootCa = "fullchain"
            if let rootCaPath = Bundle.main.path(forResource: rootCa, ofType: "pem") {
                if let rootCaData = NSData(contentsOfFile: rootCaPath) {

                    let rootCert = SecCertificateCreateWithData(nil, rootCaData)!

                    SecTrustSetAnchorCertificates(trust!, [rootCert] as CFArray)

                    SecTrustSetAnchorCertificatesOnly(trust!, false)
                }
            }

            var trustResult: SecTrustResultType = SecTrustResultType.invalid
            SecTrustEvaluate(trust!, &trustResult)

            if (trustResult == SecTrustResultType.unspecified ||
                trustResult == SecTrustResultType.proceed) {
                // Trust certificate.

                let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                challenge.sender?.use(credential, for: challenge)

            } else {
                NSLog("Invalid server certificate.")
                challenge.sender?.cancel(challenge)
            }
        } else {
            NSLog("Got unexpected authentication method \(challenge.protectionSpace.authenticationMethod)");
            challenge.sender?.cancel(challenge)
        }
    }

   }
cclgggtu

cclgggtu4#

雨燕5.1

我知道你找到了答案,但我有一个更短的方法,如果任何人whant可以使用它

class AuthViewModel: NSObject, ObservableObject {

func login(username: String, password: String,completion: @escaping ((LoginModel) -> Void)) -> Void {
    
    let url = URL(string: "https://YourAPI.xyz/APP/?action=login&user=\(username)&pass=\(password)")!

    // Here is Important ****
    let req = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
    // *****

    let task = req.dataTask(with: request) { data1, response, error in
        guard
            let data = data1,
            let response = response as? HTTPURLResponse,
            error == nil
        else {                                                               // check for fundamental networking error
            print("error", error ?? URLError(.badServerResponse))
            return
        }
        
        guard (200 ... 299) ~= response.statusCode else {                    // check for http errors
            print("statusCode should be 2xx, but is \(response.statusCode)")
            print("response = \(response)")
            return
        }
        
        // do whatever you want with the `data`, e.g.:
        
        
        DispatchQueue.main.async {
            do {
                let resStreing = String(data: data, encoding: .utf8)
                let resData = resStreing?.data(using: .utf8)
                
                let responseObject = try JSONDecoder().decode(LoginModel.self, from: resData!)
                
                print("\n\n ResponseObject = \(responseObject) \n\n")
                completion(responseObject)
            } catch {
                print("CAN NOT PARSE!") // parsing error
                
                if let responseString = String(data: data, encoding: .utf8) {
                    print("responseString = \(responseString)")
                } else {
                    print("unable to parse response as string")
                }
            }
        }
    }

    task.resume()
    
    
}
}

您需要的扩展名:

extension AuthViewModel: URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
   //Trust the certificate even if not valid
   let urlCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)

   completionHandler(.useCredential, urlCredential)
}
}

和用法:

authviewModel.login(username: "UserName",password: "Password") { loginModel in
// Do Somthing
}

相关问题