Установите сертификаты TLS на приложение iOS веб-сервера

Я пытаюсь интегрировать платеж Swish в одно из приложений, которые я разрабатываю. Чтобы иметь возможность подключиться к swish api, мне нужно "настроить сертификаты TLS в Swish Certificate Management и установить его на" моем "веб-сервере" в соответствии с документацией. Вот полная техническая документация https://developer.getswish.se/merchants-api-manual/4-merchant-setup-process/.

Проблема, которую я не понимаю, заключается в том, что я не использую веб-сервер и не могу установить эти сертификаты там.

Мое приложение просто предлагает некоторые услуги для клиента, и после нажатия кнопки оплаты следует открыть приложение Swish, чтобы вкратце завершить транзакцию.

Я попытался сделать пост-запрос, чтобы получить маркер запроса, с помощью которого я могу открыть приложение swish, загруженное с деталями платежа. Я уверен, что проблемы с сертификатами, но не смогли найти хороший источник, объясняющий, как их импортировать (интегрировать).

let strURL = " https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests/ "

guard let postURL = URL (string: strURL) else {print ("Can't create url") return}

    var request = URLRequest(url: postURL)

    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.setValue("application/json", forHTTPHeaderField: "Accept")

    let data: [String: Any] = [
        "callbackUrl": "https://example.com/api/swishcb/paymentrequests",
        "payeeAlias": "123xxxxxxx", // The Swish number of the payee. It needs to match with Merchant Swish number.
        "amount": "100",
        "currency": "SEK",
        "message": "Test request to get the token"
    ]

    do {
        let jsonParams = try JSONSerialization.data(withJSONObject: data, options: [])
        request.httpBody = jsonParams
    } catch {
        print("Error serializing the parameters of the post request")
        return
    }

    // response will contain a Token, unique for each payment request

    let config = URLSessionConfiguration.default
    config.timeoutIntervalForResource = 120
    config.timeoutIntervalForRequest = 120
    let session = URLSession(configuration: config) 

    session.dataTask(with: request) { (data, response, error) in
        print("Data \(data)")
        print("Response \(response)")
        if error != nil {
            print("Error post request \(error?.localizedDescription)")
        }
    }.resume()

Я получил следующую ошибку: Ошибка пост-запроса Необязательно ("Произошла ошибка SSL, и невозможно установить безопасное соединение с сервером".)

018-12-21 12: 24: 55.549759 + 0200 tolk-24-7 [7230: 111102] [BoringSSL] boringssl_context_alert_callback_handler (3718) [C6.1: 2] [0x7fce4a77bf00] Уровень предупреждения: смертельный, описание: сбой рукопожатия 2018 12-21 12:24:55.550047+0200 tolk-24-7[7230:111102] [BoringSSL] boringssl_session_errorlog(224) [C6.1:2][0x7fce4a77bf00] [boringssl_session_handshake_incomplete] операция SSL_ERROR_SSL не выполнена (операция 1): библиотека SSL_ERROR_SS не выполнена (1) 2018-12-21 12:24:55.550332+0200 tolk-24-7[7230:111102] [BoringSSL] boringssl_session_handshake_error_print(205) [C6.1:2][0x7fce4a77bf00] 140523985879704: ошибка: 10000410: SSin:SSL: маршрутизация:SSL SSLV3_ALERT_HANDSHAKE_FAILURE:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl_Sim/boringssl-109.220.4/ssl/tls_record.cc:586: номер предупреждения SSL 40 2018-12-21 12: 24: 55.55058k + 0200 -24-7[7230:111102] [BoringSSL] boringssl_context_get_error_code(3539) [C6.1:2][0x7fce4a77bf00] SSL_AD_HANDSHAKE_FAILURE 2018-12-21 12:24:55.552299+0200 tolk-24-72 7215 TCP Conn Failed [6:0x600002dd6c40]: 3:-9824 Ошибка (-9824) 2018-12-21 12:24:55.555924+0200 tolk-24-7[7230:111102] Ошибка загрузки HTTP NSURLSession / NSURLConnection (kCFStreamErrorDomainSSL, -9824) 2018-12-21 12:24:55.556052+0200 tolk-24-7[7230:111102] Задача <7888D080-D175-4DBF-8F66-4183F0D653E6>.<1> Ошибка загрузки HTTP (код ошибки: -1200 [3:-9824]) 2018-12-21 12:24:55.556234+0200 tolk-24-7[7230:111613] Задание <7888D080-D175-4DBF-8F66-4183F0D653E6>.<1> завершено с ошибкой - код: -1200

0 ответов

Я чувствую ваше разочарование, я не работал с Swish API как таковым, но похоже, что URLSession не выполняет запрос сертификата клиента. На этом этапе рукопожатие не удается.

Существует возможность добавить URLSessionDelegate в URLSession для обработки таких проблем аутентификации, как ServerTrust и ClientCertificate. Здесь обсуждают: Swift 3 UrlSession с аутентификацией клиента по сертификату

Если вы можете создать p12/pfx с клиентским сертификатом и закрытым ключом, вы можете использовать SecPKCS12Import для его импорта и использования для доверия URLCredential в NSURLAuthenticationMethodClientCertificate, полученном в URLSessionDelegate. Вот реализация, которую я написал:

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

        let authenticationMethod = challenge.protectionSpace.authenticationMethod
        if authenticationMethod == NSURLAuthenticationMethodServerTrust {
            //Handle server trust if necessary
        } else if authenticationMethod == NSURLAuthenticationMethodClientCertificate {
            if let clientCredential = try? getClientUrlCredential() {
                completionHandler(.useCredential, clientCredential)
            } else {
                completionHandler(.cancelAuthenticationChallenge, nil)
            }
        }
    }

getClientUrlCredential функция:

private func getClientUrlCredential() throws -> URLCredential {

        let p12Data = getP12Data() //Get the data from the bundle if it's bundled in the app
        let p12Key = getP12Key() //you need the key set for creating the p12/pfx

        let certOptions: NSDictionary = [
            kSecImportExportPassphrase as NSString : p12Key as NSString
        ]

        // import certificate to read its entries
        var items: CFArray?
        let status = SecPKCS12Import(p12Data, certOptions, &items)

        if status == errSecSuccess,
            let items = items,
            let dict = (items as Array).first as? [String: AnyObject],
            let certChain = dict[kSecImportItemCertChain as String] as? [SecTrust] {

            // Check if SecIdentityGetTypeID is present
            guard let cfIdentity = dict[kSecImportItemIdentity as String] as CFTypeRef?,
                CFGetTypeID(cfIdentity) == SecIdentityGetTypeID() else {
                    throw URLSessionPinningDelegateError.localClientCertificateError
            }

            let identity = dict[kSecImportItemIdentity as String] as! SecIdentity
            return URLCredential(
                identity: identity,
                certificates: certChain,
                persistence: .forSession
            )
        }

        //Failed to read local certificate, throw error
        throw URLSessionPinningDelegateError.localClientCertificateError
    }

С действующим сертификатом клиента вы должны иметь возможность выполнить приветствие клиента и настроить TLS на сервер, поскольку, похоже, именно здесь вы сейчас терпите неудачу. По крайней мере, мне на это указывает предупреждение SSL 40, которое вы получаете от BoringSSL.

Надеюсь, это укажет вам, по крайней мере, в правильном направлении, при необходимости будем рады оказать вам дополнительную поддержку.

Другие вопросы по тегам