2016-08-04 7 views
7

में आईओएस एसएसएल कनेक्शन मैं अपने आईओएस ऐप से अपने बैकएंड सर्वर (नोड.जेएस) में एक साधारण सॉकेट कनेक्शन (कोई HTTP) स्थापित करने की कोशिश कर रहा हूं। सर्वर प्रमाणपत्र को एक कस्टम सीए का उपयोग करके बनाया और हस्ताक्षरित किया गया है जिसे मैंने स्वयं बनाया है। मेरा मानना ​​है कि मेरे सर्वर पर भरोसा करने के लिए आईओएस प्राप्त करने के लिए मुझे किसी भी तरह से इस कस्टम सीए प्रमाणपत्र को भरोसेमंद प्रमाणपत्रों की सूची में जोड़ना होगा जो कि ट्रस्टस्टोर को जावा/एंड्रॉइड में कैसे काम करता है।स्विफ्ट

मैंने नीचे दिए गए कोड का उपयोग करके कनेक्ट करने का प्रयास किया है और कोई त्रुटि नहीं है हालांकि लिखने() फ़ंक्शन सफल नहीं लग रहा है।

मुख्य दृश्य नियंत्रक:

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 संस्करण में ठीक किया है यह वही ऐप मैं बस एसएसएल के आईओएस कार्यान्वयन के साथ उलझन में हूँ।

मैं जावा पृष्ठभूमि से हूं और इस समस्या के साथ 3 सप्ताह तक जा रहा हूं। किसी भी सहायता की सराहना की जाएगी।

स्विफ्ट कोड, नहीं उद्देश्य सी में जवाब पसंद करते हैं, लेकिन अगर आप केवल है Obj सी ठीक भी thats :)

उत्तर

6

ठीक है, मैं इस मुद्दे पर 8 सप्ताह के खर्च :(लेकिन मैं अंत में एक साथ काम कर रहे समाधान डाल करने में कामयाब रहे। मुझे कहना होगा कि आईओएस पर एसएसएल/टीएलएस एक मजाक है। एंड्रॉइड पर जावा इसे मृत के लिए छोड़ देता है। यह पूरी तरह हास्यास्पद है कि स्वयं हस्ताक्षरित प्रमाण पत्र के लिए विश्वास का मूल्यांकन करने के लिए, आपको प्रमाण पत्र श्रृंखला सत्यापन को पूरी तरह से अक्षम करना होगा और इसे स्वयं करना होगा। हास्यास्पद। वैसे भी यह एक पूरी तरह से काम कर रहा समाधान है जो स्वयं हस्ताक्षरित सर्वर प्रमाण पत्र का उपयोग कर रिमोट सॉकेट सर्वर (कोई 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 
     } 
    } 
} 
+0

धन्यवाद आदमी, यह अच्छी चीजें है! मैंने पहले ही सॉकेट की कार्यक्षमता का बड़ा हिस्सा निकाला था, लेकिन इससे मुझे एसएसएल काम करने में मदद मिली। – Sethmr

+0

मेरी इच्छा है कि मैंने यह देखा था जब मैं शुरुआत में इस पूरी चीज की समस्या निवारण कर रहा था – Sethmr

+0

खुशी हुई कि इससे आपकी मदद मिली। ईमानदारी से एसएसएल के लिए ऐप्पल बनाता है डिजाइन निर्णय भयानक हैं। आपको अपना खुद का भरोसेमंद रूट सीए "पहले" ट्रस्ट का मूल्यांकन करने में सक्षम होना चाहिए :( – Snapper26