searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

JitsiMeet如何忽略Https证书验证

2023-08-31 07:34:46
167
0

JitsiMeet是专注音视频会议的的一个开源项目,并且通过ReactNative进行跨平台,所以JitsiMeet本质上也是一个ReactNative项目;JitsiMeet项目开发中App会连接后端的各种环境地址,你会遇到一些环境是Https地址,但这些Https上用的证书可能是自建的或者使用别的域名的证书,总之就是证书无法匹配到根证书,无法通过验证。于是为了能和后端愉快的通信,我就得处理在这些环境下忽略Https证书的验证。

ReactNative在JS层并没有提供Https证书操作的接口,并且ReactNative网络最终是靠原生层的代码去实现的, 所以问题的解决思路是通过修改IOS和Android原生代码来实现忽略Https证书的验证。

要忽略证书验证的场景有以下两种:

  1. WebView
  2. Api Request

Webview

首先我们来看一下 WebView的解决方案。

var RCTWebView = requireNativeComponent('RCTWebView', WebView, WebView.extraNativeComponentConfig);

以上代码出现在 webview.android.js 和 webview.ios.js 中,通过搜索可以发现对应的Native代码。

Android:

react-native/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java.

IOS:

facebook/react-native​github.com/facebook/react-native/blob/d005c8c08ac27a0e060d381e3f1b55cf4143cdde/React/Views/RCTWebViewManager.m

React/Views/RCTWebViewManager.m

 

在Android的Webview使用了自定义的WebViewClient, 而WebViewClient中有函数onReceivedSslError

@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   handler.cancel();
}

当WebView加载Https链接发生证书错误时会回调这个函数。函数默认返回值是 handler.cancel(), 去取消当前地址的加载,所以会显示一空白页。那么想忽略错误继续加载地址,可以改成如下:

@Override 
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    handler.proceed();
}

需要修改上述的函数就得自己重写ReactWebViewManager.java,为了保证暴露给JS层的ClassName也是RTCWebView,必须实现canOverrideExistingModule,来覆盖系统的Module。

@Override
public boolean canOverrideExistingModule() {
  return true;
}

接下来看IOS的RCTWebViewManager.m, 发现并不像Android可以覆盖系统的Module。不过幸好IOS有全局扩展类的语法。通过在AppDelegate.m中 加上对NSURLRequest进行扩展加上忽略证书验证。

@implementation NSURLRequest (AllowAnyHTTPSCertificate)

+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
  return YES;
}

@end

 

Api Request

在Android中是JS层网络请求最终使用的是 Java层的 NetworkingModule类, 类中使用OkHttp处理网络请求,所以我们只需要给Okhttp加上忽略证书验让的设置。主要代码如下:

private SSLContext getSLLContext () {
        X509TrustManager xtm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] x509Certificates = new X509Certificate[0];
                return x509Certificates;
            }
        };

        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");

            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        return sslContext;
}
NetworkingModule(
            ReactApplicationContext reactContext,
            @Nullable String defaultUserAgent,
            OkHttpClient client,
            @Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
        super(reactContext);

        if (networkInterceptorCreators != null) {
            OkHttpClient.Builder clientBuilder = client.newBuilder();
            for (NetworkInterceptorCreator networkInterceptorCreator : networkInterceptorCreators) {
                clientBuilder.addNetworkInterceptor(networkInterceptorCreator.create());
            }
            client = clientBuilder.build();
        }
        client = client.newBuilder().hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        }).sslSocketFactory(getSLLContext().getSocketFactory()).build();
        mClient = client;
        mCookieHandler = new ForwardingCookieHandler(reactContext);
        mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
        mShuttingDown = false;
        mDefaultUserAgent = defaultUserAgent;
        mRequestIds = new HashSet<>();
}
@Override
    public boolean canOverrideExistingModule() {
        return true ;
    }

 

在IOS中,同样我们只需要在AppDelegate.m中去扩展RCTHTTPRequestHandler 即可。

@implementation RCTHTTPRequestHandler (RCTHTTPRequestHandlerSSL)

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
  completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}

@end

 

测试验证

经测试,JitsiMeet项目经过上述方法修改运行后,在IOS端上已经成功忽略证书验证,但在安卓端却没有效果,仍然需要验证证书;

这是因为JitsiMeet项目安卓端引用的是./node_modules/react-native/android/com/facebook/react/react-native的react-native-x.xx.x.aar(x.xx.x是react-native版本)已经预先编译好的包,而不是./node_modules/react-native下的源码。所以这里需要通过以下步骤才能使得在安卓端上成功忽略https证书:

1. 解压出aar包中的classes.jar包

2. 使用jd-gui 反编译出ReactWebViewManager.java和NetworkingModule.java

3. 对ReactWebViewManager.java和NetworkingModule.java分别添加上述的修改内容

4. 使用 javac 将ReactWebViewManager.java和NetworkingModule.java编译成.class字节码文件

5. 通过 jar -uvf classes.jar class文件将步骤4生成的字节码文件更新到原本的classes.jar中

6. 将更新后的classes.jar重新压缩成aar包并替换./node_modules/react-native/android/com/facebook/react/react-native的react-native-x.xx.x.aar

此时再次运行安卓端,可以看到https证书忽略效果生效。

0条评论
0 / 1000
wbq
18文章数
0粉丝数
wbq
18 文章 | 0 粉丝
wbq
18文章数
0粉丝数
wbq
18 文章 | 0 粉丝
原创

JitsiMeet如何忽略Https证书验证

2023-08-31 07:34:46
167
0

JitsiMeet是专注音视频会议的的一个开源项目,并且通过ReactNative进行跨平台,所以JitsiMeet本质上也是一个ReactNative项目;JitsiMeet项目开发中App会连接后端的各种环境地址,你会遇到一些环境是Https地址,但这些Https上用的证书可能是自建的或者使用别的域名的证书,总之就是证书无法匹配到根证书,无法通过验证。于是为了能和后端愉快的通信,我就得处理在这些环境下忽略Https证书的验证。

ReactNative在JS层并没有提供Https证书操作的接口,并且ReactNative网络最终是靠原生层的代码去实现的, 所以问题的解决思路是通过修改IOS和Android原生代码来实现忽略Https证书的验证。

要忽略证书验证的场景有以下两种:

  1. WebView
  2. Api Request

Webview

首先我们来看一下 WebView的解决方案。

var RCTWebView = requireNativeComponent('RCTWebView', WebView, WebView.extraNativeComponentConfig);

以上代码出现在 webview.android.js 和 webview.ios.js 中,通过搜索可以发现对应的Native代码。

Android:

react-native/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java.

IOS:

facebook/react-native​github.com/facebook/react-native/blob/d005c8c08ac27a0e060d381e3f1b55cf4143cdde/React/Views/RCTWebViewManager.m

React/Views/RCTWebViewManager.m

 

在Android的Webview使用了自定义的WebViewClient, 而WebViewClient中有函数onReceivedSslError

@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   handler.cancel();
}

当WebView加载Https链接发生证书错误时会回调这个函数。函数默认返回值是 handler.cancel(), 去取消当前地址的加载,所以会显示一空白页。那么想忽略错误继续加载地址,可以改成如下:

@Override 
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    handler.proceed();
}

需要修改上述的函数就得自己重写ReactWebViewManager.java,为了保证暴露给JS层的ClassName也是RTCWebView,必须实现canOverrideExistingModule,来覆盖系统的Module。

@Override
public boolean canOverrideExistingModule() {
  return true;
}

接下来看IOS的RCTWebViewManager.m, 发现并不像Android可以覆盖系统的Module。不过幸好IOS有全局扩展类的语法。通过在AppDelegate.m中 加上对NSURLRequest进行扩展加上忽略证书验证。

@implementation NSURLRequest (AllowAnyHTTPSCertificate)

+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
  return YES;
}

@end

 

Api Request

在Android中是JS层网络请求最终使用的是 Java层的 NetworkingModule类, 类中使用OkHttp处理网络请求,所以我们只需要给Okhttp加上忽略证书验让的设置。主要代码如下:

private SSLContext getSLLContext () {
        X509TrustManager xtm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] x509Certificates = new X509Certificate[0];
                return x509Certificates;
            }
        };

        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");

            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        return sslContext;
}
NetworkingModule(
            ReactApplicationContext reactContext,
            @Nullable String defaultUserAgent,
            OkHttpClient client,
            @Nullable List<NetworkInterceptorCreator> networkInterceptorCreators) {
        super(reactContext);

        if (networkInterceptorCreators != null) {
            OkHttpClient.Builder clientBuilder = client.newBuilder();
            for (NetworkInterceptorCreator networkInterceptorCreator : networkInterceptorCreators) {
                clientBuilder.addNetworkInterceptor(networkInterceptorCreator.create());
            }
            client = clientBuilder.build();
        }
        client = client.newBuilder().hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        }).sslSocketFactory(getSLLContext().getSocketFactory()).build();
        mClient = client;
        mCookieHandler = new ForwardingCookieHandler(reactContext);
        mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
        mShuttingDown = false;
        mDefaultUserAgent = defaultUserAgent;
        mRequestIds = new HashSet<>();
}
@Override
    public boolean canOverrideExistingModule() {
        return true ;
    }

 

在IOS中,同样我们只需要在AppDelegate.m中去扩展RCTHTTPRequestHandler 即可。

@implementation RCTHTTPRequestHandler (RCTHTTPRequestHandlerSSL)

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
  completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}

@end

 

测试验证

经测试,JitsiMeet项目经过上述方法修改运行后,在IOS端上已经成功忽略证书验证,但在安卓端却没有效果,仍然需要验证证书;

这是因为JitsiMeet项目安卓端引用的是./node_modules/react-native/android/com/facebook/react/react-native的react-native-x.xx.x.aar(x.xx.x是react-native版本)已经预先编译好的包,而不是./node_modules/react-native下的源码。所以这里需要通过以下步骤才能使得在安卓端上成功忽略https证书:

1. 解压出aar包中的classes.jar包

2. 使用jd-gui 反编译出ReactWebViewManager.java和NetworkingModule.java

3. 对ReactWebViewManager.java和NetworkingModule.java分别添加上述的修改内容

4. 使用 javac 将ReactWebViewManager.java和NetworkingModule.java编译成.class字节码文件

5. 通过 jar -uvf classes.jar class文件将步骤4生成的字节码文件更新到原本的classes.jar中

6. 将更新后的classes.jar重新压缩成aar包并替换./node_modules/react-native/android/com/facebook/react/react-native的react-native-x.xx.x.aar

此时再次运行安卓端,可以看到https证书忽略效果生效。

文章来自个人专栏
音视频开发
3 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
2
0