I have developed an app that connects to different pages and allows validation using a digital certificate. In most of the webs I can access without problem, but when I try to connect to the url: https://www.sedecatastro.***.es/Accesos/SECAccTitular.aspx?Dest=24, I always get the error: Error Domain = NSURLErrorDomain Code = -1206 The server “xxxx” requires a client certificate. I do not understand the error because I am doing the validation in other websites correctly and even in other sections of this same website.
class SessionDelegate:NSObject, URLSessionDelegate
{
let certificadosName: [String] = ["AC_Administracion_Publica","ac_raiz_fnmt","Camerfirma_AAPP_II_Chambers_of_Commerce_Root","Camerfirma_Corporate_Server_II_Chambers_of_Commerce_Root","Chambers_of_Commerce_Root","claveRaiz","DigiCert_High_Assurance_EV_Root_CA","Entrust_Root_Certification_Authority_G","GeoTrust_SSL_CA_G_GeoTrust_Global_CA","Izenpe_com"
,"GlobalSign", "GlobalSign_Extended_Validation", "USERTrust_RSA", "SEDE_CATASTRO"]
let certFileType = "cer"
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
&& challenge.protectionSpace.serverTrust != nil
{
let trust = challenge.protectionSpace.serverTrust
var certs: [SecCertificate] = [SecCertificate]()
for certificadoName in certificadosName
{
let pem = Bundle.main.url(forResource: certificadoName, withExtension: certFileType)
if (pem != nil)
{
let data = NSData(contentsOf: pem!)
let cert = SecCertificateCreateWithData(nil, data!)
certs.append(cert!)
}
}
SecTrustSetAnchorCertificates(trust!, certs as CFArray)
var result=SecTrustResultType.invalid
if SecTrustEvaluate(trust!,&result)==errSecSuccess {
if result==SecTrustResultType.proceed || result==SecTrustResultType.unspecified {
let proposedCredential = URLCredential(trust: trust!)
completionHandler(.useCredential,proposedCredential)
return
}
}
completionHandler(.performDefaultHandling, nil)
}
else if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate)
{
sendClientCertificate(for: challenge, via: completionHandler)
}
else
{
completionHandler(.performDefaultHandling, nil)
}
}
func sendClientCertificate(for challenge: URLAuthenticationChallenge, via completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let certificado: String? = Tramite.selectedCertificado
let password: String? = Tramite.passwordCertificado
if(certificado != nil && password != nil )
{
guard let data = try? Data(contentsOf: URL(fileURLWithPath: certificado!)),
let credential = credential(from: data, withPassword: password ?? "") else {
challenge.sender?.cancel(challenge)
return completionHandler(.rejectProtectionSpace, .none)
}
return completionHandler(.useCredential, credential);
}
else{
return completionHandler(.rejectProtectionSpace, .none)
}
}
func credential(from data: Data, withPassword password: String) -> URLCredential? {
guard let security = security(from: data, withPassword: password) else {
return .none
}
return URLCredential(
identity: security.identity,
certificates: security.certificates,
persistence: .forSession
)
}
func security(from data: Data, withPassword password: String) -> (identity: SecIdentity, trust: SecTrust, certificates: [SecCertificate])? {
var _items: CFArray?
let securityError = SecPKCS12Import(data as NSData,
[ kSecImportExportPassphrase as String : password ] as CFDictionary,
&_items);
guard let items = _items as? [Any],
let dict = items.first as? [String:Any],
securityError == errSecSuccess else {
return .none
}
let identity = dict["identity"] as! SecIdentity
let trust = dict["trust"] as! SecTrust;
// Certificate chain
var certificate: SecCertificate!
SecIdentityCopyCertificate(identity, &certificate);
return (identity, trust, [certificate]);
}
}