Obtener certificate de cliente para que funcione para la authentication mutua mediante Swift 3 y Alamofire 4

Estoy tratando de averiguar cómo usar Alamofire 4.0 con Swift 3.0 para enviar un p12 (también tengo el certificate PEM y la key si es necesario) a un website para la authentication. Todos los ejemplos que he visto son para Swift 2.0 y no exactamente lo que estoy buscando. En safari en mi mac puedo acceder al sitio poniendo el p12 en el llavero y enviándolo cuando safari pregunta, así que sé que esa porción funciona. No sé si alguien puede ayudarme con un ejemplo de cómo hacerlo en Alamofire 4.0 y Swift 3.0 en una aplicación. Los certificates están autofirmados también.

¿Alguna idea o ayuda? No solo busco fijar el certificate como la key del cliente y el certificate debe enviarse al server para acceder …

Pude hacerlo funcionar Algunos problemas se interponen en el path. Primero, debe permitir que IOS acepte certificates autofirmados. Esto requiere configurar AlamoFire serverTrustPolicy:

let serverTrustPolicies: [String: ServerTrustPolicy] = [ "your-domain.com": .disableEvaluation ] self.sessionManager = Alamofire.SessionManager( serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies) ) 

A partir de ahí, debe anular sessionDidRecieveChallenge para enviar el certificate del cliente. Debido a que quería usar un file p12, modifiqué un código que encontré en otro lugar (lo siento, ya no tengo la fuente) para hacer que Swift 3.0 importara el p12 usando classs de base:

 import Foundation public class PKCS12 { var label:String? var keyID:Data? var trust:SecTrust? var certChain:[SecTrust]? var identity:SecIdentity? let securityError:OSStatus public init(data:Data, password:String) { //self.securityError = errSecSuccess var items:CFArray? let certOptions:NSDictionary = [kSecImportExportPassphrase as NSString:password as NSString] // import certificate to read its entries self.securityError = SecPKCS12Import(data as NSData, certOptions, &items); if securityError == errSecSuccess { let certItems:Array = (items! as Array) let dict:Dictionary<String, AnyObject> = certItems.first! as! Dictionary<String, AnyObject>; self.label = dict[kSecImportItemLabel as String] as? String; self.keyID = dict[kSecImportItemKeyID as String] as? Data; self.trust = dict[kSecImportItemTrust as String] as! SecTrust?; self.certChain = dict[kSecImportItemCertChain as String] as? Array<SecTrust>; self.identity = dict[kSecImportItemIdentity as String] as! SecIdentity?; } } public convenience init(mainBundleResource:String, resourceType:String, password:String) { self.init(data: NSData(contentsOfFile: Bundle.main.path(forResource: mainBundleResource, ofType:resourceType)!)! as Data, password: password); } public func urlCnetworkingential() -> URLCnetworkingential { return URLCnetworkingential( identity: self.identity!, certificates: self.certChain!, persistence: URLCnetworkingential.Persistence.forSession); } } 

Esto me permitirá importar el file y enviarlo de vuelta al cliente.

 let cert = PKCS12.init(mainBundleResource: "cert", resourceType: "p12", password: "password"); self.sessionManager.delegate.sessionDidReceiveChallenge = { session, challenge in if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate { return (URLSession.AuthChallengeDisposition.useCnetworkingential, self.cert.urlCnetworkingential()); } if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { return (URLSession.AuthChallengeDisposition.useCnetworkingential, URLCnetworkingential(trust: challenge.protectionSpace.serverTrust!)); } return (URLSession.AuthChallengeDisposition.performDefaultHandling, Optional.none); } 

Ahora puede usar sessionManager para crear tantas llamadas como necesite.

Como nota, también agregué lo siguiente a info.plist según lo recomendado para moverte por las nuevas funciones de security en las nuevas funciones de iOS:

 <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> <key>NSExceptionDomains</key> <dict> <key>your-domain.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSExceptionRequiresForwardSecrecy</key> <false/> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict> 

¡Espero que esto ayude!

Aquí está mi ejemplo que puede ayudar a alguien (Alamofire 4.0, Swift 3, xCode 8)

 import Alamofire class NetworkConnection { let developmentDomain = Config.developmentDomain // "api.myappdev.com" let productionDomain = Config.productionDomain // "api.myappprod.com" let certificateFilename = Config.certificateFilename // "godaddy" let certificateExtension = Config.certificateExtension // "der" let useSSL = true var manager: SessionManager! var serverTrustPolicies: [String : ServerTrustPolicy] = [String:ServerTrustPolicy]() static let shanetworkingManager = NetworkConnection() init(){ if useSSL { manager = initSafeManager() } else { manager = initUnsafeManager() } } //USED FOR SITES WITH CERTIFICATE, OTHERWISE .DisableEvaluation func initSafeManager() -> SessionManager { setServerTrustPolicies() manager = SessionManager(configuration: URLSessionConfiguration.default, delegate: SessionDelegate(), serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)) return manager } //USED FOR SITES WITHOUT CERTIFICATE, DOESN'T CHECK FOR CERTIFICATE func initUnsafeManager() -> SessionManager { manager = Alamofire.SessionManager.default manager.delegate.sessionDidReceiveChallenge = { session, challenge in var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling var cnetworkingential: URLCnetworkingential? if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { disposition = URLSession.AuthChallengeDisposition.useCnetworkingential cnetworkingential = URLCnetworkingential(trust: challenge.protectionSpace.serverTrust!) //URLCnetworkingential(forTrust: challenge.protectionSpace.serverTrust!) } else { if challenge.previousFailureCount > 0 { disposition = .cancelAuthenticationChallenge } else { cnetworkingential = self.manager.session.configuration.urlCnetworkingentialStorage?.defaultCnetworkingential(for: challenge.protectionSpace) if cnetworkingential != nil { disposition = .useCnetworkingential } } } return (disposition, cnetworkingential) } return manager } func setServerTrustPolicies() { let pathToCert = Bundle.main.path(forResource: certificateFilename, ofType: certificateExtension) let localCertificate:Data = try! Data(contentsOf: URL(fileURLWithPath: pathToCert!)) let serverTrustPolicies: [String: ServerTrustPolicy] = [ productionDomain: .pinCertificates( certificates: [SecCertificateCreateWithData(nil, localCertificate as CFData)!], validateCertificateChain: true, validateHost: true ), developmentDomain: .disableEvaluation ] self.serverTrustPolicies = serverTrustPolicies } static func addAuthorizationHeader (_ token: String, tokenType: String) -> [String : String] { let headers = [ "Authorization": tokenType + " " + token ] return headers } } 

agregar siguiente a tu Info.plist

  <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> <key>NSExceptionDomains</key> <dict> <key>api.myappdev.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSExceptionRequiresForwardSecrecy</key> <false/> <key>NSIncludesSubdomains</key> <true/> <key>NSRequiresCertificateTransparency</key> <false/> <key>NSTemporaryExceptionMinimumTLSVersion</key> <string>TLSv1.2</string> </dict> </dict> </dict> 

y aquí hay un ejemplo de hacer una request

 import Alamofire class ActionUserUpdate { let url = "https://api.myappdev.com/v1/" let manager = NetworkConnection.shanetworkingManager.manager func updateUser(_ token: String, tokenType: String, expiresIn: Int, params: [String : String]) { let headers = NetworkConnection.addAuthorizationHeader(token, tokenType: tokenType) manager?.request(url, method: .put, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { response in print(response.description) print(response.debugDescription) print(response.request) // original URL request print(response.response) // URL response print(response.data) // server data print(response.result) // result of response serialization } } }