Skip to content

Commit 7ac91e8

Browse files
bitgammaflexsurfer
andcommitted
get images over HTTPS with self-signed certificate
Co-Authored-By: andrey <motor4ik@gmail.com>
1 parent 9850dd5 commit 7ac91e8

File tree

17 files changed

+437
-270
lines changed

17 files changed

+437
-270
lines changed

android/app/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ dependencies {
358358
implementation 'com.github.status-im:function:0.0.1'
359359
implementation 'com.facebook.fresco:fresco:2.2.0'
360360
implementation 'com.facebook.fresco:animated-gif:2.2.0'
361+
implementation "com.squareup.okhttp3:okhttp-tls:3.12.12"
361362
}
362363

363364
def getLocalNDKDir = { ->

android/app/src/main/java/im/status/ethereum/MainApplication.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
import com.reactnativenavigation.react.NavigationReactNativeHost;
1616
import com.facebook.react.ReactPackage;
1717
import com.facebook.react.ReactInstanceManager;
18+
import com.facebook.react.modules.network.OkHttpClientProvider;
1819

1920
import java.util.List;
2021

2122
import im.status.ethereum.keycard.RNStatusKeycardPackage;
2223
import im.status.ethereum.module.StatusPackage;
2324
import im.status.ethereum.pushnotifications.PushNotificationPackage;
25+
import im.status.ethereum.StatusOkHttpClientFactory;
2426

2527
public class MainApplication extends NavigationApplication {
2628

@@ -56,7 +58,9 @@ public ReactNativeHost getReactNativeHost() {
5658
@Override
5759
public void onCreate() {
5860
super.onCreate();
59-
61+
62+
OkHttpClientProvider.setOkHttpClientFactory(new StatusOkHttpClientFactory());
63+
6064
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG_WEBVIEW == "1");
6165
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
6266
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package im.status.ethereum;
2+
3+
import android.util.Log;
4+
5+
import com.facebook.react.modules.network.OkHttpClientFactory;
6+
import com.facebook.react.modules.network.OkHttpClientProvider;
7+
8+
import okhttp3.OkHttpClient;
9+
import okhttp3.Interceptor;
10+
import okhttp3.tls.HandshakeCertificates;
11+
12+
import java.io.ByteArrayInputStream;
13+
import java.io.InputStream;
14+
import java.lang.RuntimeException;
15+
import java.lang.reflect.Field;
16+
import java.lang.reflect.Method;
17+
import java.security.cert.CertificateFactory;
18+
import java.security.cert.X509Certificate;
19+
20+
import im.status.ethereum.module.StatusPackage;
21+
22+
class StatusOkHttpClientFactory implements OkHttpClientFactory {
23+
public OkHttpClient createNewNetworkModuleClient() {
24+
String certPem = StatusPackage.getImageTLSCert();
25+
X509Certificate cert;
26+
27+
try {
28+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
29+
cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certPem.getBytes()));
30+
} catch(Exception e) {
31+
Log.e("StatusOkHttpClientFactory", "Could not parse certificate");
32+
cert = null;
33+
}
34+
35+
HandshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
36+
.addPlatformTrustedCertificates()
37+
.addTrustedCertificate(cert)
38+
.build();
39+
40+
return OkHttpClientProvider.createClientBuilder()
41+
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
42+
.build();
43+
}
44+
}

ios/StatusIm/AppDelegate.m

+72-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,23 @@
1919

2020
#import <React/RCTBridge.h>
2121
#import <React/RCTBundleURLProvider.h>
22+
#import <React/RCTHTTPRequestHandler.h>
2223

2324
#import <UserNotifications/UserNotifications.h>
2425
#import <RNCPushNotificationIOS.h>
2526

27+
#import <SDWebImage/SDWebImageDownloaderConfig.h>
28+
#import <SDWebImage/SDWebImageDownloaderOperation.h>
29+
30+
#import <Security/Security.h>
31+
32+
//TODO: properly import the framework
33+
extern NSString* StatusgoImageServerTLSCert();
34+
35+
@interface StatusDownloaderOperation : SDWebImageDownloaderOperation
36+
+ (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
37+
@end
38+
2639
/*
2740
#if DEBUG
2841
#import <FlipperKit/FlipperClient.h>
@@ -45,7 +58,7 @@ static void InitializeFlipper(UIApplication *application) {
4558
#endif
4659
*/
4760

48-
@implementation AppDelegate
61+
@implementation AppDelegate
4962
{
5063
UIView *_blankView;
5164
}
@@ -84,6 +97,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
8497
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
8598
center.delegate = self;
8699

100+
SDWebImageDownloaderConfig.defaultDownloaderConfig.operationClass = [StatusDownloaderOperation class];
101+
87102
return YES;
88103
}
89104

@@ -124,7 +139,7 @@ - (void)applicationWillResignActive:(UIApplication *)application {
124139
[UIView animateWithDuration:0.5 animations:^{
125140
_blankView.alpha = 1;
126141
}];
127-
}
142+
}
128143
}
129144

130145
- (void)applicationDidBecomeActive:(UIApplication *)application {
@@ -169,14 +184,67 @@ - (void)application:(UIApplication *)application didReceiveLocalNotification:(UI
169184
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
170185
{
171186
NSDictionary *userInfo = notification.request.content.userInfo;
172-
187+
173188
NSString *notificationType = userInfo[@"notificationType"]; // check your notification type
174189
if (![notificationType isEqual: @"local-notification"]) { // we silence all notifications which are not local
175190
completionHandler(UNNotificationPresentationOptionNone);
176191
return;
177192
}
178-
193+
179194
completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
180195
}
181196

182197
@end
198+
199+
@implementation StatusDownloaderOperation
200+
201+
+ (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
202+
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
203+
__block NSURLCredential *credential = nil;
204+
205+
NSString *pemCert = StatusgoImageServerTLSCert();
206+
pemCert = [pemCert stringByReplacingOccurrencesOfString:@"-----BEGIN CERTIFICATE-----\n" withString:@""];
207+
pemCert = [pemCert stringByReplacingOccurrencesOfString:@"\n-----END CERTIFICATE-----" withString:@""];
208+
NSData *derCert = [[NSData alloc] initWithBase64EncodedString:pemCert options:NSDataBase64DecodingIgnoreUnknownCharacters];
209+
SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (__bridge_retained CFDataRef) derCert);
210+
CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&certRef, 1, NULL);
211+
SecTrustSetAnchorCertificates(challenge.protectionSpace.serverTrust, certArrayRef);
212+
213+
SecTrustResultType trustResult;
214+
SecTrustEvaluate(challenge.protectionSpace.serverTrust, &trustResult);
215+
216+
if ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified)) {
217+
disposition = NSURLSessionAuthChallengeUseCredential;
218+
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
219+
}
220+
221+
if (completionHandler) {
222+
completionHandler(disposition, credential);
223+
}
224+
}
225+
226+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
227+
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] &&
228+
[challenge.protectionSpace.host isEqualToString:@"localhost"]) {
229+
[StatusDownloaderOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
230+
} else {
231+
[super URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
232+
}
233+
}
234+
235+
@end
236+
237+
@implementation RCTHTTPRequestHandler (SelfSigned)
238+
239+
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
240+
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] &&
241+
[challenge.protectionSpace.host isEqualToString:@"localhost"]) {
242+
[StatusDownloaderOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
243+
} else {
244+
if (completionHandler) {
245+
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
246+
}
247+
}
248+
}
249+
250+
@end

modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java

+6
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@
1010
import java.util.Collections;
1111
import java.util.List;
1212

13+
import statusgo.Statusgo;
14+
1315
public class StatusPackage implements ReactPackage {
1416

1517
private boolean rootedDevice;
1618

19+
public static String getImageTLSCert() {
20+
return Statusgo.imageServerTLSCert();
21+
}
22+
1723
public StatusPackage(boolean rootedDevice) {
1824
this.rootedDevice = rootedDevice;
1925
}

0 commit comments

Comments
 (0)