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

时间:2016-11-01 19:02:05

标签: android ios swift ssl tls1.2

我在使用Ingenico iPP320设备建立SSL / TLS连接时遇到问题。我尝试过建议here的解决方案,但我收到此错误

CFNetwork SSLHandshake failed (-9824 -> -9829)

我是使用SSL / TLS连接的新手,不知道如何建立连接。我正在使用的是受密码保护的p12文件,证书也不是自签名的。我被告知服务器也必须对客户端进行身份验证,因此可能必须将中间CA和根CA发送到服务器。我能够在Android上进行身份验证,但我不确定如何在iOS中使用它。

以下是适用的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;
}

1 个答案:

答案 0 :(得分:0)

对于其他试图使用P12证书进行TLS连接的人来说,这就是我提出的解决方案。如果有人有更好的方法,请让我知道谢谢。

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

}