如何在Swift中将证书钉扎到NSURLSession中?
OWASP网站只包含目标C和NSURLConnection的一个示例。
发布于 2015-12-11 12:12:41
Swift 3+更新:
只需为NSURLSessionDelegate定义一个委托类并实现didReceiveChallenge函数(此代码来自objective示例):
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 {
let isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil)
if(isServerTrusted) {
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: "certificateFile", ofType: "der")
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)
}
}(你可以找到一个斯威夫特2的要点-从最初的答案)
然后使用.der为您的网站创建openssl文件
openssl s_client -connect my-https-website.com:443 -showcerts < /dev/null | openssl x509 -outform DER > my-https-website.der并将其添加到xcode项目中。再次检查它是否存在于Build phases选项卡中,在Copy Bundle Resources列表中。否则,将其拖放到此列表中。
最后,在代码中使用它来发出URL请求:
if let url = NSURL(string: "https://my-https-website.com") {
let session = URLSession(
configuration: URLSessionConfiguration.ephemeral,
delegate: NSURLSessionPinningDelegate(),
delegateQueue: nil)
let task = session.dataTask(with: url as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print("error: \(error!.localizedDescription): \(error!)")
} else if data != nil {
if let str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) {
print("Received data:\n\(str)")
} else {
print("Unable to convert data to text")
}
}
})
task.resume()
} else {
print("Unable to create NSURL")
}发布于 2017-09-29 15:54:14
感谢在这个站点中找到的示例:https://www.bugsee.com/blog/ssl-certificate-pinning-in-mobile-applications/,我构建了一个版本,它可以固定公钥,而不是整个证书(如果您定期更新证书,会更方便)。
更新:删除了强制展开并替换了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<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();
}
}发布于 2016-12-02 07:00:33
以下是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)
}
}https://stackoverflow.com/questions/34223291
复制相似问题