2016-08-04 21 views
7

Próbuję ustanowić proste połączenie z gniazdem (NIE HTTP) z mojej aplikacji na iOS do mojego serwera zaplecza (Node.js). Certyfikat serwera został utworzony i podpisany przy użyciu niestandardowego urzędu certyfikacji, który sam wykonałem. Uważam, że aby przekonać iOS do mojego serwera, będę musiał jakoś dodać ten niestandardowy certyfikat CA do listy zaufanych certyfikatów, które są używane do określenia zaufania, w jaki sposób działa TrustStore w Javie/Androidzie.Połączenie SSL iOS w Swift

Próbowałem połączyć za pomocą poniższego kodu i nie ma błędów, jednak funkcja write() wydaje się nie działać.

Widok główny Kontroler:

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") 
} 

klasa 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() 
} 

}

rozumiem jak SSL/TLS działa i wszystko od Zrobiłem to wszystko jest w porządku w Android wersji ta sama aplikacja. Jestem po prostu zdezorientowany z implementacją protokołu SSL na iOS.

Pochodzę z Java i pracuję z tym problemem przez 3 tygodnie. Każda pomoc będzie doceniona.

Wolisz odpowiedzi w kodzie SWIFT, nieobiektywne C, ale jeśli masz tylko Obj C to jest ok zbyt :)

Odpowiedz

6

Ok spędziłem 8 tygodni w tej kwestii :(ale w końcu udało mi się zebrać roztwór roboczy. Muszę powiedzieć, że SSL/TLS na iOS jest żartem, Java na Androida pozostawia go na śmierć.Obowiązkiem jest to, że aby ocenić zaufanie do samopodpisanego certyfikatu, należy całkowicie wyłączyć weryfikację łańcucha certyfikatów i zrobić to samemu. W każdym razie jest to w pełni działające rozwiązanie, które łączy się ze zdalnym serwerem gniazd (bez protokołu HTTP) przy użyciu certyfikatu serwera z podpisem własnym. Możesz edytować tę odpowiedź, aby uzyskać lepszą odpowiedź, ponieważ nie wprowadziłem zmiany w celu dodania kodu wysyłanie i odbieranie danych jeszcze :)

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

Dzięki stary, to jest dobre! Zrozumiałem już większość funkcjonalności gniazd, ale pomogło mi to w działaniu SSL. – Sethmr

+0

Szkoda, że ​​nie widziałem tego, kiedy rozwiązywałem ten problem na początku. – Sethmr

+0

Cieszę się, że ci pomogło. Szczerze mówiąc, decyzje projektowe, jakie Apple podejmuje dla SSL, są okropne. Powinieneś być w stanie dodać swój zaufany główny CA "PRZED" zaufanie jest oceniane :( – Snapper26