How to Use a Self-Signed Certificate with an IoT Device and a React Native App


Title: TLS Check Fails on iPhone for React Native Expo App

Body:

Hello Stack Overflow Community,

I am developing a React Native Expo app that needs to configure a device with WiFi. The app works fine on Android after implementing the following network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:tools="http://schemas.android.com/tools">
    <base-config>
        <trust-anchors>
            <certificates src="user" />
            <certificates src="system" />
        </trust-anchors>
    </base-config>
    <domain-config>
        <domain includeSubdomains="true">192.168.x.x</domain>
        <trust-anchors>
            <certificates src="@raw/mycert"
                tools:ignore="NetworkSecurityConfig" />
        </trust-anchors>
    </domain-config>
</network-security-config>

However, I am facing issues with iOS. I have tried using the react-native-ssl-pinning library both with and without my cert.pem file, but the TLS check always fails.

Additionally, I attempted to write Swift code to bypass the SSL check and use the certificate, but it also fails.

Here is the error message I received using openssl:

SSL handshake has read 601 bytes and written 465 bytes
Verification error: EE certificate key too weak

The certificate is 512 bits, which seems to be weak.

Questions:

  1. Is the weak certificate the primary reason for the TLS failure on iOS?
  2. How can I use this certificate or bypass this issue temporarily while developing?
  3. Are there any best practices for handling such situations in React Native Expo for iOS?

Thank you in advance for any suggestions or guidance!



iOS Network Configuration Issue: Certificate Invalid Error [-1202]

Application requires making network requests to a local server (192.168.x.x). I have configured my Info.plist to allow for insecure HTTP loads and added an exception domain for the local IP. Here is the configuration:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>192.168.x.x</key>
    <dict>
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
      <string>TLSv1.2</string>
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <key>NSExceptionRequiresForwardSecrecy</key>
      <false/>
      <key>NSIncludesSubdomains</key>
      <true/>
    </dict>
  </dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
  <key>NSAllowsLocalNetworking</key>
  <true/>
</dict>

Swift Code Using Alamofire

I tried using Alamofire to make requests and have bypassed SSL checks with a custom certificate ServerTrustEvaluator:

import Foundation
import Alamofire

class DisabledTrustEvaluator: ServerTrustEvaluating {
    func evaluate(_ trust: SecTrust, forHost host: String) throws {
        // Bypass all trust checks
        print("SSL trust evaluation bypassed for host: (host)")
    }
}

@objc(ConfigureMonidrop)
class ConfigureMonidrop: NSObject {
    private var session: Session!

    @objc static func requiresMainQueueSetup() -> Bool {
        return false
    }

    override init() {
        super.init()
        // Configure Alamofire with a custom ServerTrustManager to bypass SSL checks
        let serverTrustManager = ServerTrustManager(evaluators: [
            "192.168.x.x": DisabledTrustEvaluator()
        ])
        session = Session(serverTrustManager: serverTrustManager)
    }

    @objc(makeRequest:withResolver:withRejecter:)
    func makeRequest(urlString: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
        guard let url = URL(string: urlString) else {
            reject("INVALID_URL", "Invalid URL", NSError(domain: "", code: -1, userInfo: nil))
            return
        }

        session.request(urlString).validate().responseString { response in
            switch response.result {
            case .success(let responseData):
                resolve(["ok": true, "status": 200, "data": responseData])
            case .failure(let error):
                print("Request failed with error: (error)")
                if let data = response.data, let responseString = String(data: data, encoding: .utf8) {
                    print("Response data: (responseString)")
                }
                if let httpResponse = response.response {
                    resolve(["ok": false, "status": httpResponse.statusCode, "error": error.localizedDescription])
                } else {
                    reject("REQUEST_FAILED", "Request failed: (error.localizedDescription)", error as NSError)
                }
            }
        }
    }

    @objc
    static func moduleName() -> String! {
        return "ConfigureMonidrop"
    }
}

Swift Code Using URLSession and Certificate Pinning

Alternatively, I attempted certificate pinning with the following approach:

// MySessionDelegate.swift
import Foundation

class MySessionDelegate: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if let serverTrust = challenge.protectionSpace.serverTrust {
            let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)

            // Load the local certificate
            if let pathToCert = Bundle.main.path(forResource: "mycert", ofType: "pem"),
               let localCertificateData = NSData(contentsOfFile: pathToCert) {

                let certificateData = SecCertificateCopyData(certificate!) as Data

                // Check if the server's certificate matches the local one
                if certificateData == localCertificateData as Data {
                    completionHandler(.useCredential, URLCredential(trust: serverTrust))
                    return
                }
            }
        }

        // The server's certificate didn't match the local one
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}

// RNCertificatePinning.swift
import Foundation

@objc(RNCertificatePinning)
class RNCertificatePinning: NSObject {
    @objc static func requiresMainQueueSetup() -> Bool {
        return false
    }

    @objc(makeRequest:withResolver:withRejecter:)
    func makeRequest(urlString: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
        guard let url = URL(string: urlString) else {
            reject("INVALID_URL", "Invalid URL", nil)
            return
        }

        let session = URLSession(configuration: .default, delegate: MySessionDelegate(), delegateQueue: nil)
        let task = session.dataTask(with: url) { (data, response, error) in
            if let error = error {
                reject("REQUEST_FAILED", "Request failed", error)
            } else if let data = data {
                resolve(String(data: data, encoding: .utf8))
            }
        }
        task.resume()
    }
    
    @objc
    static func moduleName() -> String! {
        return "RNCertificatePinning"
    }

    @objc
    static func requiresMainQueueSetup() -> Bool {
        return false
    }
}

React Native Code

Here’s my React Native code using react-native-ssl-pinning:

import { fetch } from "react-native-ssl-pinning";

export const iosConfigureMonidrop = async (SSID, PASSWORD, TOKEN) => {
  const BASE_URL = "https://192.168.x.x/init";
  const URL = "httpsxxxdata";

  const urlString = `${BASE_URL}?ssid=${encodeURIComponent(SSID)}&pwd=${encodeURIComponent(PASSWORD)}&token=${encodeURIComponent(TOKEN)}&url=${URL}`;

  try {
    const response = await fetch(urlString, {
      method: "GET",
      timeoutInterval: 3000,
      disableAllSecurity: true,
      sslPinning: {
        certs: ["deviceDrive"]
      },
      headers: {
        Accept: "application/json; charset=utf-8",
        "Access-Control-Allow-Origin": "*",
        e_platform: "mobile",
      },
    });

    if (response.status < 200 || response.status >= 300) {
      console.error(`HTTP error! status: ${response.status}`);
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error during fetch with SSL pinning", error);
    throw error;
  }
};

Error Encountered

All of these configurations result in the following error:

Task <6A32A4AE-D06D-4A40-9D82-52936CB0F377>.<7> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.4.1” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?

Request for Help

How can I resolve this certificate issue and ensure my app can securely communicate with the local server? Is there a better approach to handle SSL pinning or trust evaluation in this context? React Native solution could be best so I dont need manage native code at all.

Any help or guidance would be greatly appreciated. Thank you!


New contributor

Aki-Petteri Kuivas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật