使用 Swift 3 和 Alamofire 4 获取客户端证书以进行相互身份验证

使用 Swift 3 和 Alamofire 4 获取客户端证书以进行相互身份验证

问题描述:

我想弄清楚如何使用 Alamofire 4.0 和 Swift 3.0 将 p12(如果需要,我还有 PEM 证书和密钥)发送到网站进行身份验证.我看到的所有示例都是针对 Swift 2.0 的,而不完全是我正在寻找的.在我的 Mac 上的 safari 中,我可以通过将 p12 放在钥匙串中并在 safari 询问时发送它来访问该站点,这样我就知道该部分有效.我不知道是否有人可以帮助我举例说明如何在应用程序中的 Alamofire 4.0 和 Swift 3.0 中执行此操作.证书也是自签名的.

I am trying to figure out how to use Alamofire 4.0 with Swift 3.0 to send a p12 (i also have the PEM cert and key if need be) to a website for authentication. All the examples i have seen are for Swift 2.0 and not exactly what i'm looking for. In safari on my mac i can access the site by putting the p12 in the keychain and sending it when safari asks so i know that portion works. I don't know if anyone can help me with an example of how to do so in Alamofire 4.0 and Swift 3.0 in an application. The certificates are self signed as well.

有什么想法或帮助吗?我不只是想固定证书,因为需要将客户端密钥和证书发送到服务器进行访问...

Any thoughts or help? I am not just looking to pin the certificate as the client key and cert needs to be sent to the server for access...

我能够让它工作.出现了一些问题.首先,您必须允许 IOS 接受自签名证书.这需要设置 AlamoFire serverTrustPolicy:

I was able to get it to work. A few issues got into the way. First, you have to allow IOS to accept self signed certificates. This requires to set up AlamoFire serverTrustPolicy:

let serverTrustPolicies: [String: ServerTrustPolicy] = [
        "your-domain.com": .disableEvaluation
    ]

self.sessionManager = Alamofire.SessionManager(
        serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
    )

从那里,您必须覆盖 sessionDidRecieveChallenge 以发送客户端证书.因为我想使用 p12 文件,所以我修改了我在其他地方找到的一些代码(对不起,我没有源代码了)使 Swift 3.0 使用基础类导入 p12:

From there, you have to override the sessionDidRecieveChallenge to send the client certificate. Because i wanted to use a p12 file I modified some code I found elsewhere (sorry i don't have the source anymore) to make is Swift 3.0 to import the p12 using foundation classes:

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);

    }



}

这将允许我导入文件,并将其发送回客户端.

This will allow me to import the file, and send it back to the client.

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 根据需要创建任意数量的调用.

Now you can use the sessionManager to create as many calls as you need to.

请注意,我还在 info.plist 中添加了以下建议,以避开较新的 iOS 功能中的新安全功能:

As a note, i've also added the following to the info.plist as recomended to get around the new security features in newer iOS features:

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

我希望这会有所帮助!