简体   繁体   中英

TLS Connection on iOS swift handshake issue

I'm having trouble establishing an SSL/TLS connection with an Ingenico iPP320 device. 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. What I'm using is a p12 file that is password protected also the certificate is not self-signed. 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. I was able to get authentication working on Android but I'm not sure how to go about it in iOS.

Below is the Android code that works.

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. 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
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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