From ac663a8875661794d22c2856188e9572f01b99fa Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 22 Jul 2015 06:28:06 -0400 Subject: [PATCH] bump websocket version --- README.md | 4 +- Socket.IO-Client-Swift.podspec | 4 +- SocketIOClientSwift/WebSocket.swift | 468 +++++++++++++++++++++------- 3 files changed, 363 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index c069e0b..657f2e7 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Carthage ----------------- Add this line to your `Cartfile`: ``` -github "socketio/socket.io-client-swift" ~> 2.3.9 # Or latest version +github "socketio/socket.io-client-swift" ~> 2.3.10 # Or latest version ``` Run `carthage update`. @@ -79,7 +79,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! -pod 'Socket.IO-Client-Swift', '~> 2.3.9' # Or latest version +pod 'Socket.IO-Client-Swift', '~> 2.3.10' # Or latest version ``` Install pods: diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index 47c6487..fcfc4fb 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" - s.version = "2.3.9" + s.version = "2.3.10" s.summary = "Socket.IO-client for iOS and OS X" s.description = <<-DESC Socket.IO-client for iOS and OS X. @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.author = { "Erik" => "nuclear.ace@gmail.com" } s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' - s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v2.3.9' } + s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v2.3.10' } s.source_files = "SocketIOClientSwift/**/*.swift" s.requires_arc = true # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files diff --git a/SocketIOClientSwift/WebSocket.swift b/SocketIOClientSwift/WebSocket.swift index 8ddd2c1..810f1b4 100644 --- a/SocketIOClientSwift/WebSocket.swift +++ b/SocketIOClientSwift/WebSocket.swift @@ -7,6 +7,7 @@ ////////////////////////////////////////////////////////////////////////////////////////////////// import Foundation +import CoreFoundation public protocol WebSocketDelegate: class { func websocketDidConnect(socket: WebSocket) @@ -15,6 +16,10 @@ public protocol WebSocketDelegate: class { func websocketDidReceiveData(socket: WebSocket, data: NSData) } +public protocol WebSocketPongDelegate: class { + func websocketDidReceivePong(socket: WebSocket) +} + public class WebSocket : NSObject, NSStreamDelegate { enum OpCode : UInt8 { @@ -78,69 +83,63 @@ public class WebSocket : NSObject, NSStreamDelegate { var buffer: NSMutableData? } - private var cookies:[NSHTTPCookie]? public weak var delegate: WebSocketDelegate? + public weak var pongDelegate: WebSocketPongDelegate? + public var onConnect: ((Void) -> Void)? + public var onDisconnect: ((NSError?) -> Void)? + public var onText: ((String) -> Void)? + public var onData: ((NSData) -> Void)? + public var onPong: ((Void) -> Void)? + public var headers = Dictionary() + public var voipEnabled = false + public var selfSignedSSL = false + public var security: Security? + public var isConnected :Bool { + return connected + } + + private var cookies:[NSHTTPCookie]? private var url: NSURL private var inputStream: NSInputStream? private var outputStream: NSOutputStream? private var isRunLoop = false private var connected = false + private var isCreated = false private var writeQueue: NSOperationQueue? private var readStack = Array() private var inputQueue = Array() private var fragBuffer: NSData? - public var headers = Dictionary() - public var voipEnabled = false - public var selfSignedSSL = false - private var connectedBlock: ((Void) -> Void)? = nil - private var disconnectedBlock: ((NSError?) -> Void)? = nil - private var receivedTextBlock: ((String) -> Void)? = nil - private var receivedDataBlock: ((NSData) -> Void)? = nil - public var isConnected :Bool { - return connected - } + private var certValidated = false + private var didDisconnect = false //init the websocket with a url public init(url: NSURL) { self.url = url } + public convenience init(url: NSURL, cookies:[NSHTTPCookie]?) { self.init(url: url) self.cookies = cookies - } + //used for setting protocols. public convenience init(url: NSURL, protocols: Array) { self.init(url: url) optionalProtocols = protocols } - //closure based instead of the delegate - public convenience init(url: NSURL, protocols: Array, connect:((Void) -> Void), disconnect:((NSError?) -> Void), text:((String) -> Void), data:(NSData) -> Void) { - self.init(url: url, protocols: protocols) - connectedBlock = connect - disconnectedBlock = disconnect - receivedTextBlock = text - receivedDataBlock = data - } - //same as above, just shorter - public convenience init(url: NSURL, connect:((Void) -> Void), disconnect:((NSError?) -> Void), text:((String) -> Void)) { - self.init(url: url) - connectedBlock = connect - disconnectedBlock = disconnect - receivedTextBlock = text - } - //same as above, just shorter - public convenience init(url: NSURL, connect:((Void) -> Void), disconnect:((NSError?) -> Void), data:((NSData) -> Void)) { - self.init(url: url) - connectedBlock = connect - disconnectedBlock = disconnect - receivedDataBlock = data - } ///Connect to the websocket server on a background thread public func connect() { + if isCreated { + return + } + dispatch_async(queue,{ + self.didDisconnect = false + }) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), { + self.isCreated = true self.createHTTPRequest() + self.isCreated = false }) } @@ -171,7 +170,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let str: NSString = url.absoluteString! let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET", - url, kCFHTTPVersion1_1) + url, kCFHTTPVersion1_1).takeRetainedValue() var port = url.port if port == nil { @@ -202,14 +201,14 @@ public class WebSocket : NSObject, NSStreamDelegate { self.addHeader(urlRequest, key: key, val: value) } - let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest.takeUnretainedValue()).takeUnretainedValue() + let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest).takeRetainedValue() self.initStreamsWithData(serializedRequest, Int(port!)) } //Add a header to the CFHTTPMessage by using the NSString bridges to CFString - private func addHeader(urlRequest: Unmanaged,key: String, val: String) { + private func addHeader(urlRequest: CFHTTPMessage,key: String, val: String) { let nsKey: NSString = key let nsVal: NSString = val - CFHTTPMessageSetHeaderFieldValue(urlRequest.takeUnretainedValue(), + CFHTTPMessageSetHeaderFieldValue(urlRequest, nsKey, nsVal) } @@ -234,14 +233,16 @@ public class WebSocket : NSObject, NSStreamDelegate { var writeStream: Unmanaged? let h: NSString = url.host! CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream) - inputStream = readStream!.takeUnretainedValue() - outputStream = writeStream!.takeUnretainedValue() + inputStream = readStream!.takeRetainedValue() + outputStream = writeStream!.takeRetainedValue() inputStream!.delegate = self outputStream!.delegate = self if url.scheme == "wss" || url.scheme == "https" { inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) + } else { + certValidated = true //not a https session, so no need to check SSL pinning } if self.voipEnabled { inputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) @@ -249,8 +250,8 @@ public class WebSocket : NSObject, NSStreamDelegate { } if self.selfSignedSSL { let settings: Dictionary = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull] - inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) - outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) + inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String) + outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String) } isRunLoop = true inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) @@ -266,6 +267,20 @@ public class WebSocket : NSObject, NSStreamDelegate { //delegate for the stream methods. Processes incoming bytes public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { + if let sec = security where !certValidated && (eventCode == .HasBytesAvailable || eventCode == .HasSpaceAvailable) { + var possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as! String) + if let trust: AnyObject = possibleTrust { + var domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as! String) + if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) { + certValidated = true + } else { + let error = self.errorWithDetail("Invalid SSL certificate", code: 1) + doDisconnect(error) + disconnectStream(error) + return + } + } + } if eventCode == .HasBytesAvailable { if(aStream == inputStream) { processInputStream() @@ -281,20 +296,19 @@ public class WebSocket : NSObject, NSStreamDelegate { if writeQueue != nil { writeQueue!.waitUntilAllOperationsAreFinished() } - inputStream!.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) - outputStream!.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) - inputStream!.close() - outputStream!.close() - inputStream = nil + if let stream = inputStream { + stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) + stream.close() + } + if let stream = outputStream { + stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) + stream.close() + } outputStream = nil isRunLoop = false + certValidated = false + self.doDisconnect(error) connected = false - dispatch_async(queue,{ - if let disconnectBlock = self.disconnectedBlock { - disconnectBlock(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) - }) } ///handles the incoming bytes and sending them to the proper processing method @@ -306,14 +320,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if !connected { connected = processHTTP(buffer, bufferLen: length) if !connected { - dispatch_async(queue,{ - //self.workaroundMethod() - let error = self.errorWithDetail("Invalid HTTP upgrade", code: 1) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) - }) + self.doDisconnect(self.errorWithDetail("Invalid HTTP upgrade", code: 1)) } } else { var process = false @@ -363,8 +370,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if totalSize > 0 { if validateResponse(buffer, bufferLen: totalSize) { dispatch_async(queue,{ - //self.workaroundMethod() - if let connectBlock = self.connectedBlock { + if let connectBlock = self.onConnect { connectBlock() } self.delegate?.websocketDidConnect(self) @@ -428,10 +434,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if((isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != OpCode.Pong.rawValue) { let errCode = CloseCode.ProtocolError.rawValue let error = self.errorWithDetail("masked and rsv data is not currently supported", code: errCode) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(errCode) return } @@ -440,20 +443,14 @@ public class WebSocket : NSObject, NSStreamDelegate { receivedOpcode != OpCode.TextFrame.rawValue && receivedOpcode != OpCode.Pong.rawValue) { let errCode = CloseCode.ProtocolError.rawValue let error = self.errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(errCode) return } if isControlFrame && isFin == 0 { let errCode = CloseCode.ProtocolError.rawValue let error = self.errorWithDetail("control frames can't be fragmented", code: errCode) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(errCode) return } @@ -463,7 +460,7 @@ public class WebSocket : NSObject, NSStreamDelegate { code = CloseCode.ProtocolError.rawValue } else if payloadLen > 1 { var codeBuffer = UnsafePointer((buffer+offset)) - code = codeBuffer[0].byteSwapped + code = codeBuffer[0].bigEndian if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) { code = CloseCode.ProtocolError.rawValue } @@ -480,10 +477,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } } let error = self.errorWithDetail("connection closed by server", code: code) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(code) return } @@ -494,13 +488,17 @@ public class WebSocket : NSObject, NSStreamDelegate { var dataLength = UInt64(payloadLen) if dataLength == 127 { let bytes = UnsafePointer((buffer+offset)) - dataLength = bytes[0].byteSwapped + dataLength = bytes[0].bigEndian offset += sizeof(UInt64) } else if dataLength == 126 { let bytes = UnsafePointer((buffer+offset)) - dataLength = UInt64(bytes[0].byteSwapped) + dataLength = UInt64(bytes[0].bigEndian) offset += sizeof(UInt16) } + if bufferLen < offset { + fragBuffer = NSData(bytes: buffer, length: bufferLen) + return + } var len = dataLength if dataLength > UInt64(bufferLen) { len = UInt64(bufferLen-offset) @@ -513,7 +511,14 @@ public class WebSocket : NSObject, NSStreamDelegate { data = NSData(bytes: UnsafePointer((buffer+offset)), length: Int(len)) } if receivedOpcode == OpCode.Pong.rawValue { - let step = offset + Int(len) + dispatch_async(queue,{ + if let pongBlock = self.onPong { + pongBlock() + } + self.pongDelegate?.websocketDidReceivePong(self) + }) + + let step = Int(offset+numericCast(len)) let extra = bufferLen-step if extra > 0 { processRawMessage((buffer+step), bufferLen: extra) @@ -527,10 +532,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if isFin == 0 && receivedOpcode == OpCode.ContinueFrame.rawValue && response == nil { let errCode = CloseCode.ProtocolError.rawValue let error = self.errorWithDetail("continue frame before a binary or text frame", code: errCode) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(errCode) return } @@ -540,10 +542,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let errCode = CloseCode.ProtocolError.rawValue let error = self.errorWithDetail("first frame can't be a continue frame", code: errCode) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(errCode) return } @@ -559,10 +558,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let errCode = CloseCode.ProtocolError.rawValue let error = self.errorWithDetail("second and beyond of fragment message must be a continue frame", code: errCode) - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) writeError(errCode) return } @@ -578,7 +574,7 @@ public class WebSocket : NSObject, NSStreamDelegate { processResponse(response!) } - let step = offset + Int(len) + let step = Int(offset+numericCast(len)) let extra = bufferLen-step if(extra > 0) { processExtra((buffer+step), bufferLen: extra) @@ -609,16 +605,15 @@ public class WebSocket : NSObject, NSStreamDelegate { return false } dispatch_async(queue,{ - if let textBlock = self.receivedTextBlock{ - textBlock(str! as String) + if let textBlock = self.onText { + textBlock(str! as! String) } - self.delegate?.websocketDidReceiveMessage(self, text: str! as String) + self.delegate?.websocketDidReceiveMessage(self, text: str! as! String) }) } else if response.code == .BinaryFrame { let data = response.buffer! //local copy so it is perverse for writing dispatch_async(queue,{ - //self.workaroundMethod() - if let dataBlock = self.receivedDataBlock{ + if let dataBlock = self.onData { dataBlock(data) } self.delegate?.websocketDidReceiveData(self, data: data) @@ -641,10 +636,10 @@ public class WebSocket : NSObject, NSStreamDelegate { private func writeError(code: UInt16) { let buf = NSMutableData(capacity: sizeof(UInt16)) var buffer = UnsafeMutablePointer(buf!.bytes) - buffer[0] = code.byteSwapped + buffer[0] = code.bigEndian dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose) } - ///used to write things to the stream in a + ///used to write things to the stream private func dequeueWrite(data: NSData, code: OpCode) { if writeQueue == nil { writeQueue = NSOperationQueue() @@ -676,12 +671,12 @@ public class WebSocket : NSObject, NSStreamDelegate { } else if dataLength <= Int(UInt16.max) { buffer[1] = 126 var sizeBuffer = UnsafeMutablePointer((buffer+offset)) - sizeBuffer[0] = UInt16(dataLength).byteSwapped + sizeBuffer[0] = UInt16(dataLength).bigEndian offset += sizeof(UInt16) } else { buffer[1] = 127 var sizeBuffer = UnsafeMutablePointer((buffer+offset)) - sizeBuffer[0] = UInt64(dataLength).byteSwapped + sizeBuffer[0] = UInt64(dataLength).bigEndian offset += sizeof(UInt64) } buffer[1] |= self.MaskMask @@ -708,10 +703,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let errCode = InternalErrorCode.OutputStreamWriteError.rawValue error = self.errorWithDetail("output stream error during write", code: errCode) } - if let disconnect = self.disconnectedBlock { - disconnect(error) - } - self.delegate?.websocketDidDisconnect(self, error: error) + self.doDisconnect(error) break } else { total += len! @@ -724,4 +716,262 @@ public class WebSocket : NSObject, NSStreamDelegate { } } + ///used to preform the disconnect delegate + private func doDisconnect(error: NSError?) { + if !self.didDisconnect { + dispatch_async(queue,{ + self.didDisconnect = true + if let disconnect = self.onDisconnect { + disconnect(error) + } + self.delegate?.websocketDidDisconnect(self, error: error) + }) + } + } + +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Security.swift +// Starscream +// +// Created by Dalton Cherry on 5/16/15. +// Copyright (c) 2015 Vluxe. All rights reserved. +// +////////////////////////////////////////////////////////////////////////////////////////////////// + +import Foundation +import Security + +public class SSLCert { + var certData: NSData? + var key: SecKeyRef? + + /** + Designated init for certificates + + :param: data is the binary data of the certificate + + :returns: a representation security object to be used with + */ + public init(data: NSData) { + self.certData = data + } + + /** + Designated init for public keys + + :param: key is the public key to be used + + :returns: a representation security object to be used with + */ + public init(key: SecKeyRef) { + self.key = key + } +} + +public class Security { + public var validatedDN = true //should the domain name be validated? + + var isReady = false //is the key processing done? + var certificates: [NSData]? //the certificates + var pubKeys: [SecKeyRef]? //the public keys + var usePublicKeys = false //use public keys or certificate validation? + + /** + Use certs from main app bundle + + :param: usePublicKeys is to specific if the publicKeys or certificates should be used for SSL pinning validation + + :returns: a representation security object to be used with + */ + public convenience init(usePublicKeys: Bool = false) { + let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".") + var collect = Array() + for path in paths { + if let d = NSData(contentsOfFile: path as! String) { + collect.append(SSLCert(data: d)) + } + } + self.init(certs:collect, usePublicKeys: usePublicKeys) + } + + /** + Designated init + + :param: keys is the certificates or public keys to use + :param: usePublicKeys is to specific if the publicKeys or certificates should be used for SSL pinning validation + + :returns: a representation security object to be used with + */ + public init(certs: [SSLCert], usePublicKeys: Bool) { + self.usePublicKeys = usePublicKeys + + if self.usePublicKeys { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), { + var collect = Array() + for cert in certs { + if let data = cert.certData where cert.key == nil { + cert.key = self.extractPublicKey(data) + } + if let k = cert.key { + collect.append(k) + } + } + self.pubKeys = collect + self.isReady = true + }) + } else { + var collect = Array() + for cert in certs { + if let d = cert.certData { + collect.append(d) + } + } + self.certificates = collect + self.isReady = true + } + } + + /** + Valid the trust and domain name. + + :param: trust is the serverTrust to validate + :param: domain is the CN domain to validate + + :returns: if the key was successfully validated + */ + public func isValid(trust: SecTrustRef, domain: String?) -> Bool { + + var tries = 0 + while(!self.isReady) { + usleep(1000) + tries += 1 + if tries > 5 { + return false //doesn't appear it is going to ever be ready... + } + } + var policy: SecPolicyRef + if self.validatedDN { + policy = SecPolicyCreateSSL(1, domain).takeRetainedValue() + } else { + policy = SecPolicyCreateBasicX509().takeRetainedValue() + } + SecTrustSetPolicies(trust,policy) + if self.usePublicKeys { + if let keys = self.pubKeys { + var trustedCount = 0 + let serverPubKeys = publicKeyChainForTrust(trust) + for serverKey in serverPubKeys as [AnyObject] { + for key in keys as [AnyObject] { + if serverKey.isEqual(key) { + trustedCount++ + break + } + } + } + if trustedCount == serverPubKeys.count { + return true + } + } + } else if let certs = self.certificates { + let serverCerts = certificateChainForTrust(trust) + var collect = Array() + for cert in certs { + collect.append(SecCertificateCreateWithData(nil,cert).takeRetainedValue()) + } + SecTrustSetAnchorCertificates(trust,collect) + var result: SecTrustResultType = 0 + SecTrustEvaluate(trust,&result) + let r = Int(result) + if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed { + var trustedCount = 0 + for serverCert in serverCerts { + for cert in certs { + if cert == serverCert { + trustedCount++ + break + } + } + } + if trustedCount == serverCerts.count { + return true + } + } + } + return false + } + + /** + Get the public key from a certificate data + + :param: data is the certificate to pull the public key from + + :returns: a public key + */ + func extractPublicKey(data: NSData) -> SecKeyRef? { + var publicKey: NSData? + let possibleCert = SecCertificateCreateWithData(nil,data) + if let cert = possibleCert { + return extractPublicKeyFromCert(cert.takeRetainedValue(),policy: SecPolicyCreateBasicX509().takeRetainedValue()) + } + return nil + } + + /** + Get the public key from a certificate + + :param: data is the certificate to pull the public key from + + :returns: a public key + */ + func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? { + var possibleTrust: Unmanaged? + SecTrustCreateWithCertificates(cert,policy, &possibleTrust) + if let trust = possibleTrust { + let t = trust.takeRetainedValue() + var result: SecTrustResultType = 0 + SecTrustEvaluate(t,&result) + return SecTrustCopyPublicKey(t).takeRetainedValue() + } + return nil + } + + /** + Get the certificate chain for the trust + + :param: trust is the trust to lookup the certificate chain for + + :returns: the certificate chain for the trust + */ + func certificateChainForTrust(trust: SecTrustRef) -> Array { + var collect = Array() + for var i = 0; i < SecTrustGetCertificateCount(trust); i++ { + let cert = SecTrustGetCertificateAtIndex(trust,i) + collect.append(SecCertificateCopyData(cert.takeRetainedValue()).takeRetainedValue()) + } + return collect + } + + /** + Get the public key chain for the trust + + :param: trust is the trust to lookup the certificate chain and extract the public keys + + :returns: the public keys from the certifcate chain for the trust + */ + func publicKeyChainForTrust(trust: SecTrustRef) -> Array { + var collect = Array() + let policy = SecPolicyCreateBasicX509().takeRetainedValue() + for var i = 0; i < SecTrustGetCertificateCount(trust); i++ { + let cert = SecTrustGetCertificateAtIndex(trust,i) + if let key = extractPublicKeyFromCert(cert.takeRetainedValue(), policy: policy) { + collect.append(key) + } + } + return collect + } + + } \ No newline at end of file