如何在使用本机反应时实现SSL证书的锁定?

时间:2022-02-23 18:59:50

I need to implement SSL Certificate Pinning in my react native application.

我需要在我的react本机应用程序中实现SSL证书锁定。

I know very little about SSL/TLS let alone pinning. I am also not a native mobile developer, though I know Java and learned Objective-C on this project enough to get around.

我对SSL/TLS知之甚少,更别提绑定了。我也不是一个本地的移动开发人员,尽管我对Java和Objective-C有足够的了解。

I started searching for how to execute this task.

我开始寻找如何执行这个任务。

Doesn't React Native already implement this?

No, My initial search lead me to this proposal which has received no activity since August 2nd 2016.

不,我最初的搜索让我想到了这个自2016年8月2日以来没有任何活动的提案。

From it I learned that react-native uses OkHttp which does support Pinning, but I wouldn't be able to pull it off from Javascript, which is not really a requirement but a plus.

从它中我了解到,反应本机使用的是支持固定的OkHttp,但我无法从Javascript中提取它,这不是一个真正的要求,而是一个附加条件。

Implement it in Javascript.

While react seems like it uses the nodejs runtime, it is more like a browser than node, meaning it does not support all native modules, specifically the https module, for which I had implemented certificate pinning following this article. Thus could not carry it into react native.

尽管react似乎使用的是nodejs运行时,但它更像是一个浏览器而不是节点,这意味着它不支持所有的本地模块,特别是https模块,我在本文之后实现了证书锁定。因此不能携带它进入自然反应。

I tried using rn-nodeify but the modules didn't work. This has been true since RN 0.33 to RN 0.35 which I'm currently on.

我尝试使用rn-nodeify,但是模块不工作。这在RN 0。33到RN 0。35之间是成立的,我现在在上面。

Implement using phonegap plugin

I thought of using a phongape-plugin however since I have a dependency on libraries that require react 0.32+ I can't use react-native-cordova-plugin

我想到了使用phongap插件,但是因为我依赖于需要0.32+的库,所以我不能使用反应式本机-cordova-plugin

Just do it natively

While I'm not a native app developer I can always take a crack at it, only a matter of time.

虽然我不是一个本地的应用程序开发人员,但我可以尝试一下,这只是时间问题。

Android has certificate pinning

I learned that android supports SSL Pinning however was unsuccessful as it seems that this approach does not work Prior to Android 7. As well as only working for android.

我了解到,android支持SSL锁定,但是失败了,因为在android 7之前,这种方法似乎并不奏效。而且只在android上工作。

The bottom line

I have exhausted several directions and will continue to pursue more native implementation, maybe figure out how to configure OkHttp and RNNetworking then maybe bridging back to react-native.

我已经给出了几个方向,并将继续追求更多的本机实现,也许会找到如何配置OkHttp和RNNetworking,然后再将其桥接回反应式本机。

But is there already any implementations or guide for IOS and android?

但是IOS和android已经有什么实现或指南了吗?

1 个解决方案

#1


32  

After exhausting the current spectrum of available options from Javascript I decided to simply implement certificate pinning natively it all seems so simple now that I'm done.

在耗尽了Javascript当前可用选项的范围之后,我决定直接实现证书固定,现在一切看起来都很简单,我已经完成了。

Skip to headers titled Android Solution and IOS Solution if you don't want to read through the process of reaching the solution.

如果您不想阅读解决方案的过程,请跳到标题为“Android解决方案和IOS解决方案”的标题。

Android

Following Kudo's recommendation I thought out to implement pinning using okhttp3.

根据Kudo的建议,我想出了使用okhttp3实现钉住。

client = new OkHttpClient.Builder()
        .certificatePinner(new CertificatePinner.Builder()
            .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
            .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
            .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
            .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
            .build())
        .build();

I first started by learning how to create a native android bridge with react nativecreating a toast module. I then extended it with a method for sending a simple request

首先,我学习了如何使用react native - android网桥来创建toast模块。然后,我用一个发送简单请求的方法扩展了它。

@ReactMethod
public void showURL(String url, int duration) {
    try {
        Request request = new Request.Builder()
        .url(url)
        .build();
        Response response = client.newCall(request).execute();
        Toast.makeText(getReactApplicationContext(), response.body().string(), duration).show();
    } catch (IOException e) {
        Toast.makeText(getReactApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

Succeeding in sending a request I then turned to sending a request pinned.

发送请求成功后,我转而发送一个请求。

I used these packages in my file

我在我的文件中使用了这些软件包

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
import java.io.IOException;

import java.util.Map;
import java.util.HashMap;

Kudo's approach wasn't clear on where I would get the public keys or how to generate them. luckily okhttp3 docs in addition to providing a clear demonstration of how to use the CertificatePinner stated that to get the public keys all I would need to do is send a request with an incorrect pin, and the correct pins will appear in the error message.

Kudo的方法不清楚我将从哪里获得公钥,也不清楚如何生成公钥。幸运的是,okhttp3文档除了提供了一个清楚的演示,说明如何使用这个证书来获取公钥,我所需要做的就是用一个不正确的pin发送一个请求,而正确的引脚将出现在错误消息中。

After taking a moment to realise that OkHttpClent.Builder() can be chained and I can include the CertificatePinner before the build, unlike the misleading example in Kudo's proposal (probably and older version) I came up with this method.

在花了一些时间认识到OkHttpClent.Builder()可以被链接,并且我可以在构建之前包含certificate atepinner,这与Kudo的提议(可能是旧版本)中的误导性示例不同。

@ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
  Callback successCallback) {
    try {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
             .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAA=")
             .build();
        OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();

        Request request = new Request.Builder()
             .url("https://" + hostname)
             .build();
        Response response =client.newCall(request).execute();
        successCallback.invoke(response.body().string());
    } catch (Exception e) {
        errorCallbackContainingCorrectKeys.invoke(e.getMessage());
    }
}

Then replacing the public keychains I got in the error yielded back the page's body, indicating I had made a successful request, I change one letter of the key to make sure it was working and I knew I was on track.

然后替换掉错误中得到的公钥链,就会返回页面的主体,这表明我已经发出了一个成功的请求,我更改了密钥的一个字母,以确保它正常工作,并且我知道我正在跟踪。

I finally had this method in my ToastModule.java file

我终于在ToastModule中有了这个方法。java文件

@ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
  Callback successCallback) {
    try {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
             .add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
             .add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
             .add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
             .build();
        OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();

        Request request = new Request.Builder()
             .url("https://" + hostname)
             .build();
        Response response =client.newCall(request).execute();
        successCallback.invoke(response.body().string());
    } catch (Exception e) {
        errorCallbackContainingCorrectKeys.invoke(e.getMessage());
    }
}

Android Solution Extending React Native's OkHttpClient

Having figured out how to send pinned http request was good, now I can use the method I created, but ideally I thought it would be best to extend the existing client, so as to immediately gain the benefit of implementing.

了解了如何发送固定的http请求之后,现在我可以使用我创建的方法了,但是我认为最好扩展现有的客户机,以便立即获得实现的好处。

This solution is valid as of RN0.35 and I don't know how it will fair in the future.

这个解对于RN0.35是有效的,我不知道将来它会如何公平。

While looking into ways of extending the OkHttpClient for RN I came across this article explaining how to add TLS 1.2 support through replacing the SSLSocketFactory.

在研究为RN扩展OkHttpClient的方法时,我遇到了这篇文章,解释了如何通过替换SSLSocketFactory来添加TLS 1.2支持。

reading it I learned react uses an OkHttpClientProvider for creating the OkHttpClient instance used by the XMLHttpRequest Object and therefore if we replace that instance we would apply pinning to all the app.

读它时,我了解到react使用一个OkHttpClientProvider来创建XMLHttpRequest对象使用的OkHttpClient实例,因此如果我们替换了这个实例,我们就会对所有的应用程序进行绑定。

I added a file called OkHttpCertPin.java to my android/app/src/main/java/com/dreidev folder

我添加了一个名为OkHttpCertPin的文件。java到我的android/app/src/main/java/com dreidev文件夹

package com.dreidev;

import android.util.Log;

import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;


import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;

public class OkHttpCertPin {
    private static String hostname = "*.efghermes.com";
    private static final String TAG = "OkHttpCertPin";

    public static OkHttpClient extend(OkHttpClient currentClient){
      try {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
             .add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
             .add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
             .add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
             .build();
        Log.d(TAG, "extending client");
        return currentClient.newBuilder().certificatePinner(certificatePinner).build();
      } catch (Exception e) {
        Log.e(TAG, e.getMessage());
      }
     return currentClient;
   }
}

This package has a method extend which takes an existing OkHttpClient and rebuilds it adding the certificatePinner and returns the newly built instance.

这个包有一个方法扩展,它接受一个现有的OkHttpClient并重新构建它,添加了certificate atepinner并返回新构建的实例。

I then modified my MainActivity.java file following this answer's advice by adding the following methods

然后我修改了主活动。通过添加以下方法来遵循此答案的java文件

.
.
.
import com.facebook.react.ReactActivity;
import android.os.Bundle;

import com.dreidev.OkHttpCertPin;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;

public class MainActivity extends ReactActivity {

  @Override
  public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     rebuildOkHtttp();
  }

  private void rebuildOkHtttp() {
      OkHttpClient currentClient = OkHttpClientProvider.getOkHttpClient();
      OkHttpClient replacementClient = OkHttpCertPin.extend(currentClient);
      OkHttpClientProvider.replaceOkHttpClient(replacementClient);
  }
.
.
.

This solution was carried out in favor of completely reimplementing the OkHttpClientProvider createClient method, as inspecting the provider I realized that the master version had implemented TLS 1.2 support but was not yet an available option for me to use, and so rebuilding was found to be the best means of extending the client. I'm wondering how this approach will fair as I upgrade but for now it works well.

这个解决方案是完全赞成重新实现OkHttpClientProvider createClient方法,作为检查供应商我意识到主版本已经实现TLS 1.2的支持,但还没有一个可用的选项供我使用,所以重建被发现的最好方法扩展客户端。我想知道在我升级的时候,这种方法会怎么样,但是现在它运行得很好。

Update It seems that starting 0.43 this trick no longer works. For timebound reasons I will freeze my project at 0.42 for now, until the reason for why rebuilding stopped working is clear.

更新一下,从0.43开始这个技巧似乎不再有效。由于时间限制的原因,我暂时将我的项目冻结在0.42,直到重建停止工作的原因变得清晰。

Solution IOS

For IOS I had thought I would need to follow a similar method, again starting with Kudo's proposal as my lead.

对于IOS,我认为我需要遵循类似的方法,从Kudo的建议开始,作为我的领导。

Inspecting the RCTNetwork module I learned that NSURLConnection was used, so instead of trying to create a completely new module with AFNetworking as suggested in the proposal I discovered TrustKit

检查RCTNetwork模块时,我了解到使用了NSURLConnection,所以我没有像在建议中发现的TrustKit那样尝试创建一个带有AFNetworking的全新模块

following its Getting Started Guide I simply added

按照它的入门指南,我只是添加了

pod 'TrustKit'

to my podfile and ran pod install

到我的播客和运行pod安装

the GettingStartedGuide explained how I can configure this pod from my pList.file but preferring to use code than configuration files I added the following lines to my AppDelegate.m file

GettingStartedGuide解释了我如何从我的pList中配置这个pod。文件但更喜欢使用代码而不是配置文件,我在AppDelegate中添加了以下代码行。m文件

.
.
.
#import <TrustKit/TrustKit.h>
.
.
.
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{


  // Initialize TrustKit
  NSDictionary *trustKitConfig =
    @{
    // Auto-swizzle NSURLSession delegates to add pinning validation
    kTSKSwizzleNetworkDelegates: @YES,

    kTSKPinnedDomains: @{

       // Pin invalid SPKI hashes to *.yahoo.com to demonstrate pinning failures
       @"efghermes.com" : @{
           kTSKEnforcePinning:@YES,
           kTSKIncludeSubdomains:@YES,
           kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],

           // Wrong SPKI hashes to demonstrate pinning failure
           kTSKPublicKeyHashes : @[
              @"+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=",
              @"aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=",
              @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY="
              ],

          // Send reports for pinning failures
          // Email info@datatheorem.com if you need a free dashboard to see your App's reports
          kTSKReportUris: @[@"https://overmind.datatheorem.com/trustkit/report"]
          },

     }
  };

  [TrustKit initializeWithConfiguration:trustKitConfig];
.
.
.

I got the public key hashes from my android implementation and it just worked (the version of TrustKit I received in my pods is 1.3.2)

我从我的android实现中获得了公钥散列,它就正常工作了(我在pod中收到的TrustKit的版本是1.3.2)

I was glad IOS turned out to be a breath

我很高兴IOS让我喘不过气来

As a side note TrustKit warned that it's Auto-swizzle won't work if the NSURLSession and Connection are already swizzled. that said it seems to be working well so far.

作为附加说明,TrustKit警告如果NSURLSession和连接已经被swizzled了,它的自动swizzle将不起作用。也就是说,到目前为止,它似乎运行得很好。

Conclusion

This answer presents the solution for both Android and IOS, given I was able to implement this in native code.

这个答案为Android和IOS提供了解决方案,因为我能够在本地代码中实现这一点。

One possible improvement may be to implement a common platform module where setting public keys and configuring the Network providers of both android and IOS can be managed in javascript.

一个可能的改进是实现一个通用的平台模块,在其中设置公钥和配置android和IOS的网络提供商可以用javascript进行管理。

Kudo's proposal mentioned simply adding the public keys to the js bundle may however expose a vulnerability, where somehow the bundle file can be replaced.

Kudo提到的简单地向js包添加公钥的建议可能会暴露出一个漏洞,在这个漏洞中可以以某种方式替换bundle文件。

I don't know how that attack vector can function, but certainly the extra step of signing the bundle.js as proposed may protect the js bundle.

我不知道攻击向量是如何工作的,但肯定是对bundle签名的额外步骤。如提议的js可以保护js包。

Another approach may be to simply encode the js bundle into a 64 bit string and include it in the native code directly as mentioned in this issue's conversation. This approach has the benefit of obfuscating as well hardwiring the js bundle into the app, making it inaccessible for attackers or so I think.

另一种方法可能是简单地将js包编码成64位字符串,并将其直接包含在本问题对话中所提到的本机代码中。这种方法的好处是混淆了js包并将其硬连接到应用程序中,使攻击者无法访问它,我想应该是这样。

If you read this far I hope I enlightened you on your quest for fixing your bug and wish you enjoy a sunny day.

如果你读到这篇文章,我希望我能启发你去解决你的问题,并祝你度过一个阳光灿烂的一天。

#1


32  

After exhausting the current spectrum of available options from Javascript I decided to simply implement certificate pinning natively it all seems so simple now that I'm done.

在耗尽了Javascript当前可用选项的范围之后,我决定直接实现证书固定,现在一切看起来都很简单,我已经完成了。

Skip to headers titled Android Solution and IOS Solution if you don't want to read through the process of reaching the solution.

如果您不想阅读解决方案的过程,请跳到标题为“Android解决方案和IOS解决方案”的标题。

Android

Following Kudo's recommendation I thought out to implement pinning using okhttp3.

根据Kudo的建议,我想出了使用okhttp3实现钉住。

client = new OkHttpClient.Builder()
        .certificatePinner(new CertificatePinner.Builder()
            .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
            .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
            .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
            .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
            .build())
        .build();

I first started by learning how to create a native android bridge with react nativecreating a toast module. I then extended it with a method for sending a simple request

首先,我学习了如何使用react native - android网桥来创建toast模块。然后,我用一个发送简单请求的方法扩展了它。

@ReactMethod
public void showURL(String url, int duration) {
    try {
        Request request = new Request.Builder()
        .url(url)
        .build();
        Response response = client.newCall(request).execute();
        Toast.makeText(getReactApplicationContext(), response.body().string(), duration).show();
    } catch (IOException e) {
        Toast.makeText(getReactApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

Succeeding in sending a request I then turned to sending a request pinned.

发送请求成功后,我转而发送一个请求。

I used these packages in my file

我在我的文件中使用了这些软件包

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
import java.io.IOException;

import java.util.Map;
import java.util.HashMap;

Kudo's approach wasn't clear on where I would get the public keys or how to generate them. luckily okhttp3 docs in addition to providing a clear demonstration of how to use the CertificatePinner stated that to get the public keys all I would need to do is send a request with an incorrect pin, and the correct pins will appear in the error message.

Kudo的方法不清楚我将从哪里获得公钥,也不清楚如何生成公钥。幸运的是,okhttp3文档除了提供了一个清楚的演示,说明如何使用这个证书来获取公钥,我所需要做的就是用一个不正确的pin发送一个请求,而正确的引脚将出现在错误消息中。

After taking a moment to realise that OkHttpClent.Builder() can be chained and I can include the CertificatePinner before the build, unlike the misleading example in Kudo's proposal (probably and older version) I came up with this method.

在花了一些时间认识到OkHttpClent.Builder()可以被链接,并且我可以在构建之前包含certificate atepinner,这与Kudo的提议(可能是旧版本)中的误导性示例不同。

@ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
  Callback successCallback) {
    try {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
             .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAA=")
             .build();
        OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();

        Request request = new Request.Builder()
             .url("https://" + hostname)
             .build();
        Response response =client.newCall(request).execute();
        successCallback.invoke(response.body().string());
    } catch (Exception e) {
        errorCallbackContainingCorrectKeys.invoke(e.getMessage());
    }
}

Then replacing the public keychains I got in the error yielded back the page's body, indicating I had made a successful request, I change one letter of the key to make sure it was working and I knew I was on track.

然后替换掉错误中得到的公钥链,就会返回页面的主体,这表明我已经发出了一个成功的请求,我更改了密钥的一个字母,以确保它正常工作,并且我知道我正在跟踪。

I finally had this method in my ToastModule.java file

我终于在ToastModule中有了这个方法。java文件

@ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
  Callback successCallback) {
    try {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
             .add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
             .add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
             .add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
             .build();
        OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();

        Request request = new Request.Builder()
             .url("https://" + hostname)
             .build();
        Response response =client.newCall(request).execute();
        successCallback.invoke(response.body().string());
    } catch (Exception e) {
        errorCallbackContainingCorrectKeys.invoke(e.getMessage());
    }
}

Android Solution Extending React Native's OkHttpClient

Having figured out how to send pinned http request was good, now I can use the method I created, but ideally I thought it would be best to extend the existing client, so as to immediately gain the benefit of implementing.

了解了如何发送固定的http请求之后,现在我可以使用我创建的方法了,但是我认为最好扩展现有的客户机,以便立即获得实现的好处。

This solution is valid as of RN0.35 and I don't know how it will fair in the future.

这个解对于RN0.35是有效的,我不知道将来它会如何公平。

While looking into ways of extending the OkHttpClient for RN I came across this article explaining how to add TLS 1.2 support through replacing the SSLSocketFactory.

在研究为RN扩展OkHttpClient的方法时,我遇到了这篇文章,解释了如何通过替换SSLSocketFactory来添加TLS 1.2支持。

reading it I learned react uses an OkHttpClientProvider for creating the OkHttpClient instance used by the XMLHttpRequest Object and therefore if we replace that instance we would apply pinning to all the app.

读它时,我了解到react使用一个OkHttpClientProvider来创建XMLHttpRequest对象使用的OkHttpClient实例,因此如果我们替换了这个实例,我们就会对所有的应用程序进行绑定。

I added a file called OkHttpCertPin.java to my android/app/src/main/java/com/dreidev folder

我添加了一个名为OkHttpCertPin的文件。java到我的android/app/src/main/java/com dreidev文件夹

package com.dreidev;

import android.util.Log;

import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;


import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;

public class OkHttpCertPin {
    private static String hostname = "*.efghermes.com";
    private static final String TAG = "OkHttpCertPin";

    public static OkHttpClient extend(OkHttpClient currentClient){
      try {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
             .add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
             .add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
             .add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
             .build();
        Log.d(TAG, "extending client");
        return currentClient.newBuilder().certificatePinner(certificatePinner).build();
      } catch (Exception e) {
        Log.e(TAG, e.getMessage());
      }
     return currentClient;
   }
}

This package has a method extend which takes an existing OkHttpClient and rebuilds it adding the certificatePinner and returns the newly built instance.

这个包有一个方法扩展,它接受一个现有的OkHttpClient并重新构建它,添加了certificate atepinner并返回新构建的实例。

I then modified my MainActivity.java file following this answer's advice by adding the following methods

然后我修改了主活动。通过添加以下方法来遵循此答案的java文件

.
.
.
import com.facebook.react.ReactActivity;
import android.os.Bundle;

import com.dreidev.OkHttpCertPin;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;

public class MainActivity extends ReactActivity {

  @Override
  public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     rebuildOkHtttp();
  }

  private void rebuildOkHtttp() {
      OkHttpClient currentClient = OkHttpClientProvider.getOkHttpClient();
      OkHttpClient replacementClient = OkHttpCertPin.extend(currentClient);
      OkHttpClientProvider.replaceOkHttpClient(replacementClient);
  }
.
.
.

This solution was carried out in favor of completely reimplementing the OkHttpClientProvider createClient method, as inspecting the provider I realized that the master version had implemented TLS 1.2 support but was not yet an available option for me to use, and so rebuilding was found to be the best means of extending the client. I'm wondering how this approach will fair as I upgrade but for now it works well.

这个解决方案是完全赞成重新实现OkHttpClientProvider createClient方法,作为检查供应商我意识到主版本已经实现TLS 1.2的支持,但还没有一个可用的选项供我使用,所以重建被发现的最好方法扩展客户端。我想知道在我升级的时候,这种方法会怎么样,但是现在它运行得很好。

Update It seems that starting 0.43 this trick no longer works. For timebound reasons I will freeze my project at 0.42 for now, until the reason for why rebuilding stopped working is clear.

更新一下,从0.43开始这个技巧似乎不再有效。由于时间限制的原因,我暂时将我的项目冻结在0.42,直到重建停止工作的原因变得清晰。

Solution IOS

For IOS I had thought I would need to follow a similar method, again starting with Kudo's proposal as my lead.

对于IOS,我认为我需要遵循类似的方法,从Kudo的建议开始,作为我的领导。

Inspecting the RCTNetwork module I learned that NSURLConnection was used, so instead of trying to create a completely new module with AFNetworking as suggested in the proposal I discovered TrustKit

检查RCTNetwork模块时,我了解到使用了NSURLConnection,所以我没有像在建议中发现的TrustKit那样尝试创建一个带有AFNetworking的全新模块

following its Getting Started Guide I simply added

按照它的入门指南,我只是添加了

pod 'TrustKit'

to my podfile and ran pod install

到我的播客和运行pod安装

the GettingStartedGuide explained how I can configure this pod from my pList.file but preferring to use code than configuration files I added the following lines to my AppDelegate.m file

GettingStartedGuide解释了我如何从我的pList中配置这个pod。文件但更喜欢使用代码而不是配置文件,我在AppDelegate中添加了以下代码行。m文件

.
.
.
#import <TrustKit/TrustKit.h>
.
.
.
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{


  // Initialize TrustKit
  NSDictionary *trustKitConfig =
    @{
    // Auto-swizzle NSURLSession delegates to add pinning validation
    kTSKSwizzleNetworkDelegates: @YES,

    kTSKPinnedDomains: @{

       // Pin invalid SPKI hashes to *.yahoo.com to demonstrate pinning failures
       @"efghermes.com" : @{
           kTSKEnforcePinning:@YES,
           kTSKIncludeSubdomains:@YES,
           kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],

           // Wrong SPKI hashes to demonstrate pinning failure
           kTSKPublicKeyHashes : @[
              @"+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=",
              @"aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=",
              @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY="
              ],

          // Send reports for pinning failures
          // Email info@datatheorem.com if you need a free dashboard to see your App's reports
          kTSKReportUris: @[@"https://overmind.datatheorem.com/trustkit/report"]
          },

     }
  };

  [TrustKit initializeWithConfiguration:trustKitConfig];
.
.
.

I got the public key hashes from my android implementation and it just worked (the version of TrustKit I received in my pods is 1.3.2)

我从我的android实现中获得了公钥散列,它就正常工作了(我在pod中收到的TrustKit的版本是1.3.2)

I was glad IOS turned out to be a breath

我很高兴IOS让我喘不过气来

As a side note TrustKit warned that it's Auto-swizzle won't work if the NSURLSession and Connection are already swizzled. that said it seems to be working well so far.

作为附加说明,TrustKit警告如果NSURLSession和连接已经被swizzled了,它的自动swizzle将不起作用。也就是说,到目前为止,它似乎运行得很好。

Conclusion

This answer presents the solution for both Android and IOS, given I was able to implement this in native code.

这个答案为Android和IOS提供了解决方案,因为我能够在本地代码中实现这一点。

One possible improvement may be to implement a common platform module where setting public keys and configuring the Network providers of both android and IOS can be managed in javascript.

一个可能的改进是实现一个通用的平台模块,在其中设置公钥和配置android和IOS的网络提供商可以用javascript进行管理。

Kudo's proposal mentioned simply adding the public keys to the js bundle may however expose a vulnerability, where somehow the bundle file can be replaced.

Kudo提到的简单地向js包添加公钥的建议可能会暴露出一个漏洞,在这个漏洞中可以以某种方式替换bundle文件。

I don't know how that attack vector can function, but certainly the extra step of signing the bundle.js as proposed may protect the js bundle.

我不知道攻击向量是如何工作的,但肯定是对bundle签名的额外步骤。如提议的js可以保护js包。

Another approach may be to simply encode the js bundle into a 64 bit string and include it in the native code directly as mentioned in this issue's conversation. This approach has the benefit of obfuscating as well hardwiring the js bundle into the app, making it inaccessible for attackers or so I think.

另一种方法可能是简单地将js包编码成64位字符串,并将其直接包含在本问题对话中所提到的本机代码中。这种方法的好处是混淆了js包并将其硬连接到应用程序中,使攻击者无法访问它,我想应该是这样。

If you read this far I hope I enlightened you on your quest for fixing your bug and wish you enjoy a sunny day.

如果你读到这篇文章,我希望我能启发你去解决你的问题,并祝你度过一个阳光灿烂的一天。