我正在试图弄清楚如何使用Alamofire 4.0和Swift 3.0将p12(我还有PEM证书和密钥,如果需要的话)发送到网站进行身份验证。我见过的所有例子都是针对Swift 2.0而不是我正在寻找的。在我的Mac上的safari中,我可以通过将p12放入钥匙串并在safari询问时发送它来访问该网站,所以我知道该部分有效。我不知道是否有人可以帮助我在一个应用程序中的Alamofire 4.0和Swift 3.0中如何这样做。证书也是自签名的。
有任何想法或帮助吗?我不只是想将证书固定为客户端密钥,并且需要将证书发送到服务器以进行访问...
答案 0 :(得分:17)
我能够让它发挥作用。一些问题陷入了困境。首先,您必须允许IOS接受自签名证书。这需要设置AlamoFire serverTrustPolicy:
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"your-domain.com": .disableEvaluation
]
self.sessionManager = Alamofire.SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
从那里,您必须覆盖sessionDidRecieveChallenge以发送客户端证书。因为我想使用p12文件,我修改了一些我在其他地方找到的代码(抱歉,我没有源代码)使用Swift 3.0来导入p12使用基础类:
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 urlCredential() -> URLCredential {
return URLCredential(
identity: self.identity!,
certificates: self.certChain!,
persistence: URLCredential.Persistence.forSession);
}
}
这将允许我导入文件,并将其发送回客户端。
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.useCredential, self.cert.urlCredential());
}
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
return (URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!));
}
return (URLSession.AuthChallengeDisposition.performDefaultHandling, Optional.none);
}
现在,您可以使用sessionManager根据需要创建任意数量的呼叫。
作为一个说明,我还在info.plist中添加了以下内容,以推荐更新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>
我希望这有帮助!
答案 1 :(得分:6)
这是我可以帮助某人的例子(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 sharedManager = 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 credential: URLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) //URLCredential(forTrust: challenge.protectionSpace.serverTrust!)
} else {
if challenge.previousFailureCount > 0 {
disposition = .cancelAuthenticationChallenge
} else {
credential = self.manager.session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
if credential != nil {
disposition = .useCredential
}
}
}
return (disposition, credential)
}
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
}
}
将以下内容添加到您的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>
这是一个发出请求的例子
import Alamofire
class ActionUserUpdate {
let url = "https://api.myappdev.com/v1/"
let manager = NetworkConnection.sharedManager.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
}
}
}