2016-08-04 23 views
7

Tôi đang cố gắng thiết lập kết nối socket đơn giản (NO HTTP) từ ứng dụng iOS của tôi đến máy chủ phụ trợ của tôi (Node.js). Chứng chỉ máy chủ đã được tạo và ký bằng cách sử dụng CA tùy chỉnh mà tôi đã tự tạo. Tôi tin rằng để có được iOS tin tưởng máy chủ của tôi, tôi sẽ phải bằng cách nào đó thêm Chứng chỉ CA tùy chỉnh này vào danh sách các chứng chỉ đáng tin cậy được sử dụng để xác định loại tin cậy về cách TrustStore trong Java/Android hoạt động.Kết nối SSL SSL trong Swift

Tôi đã thử kết nối bằng mã bên dưới và không có lỗi tuy nhiên hàm write() dường như không thành công.

chính View Controller:

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

lớp 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() 
} 

}

Tôi hiểu thế nào SSL/TLS hoạt động và tất cả vì tôi đã làm điều này tất cả tiền phạt trong phiên bản Android của cùng một ứng dụng này. Tôi chỉ nhầm lẫn với việc triển khai SSL của iOS.

Tôi đến từ nền Java và đã xảy ra sự cố này trong 3 tuần. Bất kỳ trợ giúp sẽ được đánh giá cao.

thích câu trả lời trong mã Swift, không Objective C nhưng nếu bạn chỉ có obj C thats ok quá :)

Trả lời

6

Ok tôi đã dành 8 tuần về vấn đề này :(nhưng tôi cuối cùng đã quản lý để cùng nhau đưa ra một giải pháp làm việc. Tôi phải nói rằng SSL/TLS trên iOS là một trò đùa, Java trên Android để nó chết, nó hoàn toàn vô lý để đánh giá niềm tin cho một chứng chỉ tự ký, bạn phải vô hiệu hóa xác nhận chuỗi chứng chỉ hoàn toàn và tự mình làm. Dù sao đây là giải pháp làm việc hoàn toàn kết nối với một máy chủ ổ cắm từ xa (không có HTTP) bằng cách sử dụng chứng chỉ máy chủ tự ký. Vui lòng chỉnh sửa câu trả lời này để cung cấp câu trả lời tốt hơn vì tôi chưa có thay đổi để thêm mã cho gửi và nhận dữ liệu :)

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

Cảm ơn người đàn ông, đây là công cụ tốt! Tôi đã tìm ra số lượng lớn các chức năng ổ cắm, nhưng điều này đã giúp tôi có được SSL làm việc. – Sethmr

+0

Tôi ước gì tôi đã thấy điều này khi tôi khắc phục sự cố toàn bộ điều này trong đầu – Sethmr

+0

Rất vui khi nó giúp bạn. Thành thật mà nói, quyết định thiết kế của Apple làm cho SSL là khủng khiếp. Bạn sẽ có thể thêm CA gốc tin cậy của riêng bạn "TRƯỚC" sự tin cậy được đánh giá: ( – Snapper26