I'm developing an application that should be able to connect to IoT devices in the local network. I want to use https with a self-signed certificate. As far as I understand it's against AST, but I could request an exception.
There are 2 things that I would need to change to make it work:
Trust my self-signed Root CA - I'm planning to use it to issue certificate on each device with subjectAlternativeName = DNS:<deviceId>.device.mydomain.com that is not real DNS name.
Alter logic to verify host - those devices don't have public DNS addresses and the application communicates with IoT devices using address in local network like 192.168.0.14. Because my application knows the exact id of the device (each IoT device is connected to the user account) if a server responds with a certificate issued by my Root CA I would check that <deviceId> in the certificate matches with the IoT device user is trying to connect to instead of checking hostname.
Alternatively, aside from using plaintext http, I was thinking about giving my IoT devices certificates issued by trusted CA, like Let's Encrypt, and setting up DNS that would actually resolve <deviceId>.device.mydomain.com to its current address on local network, but there are 2 issues with that approach that I would like to avoid:
Let's Encrypt has a quota of 50 certificates per week and I assume other Public CAs would have similar restrictions
My backend server might not be aware of the actual local address if there is no connectivity between IoT device and backend server, whereas application can find it on the local network.
Code Block #import <React/RCTBridgeModule.h> #import <React/RCTHTTPRequestHandler.h> #import <React/RCTLog.h> #import "RCTActiveDevice.h" @implementation RCTHTTPRequestHandler(iotTls) - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSString *deviceId = [RCTActiveDevice getCurrentDeviceId]; if (deviceId && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; SecCertificateRef issuerRootCa = SecTrustGetCertificateAtIndex(serverTrust, SecTrustGetCertificateCount(serverTrust) - 1); NSData *issuerRootCaData = CFBridgingRelease(SecCertificateCopyData(issuerRootCa)); NSString *deviceRootCaPath = [[NSBundle mainBundle] pathForResource:@"device_root_ca" ofType:@"crt"]; NSData *deviceRootCaData = [NSData dataWithContentsOfFile:deviceRootCaPath]; SecCertificateRef deviceRootCa = SecCertificateCreateWithData(NULL, (bridge CFDataRef) deviceRootCaData); bool isDeviceCertificate = [deviceRootCaData isEqualToData:issuerRootCaData]; if (isDeviceCertificate) { NSString *domain = [NSString stringWithFormat:@"%@.device.mydomain.com", deviceId]; SecPolicyRef policy = SecPolicyCreateSSL(false, (bridge CFStringRef) domain); SecTrustSetPolicies(serverTrust, (bridge CFArrayRef) [NSArray arrayWithObjects: (bridge id _Nonnull) (policy), nil]); SecTrustSetAnchorCertificates(serverTrust, (bridge CFArrayRef) [NSArray arrayWithObjects: (bridge id _Nonnull) (deviceRootCa), nil]); bool isDeviceTrusted; if (@available(iOS 12.0, *)) { isDeviceTrusted = SecTrustEvaluateWithError(serverTrust, nil); } else { SecTrustResultType result; SecTrustEvaluate(serverTrust, &result); isDeviceTrusted = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); } if (isDeviceTrusted) { NSURLCredential *credential = [NSURLCredential credentialForTrust: serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); return; } completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } } completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } @end
My question is this approach good and would I be able to get approval for my application to be included in the App Store?