221 lines
9.2 KiB
Diff
221 lines
9.2 KiB
Diff
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
|
|
index ab869cf..2aa7a9e 100644
|
|
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
|
|
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
|
|
@@ -84,6 +84,12 @@ import java.util.Map;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
+import java.security.cert.X509Certificate;
|
|
+import java.security.PrivateKey;
|
|
+import android.webkit.ClientCertRequest;
|
|
+import android.os.AsyncTask;
|
|
+import android.security.KeyChain;
|
|
+
|
|
/**
|
|
* Manages instances of {@link WebView}
|
|
* <p>
|
|
@@ -140,6 +146,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
protected @Nullable String mUserAgent = null;
|
|
protected @Nullable String mUserAgentWithApplicationName = null;
|
|
|
|
+ private static String certificateAlias = null;
|
|
+
|
|
public RNCWebViewManager() {
|
|
mWebViewConfig = new WebViewConfig() {
|
|
public void configWebView(WebView webView) {
|
|
@@ -151,6 +159,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
mWebViewConfig = webViewConfig;
|
|
}
|
|
|
|
+ public static void setCertificateAlias(String alias) {
|
|
+ certificateAlias = alias;
|
|
+ }
|
|
+
|
|
protected static void dispatchEvent(WebView webView, Event event) {
|
|
ReactContext reactContext = (ReactContext) webView.getContext();
|
|
EventDispatcher eventDispatcher =
|
|
@@ -562,7 +574,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
@Override
|
|
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
|
|
// Do not register default touch emitter and let WebView implementation handle touches
|
|
- view.setWebViewClient(new RNCWebViewClient());
|
|
+ view.setWebViewClient(new RNCWebViewClient(reactContext));
|
|
}
|
|
|
|
@Override
|
|
@@ -742,12 +754,54 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
|
|
protected static class RNCWebViewClient extends WebViewClient {
|
|
|
|
+ protected ReactContext reactContext;
|
|
protected boolean mLastLoadFailed = false;
|
|
protected @Nullable
|
|
ReadableArray mUrlPrefixesForDefaultIntent;
|
|
protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
|
|
protected @Nullable String ignoreErrFailedForThisURL = null;
|
|
|
|
+ public RNCWebViewClient(ReactContext reactContext) {
|
|
+ this.reactContext = reactContext;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
|
|
+ class SslStuff {
|
|
+ PrivateKey privKey;
|
|
+ X509Certificate[] certChain;
|
|
+
|
|
+ public SslStuff(PrivateKey privKey, X509Certificate[] certChain) {
|
|
+ this.privKey = privKey;
|
|
+ this.certChain = certChain;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (certificateAlias != null) {
|
|
+ AsyncTask<Void, Void, SslStuff> task = new AsyncTask<Void, Void, SslStuff>() {
|
|
+ @Override
|
|
+ protected SslStuff doInBackground(Void... params) {
|
|
+ try {
|
|
+ PrivateKey privKey = KeyChain.getPrivateKey(reactContext, certificateAlias);
|
|
+ X509Certificate[] certChain = KeyChain.getCertificateChain(reactContext, certificateAlias);
|
|
+
|
|
+ return new SslStuff(privKey, certChain);
|
|
+ } catch (Exception e) {
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void onPostExecute(SslStuff sslStuff) {
|
|
+ if (sslStuff != null) {
|
|
+ request.proceed(sslStuff.privKey, sslStuff.certChain);
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ task.execute();
|
|
+ }
|
|
+ }
|
|
+
|
|
public void setIgnoreErrFailedForThisURL(@Nullable String url) {
|
|
ignoreErrFailedForThisURL = url;
|
|
}
|
|
diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m
|
|
index 02b4238..04bad05 100644
|
|
--- a/node_modules/react-native-webview/apple/RNCWebView.m
|
|
+++ b/node_modules/react-native-webview/apple/RNCWebView.m
|
|
@@ -17,6 +17,9 @@
|
|
|
|
#import "objc/runtime.h"
|
|
|
|
+#import "SecureStorage.h"
|
|
+#import <MMKV/MMKV.h>
|
|
+
|
|
static NSTimer *keyboardTimer;
|
|
static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
|
|
static NSString *const MessageHandlerName = @"ReactNativeWebView";
|
|
@@ -737,6 +740,68 @@ + (void)setCustomCertificatesForHost:(nullable NSDictionary*)certificates {
|
|
customCertificatesForHost = certificates;
|
|
}
|
|
|
|
+-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password
|
|
+{
|
|
+ NSString *authMethod = [[challenge protectionSpace] authenticationMethod];
|
|
+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
|
+
|
|
+ if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || path == nil || password == nil) {
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
+ } else if (path && password) {
|
|
+ NSMutableArray *policies = [NSMutableArray array];
|
|
+ [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
|
|
+ SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
|
|
+
|
|
+ SecTrustResultType result;
|
|
+ SecTrustEvaluate(serverTrust, &result);
|
|
+
|
|
+ if (![[NSFileManager defaultManager] fileExistsAtPath:path])
|
|
+ {
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
+ }
|
|
+
|
|
+ NSData *p12data = [NSData dataWithContentsOfFile:path];
|
|
+ NSDictionary* options = @{ (id)kSecImportExportPassphrase:password };
|
|
+ CFArrayRef rawItems = NULL;
|
|
+ OSStatus status = SecPKCS12Import((__bridge CFDataRef)p12data,
|
|
+ (__bridge CFDictionaryRef)options,
|
|
+ &rawItems);
|
|
+
|
|
+ if (status != noErr) {
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
+ }
|
|
+
|
|
+ NSArray* items = (NSArray*)CFBridgingRelease(rawItems);
|
|
+ NSDictionary* firstItem = nil;
|
|
+ if ((status == errSecSuccess) && ([items count]>0)) {
|
|
+ firstItem = items[0];
|
|
+ }
|
|
+
|
|
+ SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]);
|
|
+ SecCertificateRef certificate = NULL;
|
|
+ if (identity) {
|
|
+ SecIdentityCopyCertificate(identity, &certificate);
|
|
+ if (certificate) { CFRelease(certificate); }
|
|
+ }
|
|
+
|
|
+ NSMutableArray *certificates = [[NSMutableArray alloc] init];
|
|
+ [certificates addObject:CFBridgingRelease(certificate)];
|
|
+
|
|
+ return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone];
|
|
+ }
|
|
+
|
|
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
+}
|
|
+
|
|
+- (NSString *)stringToHex:(NSString *)string
|
|
+{
|
|
+ char *utf8 = (char *)[string UTF8String];
|
|
+ NSMutableString *hex = [NSMutableString string];
|
|
+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF];
|
|
+
|
|
+ return [[NSString stringWithFormat:@"%@", hex] lowercaseString];
|
|
+}
|
|
+
|
|
- (void) webView:(WKWebView *)webView
|
|
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
|
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable))completionHandler
|
|
@@ -746,7 +811,34 @@ - (void) webView:(WKWebView *)webView
|
|
host = webView.URL.host;
|
|
}
|
|
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
|
|
- completionHandler(NSURLSessionAuthChallengeUseCredential, clientAuthenticationCredential);
|
|
+ NSString *host = challenge.protectionSpace.host;
|
|
+
|
|
+ // Read the clientSSL info from MMKV
|
|
+ __block NSDictionary *clientSSL;
|
|
+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
|
|
+
|
|
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
|
|
+ [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"] callback:^(NSArray *response) {
|
|
+ // Error happened
|
|
+ if ([response objectAtIndex:0] != [NSNull null]) {
|
|
+ return;
|
|
+ }
|
|
+ NSString *key = [response objectAtIndex:1];
|
|
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
|
|
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
|
|
+
|
|
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
|
|
+ }];
|
|
+
|
|
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
+
|
|
+ if (clientSSL != (id)[NSNull null]) {
|
|
+ NSString *path = [clientSSL objectForKey:@"path"];
|
|
+ NSString *password = [clientSSL objectForKey:@"password"];
|
|
+ credential = [self getUrlCredential:challenge path:path password:password];
|
|
+ }
|
|
+
|
|
+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
|
return;
|
|
}
|
|
if ([[challenge protectionSpace] serverTrust] != nil && customCertificatesForHost != nil && host != nil) {
|