Search code examples
iosswiftsslcertificate

Certificate Pinning Error: Extended key usage does not match certificate usage


I used the "keytool -keyalg RSA -keysize 2048 -storetype PKCS12" command to generate a p12 format certificate, then I installed it on my Mac and exported a cer certificate.

But an error occurred when I request the server.

iOS: 11.4, Swift: 5, Alamofire: 5.2

error: Extended key usage does not match certificate usage

serverTrustEvaluationFailed(reason: Alamofire.AFError.ServerTrustFailureReason.trustEvaluationFailed(error: Optional(Error Domain=NSOSStatusErrorDomain Code=-67609 "“***” certificate is not permitted for this usage" UserInfo={NSLocalizedDescription=“***” certificate is not permitted for this usage, NSUnderlyingError=0x6000006233c0 {Error Domain=NSOSStatusErrorDomain Code=-67609 "Certificate 0 “***” has errors: Extended key usage does not match certificate usage;" UserInfo={NSLocalizedDescription=Certificate 0 “***” has errors: Extended key usage does not match certificate usage;}}})))
error:Certificate Pinning Error

Server configuration:

server:
  port: ***
  servlet:
    context-path: /fik
  ssl:
    key-store: classpath:ssl/fik-server-730.p12
    key-store-password: ****
    key-store-type: PKCS12
    key-alias: fik-cer

Swift code:

final class AlamofireClient {
    let evaluators = ["47.*.*.*": PinnedCertificatesTrustEvaluator(certificates: [Certificates.stackExchange], acceptSelfSignedCertificates: true)]
    
    let session: Session
    
    private init() {
        session = Session(serverTrustManager: ServerTrustManager(evaluators: evaluators))
    }
    
    private static let shared = AlamofireClient()
    
    static func doPost(api: String, parameters: [String: String]) -> String {
        var dict = parameters
        let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
        dict["timestamp"] = "\(timestamp)"
        let sign = self.getSign(dict: dict)
        
        let url = CHConstants.ApiGateway + api
        let headers: HTTPHeaders = [
            HTTPHeader(name: "Content-Type", value: "application/json"),
            HTTPHeader(name: "signature", value: sign),
            HTTPHeader(name: "time", value: "\(timestamp)")
        ]
        
        shared.session.request(url, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, headers: headers).responseJSON { response in
            switch response.result {
            case .success:
                print("value:\(response.value)")
                print("data:\(response.data)")
            case let .failure(error):
                let isServerTrustEvaluationError = error.asAFError?.isServerTrustEvaluationError ?? false
                let message: String
                if isServerTrustEvaluationError {
                    message = "Certificate Pinning Error"
                } else {
                    message = error.localizedDescription
                }
                print(error)
                print("error:\(message)")
            }
        }
        return "ok"
    }
    
    private static func getSign(dict: [String: String]) -> String {
        ...
        
        return sign
    }
}

struct Certificates {
    static let stackExchange = Certificates.certificate(filename: "fik-server-730", type: "cer")
    
    private static func certificate(filename: String, type: String) -> SecCertificate {
        let filePath = Bundle.main.path(forResource: filename, ofType: type)!
        let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
        let certificate = SecCertificateCreateWithData(nil, data as CFData)!
        
        return certificate
    }
}

Solution

  • Looking at the documentation you need to set the extension for ExtendedkeyUsage to serverAuth when generating the certificate:

    keytool -keyalg RSA -keysize 2048 -storetype PKCS12 -ext EKU=serverAuth