1

I built an MITM Go proxy to intercept all requests. I’m also developing an enterprise Flutter iOS app that uses a custom in-app trust store (so it doesn’t rely on the OS trust store) and I’ve stored my .pem certificate in the app’s assets. Certificate validation works for internal office portals using the in-app trust store, but when I try to open public portals like Microsoft Outlook, my proxy shows a TLS error: cannot do handshake: EOF (HTTP 403). How can I resolve this?

class WebviewProxyConfig {
  static const MethodChannel _channel = MethodChannel('webview_proxy_config');
  
  static final WebviewProxyConfig _instance = WebviewProxyConfig._internal();
  static WebviewProxyConfig get instance => _instance;
  WebviewProxyConfig._internal();
  
  Future<bool> webivewProxyConfig(String proxyHost, String proxyPort, 
      String endpoint, String userIdHash, String tenantIdHash, String sessionId) async {
    try {
      if (Platform.isIOS) {
        final isTrusted = await CertificateManager.instance.checkCertificateTrustStatus();
        if (!isTrusted) {
          debugPrint("Certificate not trusted. Proxy configuration may not work correctly.");
        }
      }
      
      await _channel.invokeMethod('setWebivewProxyConfig', <String, dynamic>{
        'proxy_host': proxyHost,
        'proxy_port': proxyPort,
        'endpoint': endpoint,
        'userIdHash': userIdHash,
        'tenantIdHash': tenantIdHash,
        'sessionId': sessionId,
        'use_certificate': true,
      });
      
      return true;
    } on PlatformException catch(e) {
      debugPrint("Failed to start proxy: $e");
      return false;
    }
  }
}
// WebViewProxyConfig.swift
import UIKit
import Network
import WebKit

class WebViewProxyConfig {
    static let shared = WebViewProxyConfig()
    
    public func setWebivewProxyConfig(
        proxyHost: String, 
        proxyPort: String,
        endpoint: String,
        userIdHash: String,
        tenantIdHash: String,
        sessionID: String,
        useCertificate: Bool = false
    ) {
        if #available(iOS 17.0, *) {
            let host = NWEndpoint.Host(proxyHost)
            guard let portValue = UInt16(proxyPort) else {
                print("Invalid port number")
                return
            }
            let port = NWEndpoint.Port(integerLiteral: portValue)
            
            let proxyConfig = ProxyConfiguration(httpCONNECTProxy: NWEndpoint.hostPort(host: host, port: port))
            let dataStore = WKWebsiteDataStore.default()
            dataStore.proxyConfigurations = [proxyConfig]
            
            if useCertificate {
                print("WKWebView proxy configured with certificate validation")
            }
        }
    }
}
// CertificateManagerPlugin.swift - URLSessionDelegate implementation
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, 
                      completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    
    guard let serverTrust = challenge.protectionSpace.serverTrust,
          let customCA = customCA else {
        completionHandler(.performDefaultHandling, nil)
        return
    }
    
    let anchorCertificates = [customCA] as CFArray
    SecTrustSetAnchorCertificates(serverTrust, anchorCertificates)
    
    var error: CFError?
    let isTrusted = SecTrustEvaluateWithError(serverTrust, &error)
    
    if isTrusted {
        completionHandler(.useCredential, URLCredential(trust: serverTrust))
    } else {
        print("Custom Native Validation Failed for \(challenge.protectionSpace.host)")
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}

All the steps and code I mentioned describe my approach.

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.