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

标签 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;
}

最佳答案

对于任何其他尝试使用 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
    }

}

关于android - iOS 快速握手问题上的 TLS 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40366879/

相关文章:

ios - 代号一个文本域密码约束

swift - 如何有条件地调用 SwiftUI NavigationLink?

ios - Firestore在文档中的数组中搜索字符串

ios - NSPhotoLibraryUsageDescription 键添加到 info.plist,由于隐私敏感数据仍然崩溃

即使实现了可序列化,也会抛出 java.io.NotSerializedException

android - 有没有办法让每个人都可以访问 firestore 数据库,但只能通过应用程序访问?

java - Xml 布局文件不适用于 Textview

ios - SQLite 数据库在更新时挂起

android - android中的Gridview布局

ios - 默认 UISwitch 为 YES 然后使用 NSUserDefaults 保存状态