Подтвердить что ты не робот

Как я могу реализовать привязку SSL-сертификата при использовании React Native

Мне нужно внедрить SSL-сертификат в моем собственном приложении для реагирования.

Я очень мало знаю о SSL/TLS, не говоря уже о закреплении. Я также не являюсь родным мобильным разработчиком, хотя я знаю Java и изучил Objective-C в этом проекте, чтобы обойти.

Я начал искать, как выполнить эту задачу.

Не работает React Native уже реализовано?

Нет. Мой первоначальный поиск привел меня к это предложение, которое не было проведено с 2 августа 2016 года.

Из него я узнал, что response-native использует OkHttp, который поддерживает Pinning, но я бы не смог его отключить от Javascript, что на самом деле не требование, а плюс.

Внедрить его в Javascript.

В то время как реакция кажется, что она использует время выполнения nodejs, она больше похожа на браузер, чем node, что означает, что он не поддерживает все собственные модули, в частности модуль https, для которого я реализовал выдачу сертификата после в этой статье. Таким образом, это не могло нести его в ответ на родной.

Я попытался использовать rn-nodeify, но модули не работали. Это было верно, так как RN 0,33 до RN 0,35, который я сейчас нахожу.

Реализация с использованием плагина phonegap

Я думал об использовании phongape-plugin, однако, поскольку у меня есть зависимость от библиотек, для которых требуется реакция 0.32+, я не могу использовать react-native-cordova-plugin

Просто сделайте это изначально

Пока я не являюсь разработчиком собственного приложения, я всегда могу взломать его, только вопрос времени.

Android имеет фиксацию сертификата

Я узнал, что поддержка андроида SSL-скриншот однако не увенчалась успехом, поскольку кажется, что этот подход не работает до Android 7. Как и только работающих на Android.

Нижняя строка

Я исчерпал несколько направлений и продолжу реализацию более естественной реализации, возможно, выясню, как настроить OkHttp и RNNetworking, а затем, возможно, перейдем к реакции-native.

Но есть ли какие-либо реализации или руководство для IOS и Android?

4b9b3361

Ответ 1

После исчерпания существующего спектра доступных опций из Javascript я решил просто внедрить сертификат, который изначально был таким простым, что я закончил.

Перейдите к заголовкам под названием Решение для Android и IOS Solution, если вы не хотите читать процесс достижения решения.

Android

Следуя рекомендации 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();

Сначала я начал изучать, как создать родной андроидный мост с реакцией native, создающий тост-модуль. Затем я расширил его методом отправки простого запроса

@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();
    }
}

При отправке запроса я затем повернулся к отправке запрошенного запроса.

Я использовал эти пакеты в своем файле

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 не был ясен, когда я получу открытые ключи или их создание. к счастью okhttp3 docs в дополнение к предоставлению четкой демонстрации того, как использовать CertificatePinner, заявил, что для получения открытых ключей все, что мне нужно будет сделать, это отправить запрос с неправильным выводом, и в сообщении об ошибке появятся правильные контакты.

После того, как вы поняли, что OkHttpClent.Builder() может быть привязанным, и я могу включить CertificatePinner перед сборкой, в отличие от вводящего в заблуждение примера в предложении 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());
    }
}

Затем, заменив публичные связки ключей, я получил ошибку, возвратил тело страницы, указав, что сделал успешный запрос, я меняю одну букву ключа, чтобы убедиться, что он работает, и я знал, что я на ходу.

У меня, наконец, был этот метод в моем файле 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 Расширение действия Реальный корень OkHttpClient

Выяснив, как отправить прикрепленный http-запрос, было хорошо, теперь я могу использовать метод, который я создал, но в идеале я думал, что лучше всего расширить существующий клиент, чтобы сразу воспользоваться преимуществами реализации.

Это решение действует как RN0.35, и я не знаю, как это будет справедливо в будущем.

Изучая способы расширения OkHttpClient для RN, я столкнулся с этой статьей, объясняя, как добавить поддержку TLS 1.2, заменив SSLSocketFactory.

Чтение. Я узнал, что реакция использует OkHttpClientProvider для создания экземпляра OkHttpClient, используемого объектом XMLHttpRequest, и поэтому, если мы заменим этот экземпляр, мы применили бы привязку ко всему приложению.

Я добавил файл под названием 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;
   }
}

Этот пакет имеет метод расширения, который принимает существующий OkHttpClient и перестраивает его, добавляя certificatePinner и возвращает вновь созданный экземпляр.

Затем я изменил файл MainActivity.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);
  }
.
.
.

Это решение было выполнено в пользу полного переопределения метода OkHttpClientProvider createClient, поскольку при проверке поставщика я понял, что главная версия реализовала TLS 1.2, но еще не был доступным вариантом для меня, и поэтому восстановление оказалось лучшим средством для расширения клиента. Мне интересно, как этот подход будет справедливым по мере моего обновления, но на данный момент он работает хорошо.

Обновление Кажется, что начиная с 0,43 этот трюк больше не работает. По срочным причинам я заморожу свой проект на 0,42 до тех пор, пока не станет ясно, почему восстановление перестало работать.

Решение IOS

Для IOS я подумал, что мне нужно будет следовать аналогичному методу, опять же, начиная с предложения Kudo в качестве моего руководства.

Проверяя модуль RCTNetwork, я узнал, что NSURLConnection используется, поэтому вместо того, чтобы пытаться создать совершенно новый модуль с AFNetworking, как было предложено в предложении, я обнаружил TrustKit

следуя руководству по началу работы, я просто добавил

pod 'TrustKit'

в мой podfile и запустил pod install

the GettingStartedGuide объяснил, как я могу настроить этот модуль из моего pList.file, но предпочитая использовать код, кроме файлов конфигурации, я добавил следующие строки в файл 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 [email protected] if you need a free dashboard to see your App reports
          kTSKReportUris: @[@"https://overmind.datatheorem.com/trustkit/report"]
          },

     }
  };

  [TrustKit initializeWithConfiguration:trustKitConfig];
.
.
.

Я получил хэши открытого ключа из моей реализации Android и только что сработал (версия TrustKit, которую я получил в моих контейнерах, - 1.3.2)

Я был рад, что IOS оказалась в состоянии дыхания.

В качестве дополнительной заметки TrustKit предупредил, что Auto-swizzle не будет работать, если NSURLSession и Connection уже будут проверены. что, похоже, он работает до сих пор.

Заключение

В этом ответе представлено решение как для Android, так и для IOS, поскольку я смог реализовать это в собственном коде.

Одним из возможных улучшений может быть внедрение общего модуля платформы, в котором настройку открытых ключей и настройку сетевых поставщиков как Android, так и IOS можно управлять в javascript.

Предложение Kudo, упомянутое просто добавлением открытых ключей в пакет js, может, однако, подвергнуть уязвимости, где каким-то образом файл пакета может быть заменен.

Я не знаю, как этот вектор атаки может функционировать, но, безусловно, дополнительный шаг подписи пакета .js, как было предложено, может защитить пакет js.

Другим подходом может быть просто кодирование пакета js в 64-разрядную строку и включение его в собственный код непосредственно в качестве упомянутого в этом выпуске. Этот подход имеет преимущество при запутывании, а также упрощение набора js в приложение, что делает его недоступным для злоумышленников или, как я полагаю.

Если вы прочтете это, я надеюсь, что я просветил вас в поисках исправления вашей ошибки и пожелал вам насладиться солнечным днем.

Ответ 2

Вы можете использовать эту библиотеку https://github.com/nlt2390/react-native-pinning-ssl

Он проверяет SSL-соединение, используя ключи SHA1, а не сертификаты.

Ответ 3

Прикрепление SSL не является хорошей практикой на уровне приложения, потому что каждый сертификат имеет срок действия в течение определенного периода времени. Так что лучший способ закрепить сертификат - на стороне сервера. Я интегрировал то же самое в случае шведской интеграции Bankid SSL в моем приложении.