iOS certificate pinning with Swift and NSURLSession

前端 未结 7 829
无人及你
无人及你 2020-12-07 18:04

Howto add certificate pinning to a NSURLSession in Swift?

The OWASP website contains only an example for Objective-C and NSURLConnection.

7条回答
  •  不思量自难忘°
    2020-12-07 18:37

    Thanks to the example found in this site: https://www.bugsee.com/blog/ssl-certificate-pinning-in-mobile-applications/ I built a version that pins the public key and not the entire certificate (more convenient if you renew your certificate periodically).

    Update: Removed the forced unwrapping and replaced SecTrustEvaluate.

    import Foundation
    import CommonCrypto
    
    class SessionDelegate : NSObject, URLSessionDelegate {
    
    private static let rsa2048Asn1Header:[UInt8] = [
        0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
        0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
    ];
    
    private static let google_com_pubkey = ["4xVxzbEegwDBoyoGoJlKcwGM7hyquoFg4l+9um5oPOI="];
    private static let google_com_full = ["KjLxfxajzmBH0fTH1/oujb6R5fqBiLxl0zrl2xyFT2E="];
    
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    
        guard let serverTrust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil);
            return;
        }
    
        // Set SSL policies for domain name check
        let policies = NSMutableArray();
        policies.add(SecPolicyCreateSSL(true, (challenge.protectionSpace.host as CFString)));
        SecTrustSetPolicies(serverTrust, policies);
    
        var isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil);
    
        if(isServerTrusted && challenge.protectionSpace.host == "www.google.com") {
            let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
            //Compare public key
            if #available(iOS 10.0, *) {
                let policy = SecPolicyCreateBasicX509();
                let cfCertificates = [certificate] as CFArray;
    
                var trust: SecTrust?
                SecTrustCreateWithCertificates(cfCertificates, policy, &trust);
    
                guard trust != nil, let pubKey = SecTrustCopyPublicKey(trust!) else {
                    completionHandler(.cancelAuthenticationChallenge, nil);
                    return;
                }
    
                var error:Unmanaged?
                if let pubKeyData = SecKeyCopyExternalRepresentation(pubKey, &error) {
                    var keyWithHeader = Data(bytes: SessionDelegate.rsa2048Asn1Header);
                    keyWithHeader.append(pubKeyData as Data);
                    let sha256Key = sha256(keyWithHeader);
                    if(!SessionDelegate.google_com_pubkey.contains(sha256Key)) {
                        isServerTrusted = false;
                    }
                } else {
                    isServerTrusted = false;
                }
            } else { //Compare full certificate
                let remoteCertificateData = SecCertificateCopyData(certificate!) as Data;
                let sha256Data = sha256(remoteCertificateData);
                if(!SessionDelegate.google_com_full.contains(sha256Data)) {
                    isServerTrusted = false;
                }
            }
        }
    
        if(isServerTrusted) {
            let credential = URLCredential(trust: serverTrust);
            completionHandler(.useCredential, credential);
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil);
        }
    
    }
    
    func sha256(_ data : Data) -> String {
        var hash = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))
        data.withUnsafeBytes {
            _ = CC_SHA256($0, CC_LONG(data.count), &hash)
        }
        return Data(bytes: hash).base64EncodedString();
    }
    
    }
    

提交回复
热议问题