Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/102.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
iOS证书与Swift和NSURLSession绑定_Ios_Swift_Ssl_Owasp_Pinning - Fatal编程技术网

iOS证书与Swift和NSURLSession绑定

iOS证书与Swift和NSURLSession绑定,ios,swift,ssl,owasp,pinning,Ios,Swift,Ssl,Owasp,Pinning,如何在Swift中将证书固定添加到NSURLSession 仅包含Objective-C和NSURLConnection的示例。Swift 3+更新: 只需为nsurlsessionelegate定义一个委托类并实现didReceiveChallenge函数(此代码改编自objective-c OWASP示例): (你可以找到一个) 然后使用openssl为您的网站创建.der文件 openssl s_client -connect my-https-website.com:443 -showc

如何在Swift中将证书固定添加到NSURLSession


仅包含Objective-C和NSURLConnection的示例。

Swift 3+更新:

只需为
nsurlsessionelegate
定义一个委托类并实现didReceiveChallenge函数(此代码改编自objective-c OWASP示例):

(你可以找到一个)

然后使用openssl为您的网站创建
.der
文件

openssl s_client -connect my-https-website.com:443 -showcerts < /dev/null | openssl x509 -outform DER > my-https-website.der

@lifeisfoo答案中的
openssl
命令将在OS X中为某些使用较新密码(如ECDSA)的SSL证书提供一个错误

如果在@lifeisfoo的答案中运行
openssl
命令时出现以下错误:

    write:errno=54
    unable to load certificate
    1769:error:0906D06C:PEM routines:PEM_read_bio:no start
    line:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL09        
    8-59.60.1/src/crypto/pem/pem_lib.c:648:Expecting: TRUSTED CERTIFICATE
您的网站的SSL证书可能使用的算法在OS X的默认
openssl
版本(v0.9.X,其中不支持ECDSA)中不受支持

这是解决方法:

要获得正确的
.der
文件,您必须首先
brew安装openssl
,然后将@lifeisfoo答案中的
openssl
命令替换为:

/usr/local/ceral/openssl/1.0.2h_1/bin/openssl[上述命令的其余部分]

自制安装命令:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

希望能有所帮助。

这里是Swift 3的更新版本

import Foundation
import Security

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        // Adapted from OWASP https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#iOS

        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let serverTrust = challenge.protectionSpace.serverTrust {
                var secresult = SecTrustResultType.invalid
                let status = SecTrustEvaluate(serverTrust, &secresult)

                if(errSecSuccess == status) {
                    if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
                        let serverCertificateData = SecCertificateCopyData(serverCertificate)
                        let data = CFDataGetBytePtr(serverCertificateData);
                        let size = CFDataGetLength(serverCertificateData);
                        let cert1 = NSData(bytes: data, length: size)
                        let file_der = Bundle.main.path(forResource: "name-of-cert-file", ofType: "cer")

                        if let file = file_der {
                            if let cert2 = NSData(contentsOfFile: file) {
                                if cert1.isEqual(to: cert2 as Data) {
                                    completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:serverTrust))
                                    return
                                }
                            }
                        }
                    }
                }
            }
        }

        // Pinning failed
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }

}

多亏了这个网站上的例子:我构建了一个版本,它锁定了公钥,而不是整个证书(如果定期更新证书,会更方便)

更新:删除了强制展开并替换了分区

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<CFError>?
            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();
}

}
<代码>导入基础 导入通用加密 类SessionDelegate:NSObject,URLSessionDelegate{ 专用静态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 ]; 私有静态let google_com_pubkey=[“4xvxzbeegwdboyogojlkcwgm7hyqougf4l+9um5oPOI=“”]; 私有静态让google_com_full=[“KjLxfxajzmBH0fTH1/oujb6R5fqBiLxl0zrl2xyFT2E=“”]; func urlSession(usession:urlSession, didReceive质询:URLAuthenticationChallenge, completionHandler:@escaping(URLSession.AuthChallengeDisposition,URLCredential?->Void){ guard let serverTrust=challenge.protectionSpace.serverTrust else{ completionHandler(.cancelAuthenticationChallenge,nil); 返回; } //为域名检查设置SSL策略 let policies=NSMutableArray(); 添加(SecPolicyCreateSSL(true,(challenge.protectionSpace.host作为CFString)); SectrustSetPolicys(服务器信任,策略); var isServerTrusted=SecTrustEvaluateWithError(serverTrust,nil); if(isServerTrusted&&challenge.protectionSpace.host==“www.google.com”){ 让certificate=SecTrustGetCertificateAtIndex(serverTrust,0); //比较公钥 如果可用(iOS 10.0,*){ 让policy=SecPolicyCreateBasicX509(); 让cfCertificates=[certificate]作为CFArray; var信托:宗派信任? 具有证书的部门(CFC证书、政策和信托); 守卫信任!=nil,让pubKey=SecTrustCopyPublicKey(信任!)否则{ completionHandler(.cancelAuthenticationChallenge,nil); 返回; } var错误:非托管? 如果让pubKeyData=SecKeyCopyExternalRepresentation(pubKey,&错误){ var keyWithHeader=数据(字节:sessionlegate.rsa2048Asn1Header); 追加(pubKeyData作为数据); 设sha256Key=sha256(keyWithHeader); 如果(!SessionDelegate.google_com_pubkey.contains(sha256Key)){ isServerTrusted=false; } }否则{ isServerTrusted=false; } }else{//比较完整证书 让remoteCertificateData=SecCertificateCopyData(证书!)作为数据; 设sha256Data=SH256(remoteCertificateData); 如果(!SessionDelegate.google_com_full.contains(sha256Data)){ isServerTrusted=false; } } } 如果(isServerTrusted){ let credential=URLCredential(信任:serverTrust); completionHandler(.useCredential,credential); }否则{ completionHandler(.cancelAuthenticationChallenge,nil); } } func sha256(u数据:数据)->字符串{ var hash=[UInt8](重复:0,计数:Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes{ _=CC_SHA256($0,CC_LONG(data.count)和散列) } 返回数据(字节:散列).base64EncodedString(); } } 将网站的证书(作为.cer文件)保存在主捆绑包中。然后使用URLSessionLegate方法:

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard
        challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
        let serverTrust = challenge.protectionSpace.serverTrust,
        SecTrustEvaluate(serverTrust, nil) == errSecSuccess,
        let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 0) else {

            reject(with: completionHandler)
            return
    }

    let serverCertData = SecCertificateCopyData(serverCert) as Data

    guard
        let localCertPath = Bundle.main.path(forResource: "shop.rewe.de", ofType: "cer"),
        let localCertData = NSData(contentsOfFile: localCertPath) as Data?,

        localCertData == serverCertData else {

            reject(with: completionHandler)
            return
    }

    accept(with: serverTrust, completionHandler)

}


请注意,
SecTrustEvaluate
已被弃用,应替换为
SecTrustEvaluateWithError

因此:

var secresult=SecTrustResultType.invalid
让status=SecTrustEvaluate(serverTrust和secresult)
如果errSecSuccess==状态{
//进行评估
切换结果{
case.unspecified,.procedure:返回true
默认值:返回false
}
}
我之所以编写
//继续求值
部分,是因为您应该验证
secresult
,这可能意味着证书实际上无效。您可以选择覆盖此选项,并将任何提出的问题添加为例外,最好是在提示用户做出决定之后

应该是这样的:

如果扇区错误(服务器,无){
//证书有效,请继续。
}
第二个参数将捕获任何错误,但如果您对细节不感兴趣,可以传递
ni
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<CFError>?
            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();
}

}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard
        challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
        let serverTrust = challenge.protectionSpace.serverTrust,
        SecTrustEvaluate(serverTrust, nil) == errSecSuccess,
        let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 0) else {

            reject(with: completionHandler)
            return
    }

    let serverCertData = SecCertificateCopyData(serverCert) as Data

    guard
        let localCertPath = Bundle.main.path(forResource: "shop.rewe.de", ofType: "cer"),
        let localCertData = NSData(contentsOfFile: localCertPath) as Data?,

        localCertData == serverCertData else {

            reject(with: completionHandler)
            return
    }

    accept(with: serverTrust, completionHandler)

}
func reject(with completionHandler: ((URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) {
    completionHandler(.cancelAuthenticationChallenge, nil)
}

func accept(with serverTrust: SecTrust, _ completionHandler: ((URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) {
    completionHandler(.useCredential, URLCredential(trust: serverTrust))
}
import Foundation
import Security

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

      let certFileName = "name-of-cert-file"
      let certFileType = "cer"

      func urlSession(_ session: URLSession, 
                  didReceive challenge: URLAuthenticationChallenge, 
                  completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
        if let serverTrust = challenge.protectionSpace.serverTrust {
            var secresult = SecTrustResultType.invalid
            let status = SecTrustEvaluate(serverTrust, &secresult)

            if(errSecSuccess == status) {
                if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
                    let serverCertificateData = SecCertificateCopyData(serverCertificate)
                    let data = CFDataGetBytePtr(serverCertificateData);
                    let size = CFDataGetLength(serverCertificateData);
                    let certificateOne = NSData(bytes: data, length: size)
                    let filePath = Bundle.main.path(forResource: self.certFileName, 
                                                         ofType: self.certFileType)

                    if let file = filePath {
                        if let certificateTwo = NSData(contentsOfFile: file) {
                            if certificateOne.isEqual(to: certificateTwo as Data) {
                                completionHandler(URLSession.AuthChallengeDisposition.useCredential, 
                                                  URLCredential(trust:serverTrust))
                                return
                            }
                        }
                    }
                }
            }
        }
    }

    // Pinning failed
    completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}
}