我正在尝试从我的iOS应用程序到后端服务器(Node.js)建立简单的套接字连接(NO HTTP)。服务器证书已使用我自己创建的自定义CA创建并签名。我相信,为了让iOS信任我的服务器,我将不得不以某种方式将此自定义CA证书添加到用于确定Java / Android中TrustStore的工作方式的信任类型的受信任证书列表。
我尝试使用下面的代码进行连接,并且没有错误,但是write()函数似乎未成功。
主视图控制器:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let api: APIClient = APIClient() api.initialiseSSL("10.13.37.200", port: 8080) api.write("Hello") api.deinitialise() print("Done") }
APIClient类
class APIClient: NSObject, NSStreamDelegate { var readStream: Unmanaged<CFReadStreamRef>? var writeStream: Unmanaged<CFWriteStreamRef>? var inputStream: NSInputStream? var outputStream: NSOutputStream? func initialiseSSL(host: String, port: UInt32) { CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream) inputStream = readStream!.takeRetainedValue() outputStream = writeStream!.takeRetainedValue() inputStream?.delegate = self outputStream?.delegate = self inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) let cert: SecCertificateRef? = CreateCertificateFromFile("ca", ext: "der") if cert != nil { print("GOT CERTIFICATE") } let certs: NSArray = NSArray(objects: cert!) let sslSettings = [ NSString(format: kCFStreamSSLLevel): kCFStreamSocketSecurityLevelNegotiatedSSL, NSString(format: kCFStreamSSLValidatesCertificateChain): kCFBooleanFalse, NSString(format: kCFStreamSSLPeerName): kCFNull, NSString(format: kCFStreamSSLCertificates): certs, NSString(format: kCFStreamSSLIsServer): kCFBooleanFalse ] CFReadStreamSetProperty(inputStream, kCFStreamPropertySSLSettings, sslSettings) CFWriteStreamSetProperty(outputStream, kCFStreamPropertySSLSettings, sslSettings) inputStream!.open() outputStream!.open() } func write(text: String) { let data = [UInt8](text.utf8) outputStream?.write(data, maxLength: data.count) } func CreateCertificateFromFile(filename: String, ext: String) -> SecCertificateRef? { var cert: SecCertificateRef! if let path = NSBundle.mainBundle().pathForResource(filename, ofType: ext) { let data = NSData(contentsOfFile: path)! cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)! } else { } return cert } func deinitialise() { inputStream?.close() outputStream?.close() }
}
我了解SSL / TLS的工作原理,并且所有这些都是我在同一个应用的Android版本中所做的所有工作。我只是对SSL的iOS实现感到困惑。
我来自Java背景,已经解决了3个星期的问题。任何帮助,将不胜感激。
更喜欢Swift代码中的答案,而不是Objective C,但是如果您只有Obj C也可以:)
好的,我在这个问题上花了8周的时间:(但是我终于设法提出了一个可行的解决方案。我必须说iOS上的SSL / TLS是个玩笑。Android上的Java会让它死掉。为了这样做,这完全荒谬。评估自签名证书的信任度,您必须完全禁用证书链验证并自己做,这完全荒谬。无论如何,这是使用自签名服务器证书连接到远程套接字服务器(无HTTP)的完全有效的解决方案。编辑此答案以提供更好的答案,因为我还没有添加添加用于发送和接收数据的代码的更改:)
// SecureSocket // // Created by snapper26 on 2/9/16. // Copyright © 2016 snapper26. All rights reserved. // import Foundation class ProXimityAPIClient: NSObject, StreamDelegate { // Input and output streams for socket var inputStream: InputStream? var outputStream: OutputStream? // Secondary delegate reference to prevent ARC deallocating the NSStreamDelegate var inputDelegate: StreamDelegate? var outputDelegate: StreamDelegate? // Add a trusted root CA to out SecTrust object func addAnchorToTrust(trust: SecTrust, certificate: SecCertificate) -> SecTrust { let array: NSMutableArray = NSMutableArray() array.add(certificate) SecTrustSetAnchorCertificates(trust, array) return trust } // Create a SecCertificate object from a DER formatted certificate file func createCertificateFromFile(filename: String, ext: String) -> SecCertificate { let rootCertPath = Bundle.main.path(forResource:filename, ofType: ext) let rootCertData = NSData(contentsOfFile: rootCertPath!) return SecCertificateCreateWithData(kCFAllocatorDefault, rootCertData!)! } // Connect to remote host/server func connect(host: String, port: Int) { // Specify host and port number. Get reference to newly created socket streams both in and out Stream.getStreamsToHost(withName:host, port: port, inputStream: &inputStream, outputStream: &outputStream) // Create strong delegate reference to stop ARC deallocating the object inputDelegate = self outputDelegate = self // Now that we have a strong reference, assign the object to the stream delegates inputStream!.delegate = inputDelegate outputStream!.delegate = outputDelegate // This doesn't work because of arc memory management. Thats why another strong reference above is needed. //inputStream!.delegate = self //outputStream!.delegate = self // Schedule our run loops. This is needed so that we can receive StreamEvents inputStream!.schedule(in:RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode) outputStream!.schedule(in:RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode) // Enable SSL/TLS on the streams inputStream!.setProperty(kCFStreamSocketSecurityLevelNegotiatedSSL, forKey: Stream.PropertyKey.socketSecurityLevelKey) outputStream!.setProperty(kCFStreamSocketSecurityLevelNegotiatedSSL, forKey: Stream.PropertyKey.socketSecurityLevelKey) // Defin custom SSL/TLS settings let sslSettings : [NSString: Any] = [ // 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, // NSString(format: kCFStreamSSLPeerName): kCFNull, // We are an SSL/TLS client, not a server NSString(format: kCFStreamSSLIsServer): kCFBooleanFalse ] // Set the SSL/TLS settingson the streams inputStream!.setProperty(sslSettings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey) outputStream!.setProperty(sslSettings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey) // Open the streams inputStream!.open() outputStream!.open() } // This is where we get all our events (haven't finished writing this class) func stream(_ aStream: Stream, handle eventCode: Stream.Event) { switch eventCode { case Stream.Event.endEncountered: print("End Encountered") break case Stream.Event.openCompleted: print("Open Completed") break case Stream.Event.hasSpaceAvailable: print("Has Space Available") // If you try and obtain the trust object (aka kCFStreamPropertySSLPeerTrust) before the stream is available for writing I found that the oject is always nil! var sslTrustInput: SecTrust? = inputStream! .property(forKey:kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust? var sslTrustOutput: SecTrust? = outputStream!.property(forKey:kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust? if (sslTrustInput == nil) { print("INPUT TRUST NIL") } else { print("INPUT TRUST NOT NIL") } if (sslTrustOutput == nil) { print("OUTPUT TRUST NIL") } else { print("OUTPUT TRUST NOT NIL") } // Get our certificate reference. Make sure to add your root certificate file into your project. let rootCert: SecCertificate? = createCertificateFromFile(filename: "ca", ext: "der") // TODO: Don't want to keep adding the certificate every time??? // Make sure to add your trusted root CA to the list of trusted anchors otherwise trust evaulation will fail sslTrustInput = addAnchorToTrust(trust: sslTrustInput!, certificate: rootCert!) sslTrustOutput = addAnchorToTrust(trust: sslTrustOutput!, certificate: rootCert!) // convert kSecTrustResultUnspecified type to SecTrustResultType for comparison var result: SecTrustResultType = SecTrustResultType.unspecified // This is it! Evaulate the trust. let error: OSStatus = SecTrustEvaluate(sslTrustInput!, &result) // An error occured evaluating the trust check the OSStatus codes for Apple at osstatus.com if (error != noErr) { print("Evaluation Failed") } if (result != SecTrustResultType.proceed && result != SecTrustResultType.unspecified) { // Trust failed. This will happen if you faile to add the trusted anchor as mentioned above print("Peer is not trusted :(") } else { // Peer certificate is trusted. Now we can send data. Woohoo! print("Peer is trusted :)") } break case Stream.Event.hasBytesAvailable: print("Has Bytes Available") break case Stream.Event.errorOccurred: print("Error Occured") break default: print("Default") break } } }