简体   繁体   English

iOS快速握手问题上的TLS连接

[英]TLS Connection on iOS swift handshake issue

I'm having trouble establishing an SSL/TLS connection with an Ingenico iPP320 device. 我在与Ingenico iPP320设备建立SSL / TLS连接时遇到问题。 I have tried the solution suggested here but I'm getting this error 我已经尝试过这里建议的解决方案但出现此错误

CFNetwork SSLHandshake failed (-9824 -> -9829)

I'm new to using SSL/TLS connections and not sure how I should go about making a connection. 我是使用SSL / TLS连接的新手,但不确定如何建立连接。 What I'm using is a p12 file that is password protected also the certificate is not self-signed. 我正在使用的是一个受密码保护的p12文件,该证书不是自签名的。 I was told that the server would have to authenticate the client as well so the intermediate and root CA may have to be sent to the server. 有人告诉我,服务器也必须对客户端进行身份验证,因此中间和根CA可能必须发送到服务器。 I was able to get authentication working on Android but I'm not sure how to go about it in iOS. 我可以在Android上使用身份验证,但是不确定在iOS中如何进行身份验证。

Below is the Android code that works. 以下是有效的Android代码。

public SSLSocket createSSLSocket(String ipAddress, int port)
{
    try
    {
        SSLSocket socket = null;
        String certStorePassword = "password";
        String certStoreType = "pkcs12";

        InputStream iStream = getResources().openRawResource(R.raw.clientP12File);

        KeyStore keyStore = KeyStore.getInstance(certStoreType);
        keyStore.load(iStream, certStorePassword.toCharArray());
//            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, certStorePassword.toCharArray());

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new SecureRandom());
        SSLContext.setDefault(sc);
        SSLSocketFactory factory = sc.getSocketFactory();
        socket = (SSLSocket) factory.createSocket(ipAddress, port);
        socket.setEnabledProtocols(new String[] { "TLSv1.2" });
        socket.setUseClientMode(true);
        socket.startHandshake();
        return socket;
    }
    catch (Exception ex) {
        Log.e(TAG, "createSSLSocket: ", ex);
    }

    return null;
}

For anyone else trying to do TLS connection with a P12 certificate, this was the solution I came up with. 对于尝试使用P12证书进行TLS连接的其他人,这是我想出的解决方案。 If anyone has a better way of doing it please let me know thanks. 如果有人有更好的方法,请告诉我谢谢。

//
//  SSLConnection.swift
//  SSLConnection
//
//  Created by JC Castano on 3/27/17.
//  Copyright © 2017 1stPayGateway. All rights reserved.
//

import Foundation

class SSLConnection: NSObject, StreamDelegate {

    private static var inputStream:InputStream!
    private static var outputStream:OutputStream!
    private var ipAddress:String = ""
    private var sslEnabled: Bool = false

    public func connectToIngenico(address:String, sslEnabled: Bool) {

        // TODO: Create dispatch queue to handle Ingenico connection
        //        initIngenicoQueue()

        self.ipAddress = address

        var readStream:  Unmanaged<CFReadStream>?
        var writeStream: Unmanaged<CFWriteStream>?

        CFStreamCreatePairWithSocketToHost(nil, address as CFString!,12000, &readStream, &writeStream)

        // Documentation suggests readStream and writeStream can be assumed to
        // be non-nil. If you believe otherwise, you can test if either is nil
        // and implement whatever error-handling you wish.

        SSLConnection.inputStream = readStream!.takeRetainedValue()
        SSLConnection.outputStream = writeStream!.takeRetainedValue()

        SSLConnection.inputStream.delegate = self
        SSLConnection.outputStream.delegate = self

        SSLConnection.inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        SSLConnection.outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)

        if sslEnabled {

            // Enable SSL/TLS on the streams
            SSLConnection.inputStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL, forKey: Stream.PropertyKey.socketSecurityLevelKey)
            SSLConnection.outputStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL, forKey: Stream.PropertyKey.socketSecurityLevelKey)

            let sslSettings = [

                // NSStream automatically sets up the socket, the streams and creates a trust object and evaulates it before you even get a chance to check the trust yourself. Only proper SSL certificates will work with this method. If you have a self signed certificate like I do, you need to disable the trust check here and evaulate the trust against your custom root CA yourself.
                NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse,

                // We are an SSL/TLS client, not a server
                NSString(format: kCFStreamSSLIsServer): kCFBooleanFalse,

                // Get the key chain items and add it to ssl settings
                NSString(format: kCFStreamSSLCertificates): getKeyChain(fileName: "CLIENT", ofType: "p12", password: "password")
            ] as [NSString : Any]

            SSLConnection.inputStream.setProperty(sslSettings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
            SSLConnection.outputStream.setProperty(sslSettings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)

        }

        SSLConnection.inputStream.open()
        SSLConnection.outputStream.open()
    }

    func getKeyChain(fileName: String, ofType type: String, password: String) -> CFArray {

        let mainBundle = Bundle.main
        let thePath = mainBundle.path(forResource: fileName, ofType: type)!

        let PKCS12Data: NSData = NSData(contentsOfFile: thePath)!

        var items: CFArray?
        let optionDict: NSMutableDictionary = [kSecImportExportPassphrase as NSString: password]
        let sanityCheck = SecPKCS12Import(PKCS12Data, optionDict, &items)

        if sanityCheck == errSecSuccess && CFArrayGetCount(items) > 0 {
            return parseKeyChainItems(items!)
        } else {
            switch sanityCheck {
            case errSecSuccess:
                print("Error importing p12: errSecSuccess")
            case errSecUnimplemented:
                print("Error importing p12: errSecUnimplemented")
            case errSecIO:
                print("Error importing p12: errSecIO")
            case errSecOpWr:
                print("Error importing p12: errSecOpWr")
            case errSecParam:
                print("Error importing p12: errSecParam")
            case errSecAllocate:
                print("Error importing p12: errSecAllocate")
            case errSecUserCanceled:
                print("Error importing p12: errSecUserCanceled")
            case errSecBadReq:
                print("Error importing p12: errSecBadReq")
            case errSecInternalComponent:
                print("Error importing p12: errSecInternalComponent")
            case errSecNotAvailable:
                print("Error importing p12: errSecNotAvailable")
            case errSecDuplicateItem:
                print("Error importing p12: errSecDuplicateItem")
            case errSecItemNotFound:
                print("Error importing p12: errSecItemNotFound")
            case errSecInteractionNotAllowed:
                print("Error importing p12: errSecInteractionNotAllowed")
            case errSecDecode:
                print("Error importing p12: errSecDecode")
            case errSecAuthFailed:
                print("Error importing p12: errSecAuthFailed")
            default:
                print("Error importing p12: Unknown items: \(items)")
                break
            }
        }
        return [] as CFArray
    }

    func parseKeyChainItems(_ keychainArray: NSArray) -> CFArray {
        print("Key chain array: \(keychainArray)")
        let dict = keychainArray[0] as! Dictionary<String,AnyObject>
        let key = String(kSecImportItemIdentity)
        let identity = dict[key] as! SecIdentity?

        let certArray:[AnyObject] = dict["chain"] as! [SecCertificate]

        var certChain:[AnyObject] = [identity!]

        for item in certArray {
            certChain.append(item)
        }
        return certChain as CFArray
    }

    func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
        let streamName = getStreamName(aStream)

        switch eventCode {
        case Stream.Event.openCompleted:
            print("\(streamName).OpenCompleted")
            break
        case Stream.Event.hasBytesAvailable:
            print("\(streamName).HasBytesAvailable")
        case Stream.Event.hasSpaceAvailable:
            print("\(streamName).HasSpaceAvailable")
            break
        case Stream.Event.endEncountered:
            print("\(streamName).EndEncountered")
            break
        case Stream.Event.errorOccurred:
            print("\(streamName).ErrorOccurred")
            break
        default:
            print("\(streamName) unknown event")
            break
        }
    }

    func getStreamName(_ aStream: Stream) -> String {

        if comparedStreamEqual(aStream, bStream: SSLConnection.inputStream) {
            return "InputStream"
        } else if comparedStreamEqual(aStream, bStream: SSLConnection.outputStream) {
            return "OutputStream"
        }
        return "UnknownStream"
    }

    func comparedStreamEqual(_ aStream: Stream? , bStream: Stream?) -> Bool {
        if aStream != nil && bStream != nil {
            if aStream == bStream {
                return true
            }
        }
        return false
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM