¿Cómo puedo implementar Pinning del certificado SSL mientras uso React Native

Tengo que implementar la vinculación de certificados SSL en mi aplicación nativa de respuesta.

Sé muy poco acerca de SSL / TLS y mucho menos pinning. Yo también no soy un desarrollador de móviles nativos, aunque conozco Java y aprendí Objective-C en este proyecto lo suficiente para moverse.

Empecé a buscar cómo ejecutar esta tarea.

¿No React Native ya implementa esto?

No, Mi búsqueda inicial me lleva a esta propuesta que no ha recibido actividad desde el 2 de agosto de 2016.

De él me enteré de que reaccionar-nativo utiliza OkHttp que apoya Pinning, pero no sería capaz de sacarlo de Javascript, que no es realmente un requisito, sino un plus.

Implementarlo en Javascript.

Mientras reacciona parece que utiliza el tiempo de ejecución nodejs, es más como un navegador de nodo, lo que significa que no es compatible con todos los módulos nativos, específicamente el módulo https, para el que había implementado el certificado de fijación después de este artículo . Por lo tanto no podía llevarlo a reaccionar nativo.

Intenté usar rn-nodeify pero los módulos no trabajaron. Esto ha sido cierto desde RN 0,33 a RN 0,35 que estoy actualmente en.

Implementar mediante el complemento phonegap

Pensé en utilizar un phongape-plugin sin embargo, ya que tengo una dependencia de las bibliotecas que requieren reaccionar 0.32 + No puedo usar reaccionar-native-cordova-plugin

Solo hazlo de manera nativa

Aunque no soy un desarrollador de aplicaciones nativas, siempre puedo darme un respiro, solo es cuestión de tiempo.

Android tiene certificado de fijación

Me enteré de que Android soporta SSL Pinning sin embargo no tuvo éxito ya que parece que este enfoque no funciona Antes de Android 7. Además de trabajar sólo para Android.

La línea de fondo

He agotado varias direcciones y seguirá persiguiendo la implementación más nativa, tal vez averiguar cómo configurar OkHttp y RNNetworking entonces tal vez puentear de nuevo a reaccionar-nativo.

¿Pero ya hay alguna implementación o guía para IOS y Android?

One Solution collect form web for “¿Cómo puedo implementar Pinning del certificado SSL mientras uso React Native”

Después de agotar el espectro actual de opciones disponibles de Javascript decidí simplemente implementar el certificado pinning nativamente todo parece tan simple ahora que he terminado.

Salta a los encabezados titulados Solución de Android y Solución de IOS si no quieres leer el proceso de llegar a la solución.

Androide

Siguiendo la recomendación de Kudo, pensé en implementar pinning usando 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(); 

Primero comencé aprendiendo cómo crear un puente androide nativo con reaccionar nativo que crea un módulo del pan tostado. Luego lo amplié con un método para enviar una simple solicitud

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

Al tener éxito en el envío de una solicitud, a continuación, se volvió a enviar una solicitud clavada.

He utilizado estos paquetes en mi archivo

 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; 

El enfoque de Kudo no estaba claro sobre dónde conseguiría las claves públicas o cómo generarlas. Por suerte okhttp3 docs, además de proporcionar una clara demostración de cómo utilizar el CertificatePinner declaró que para obtener las claves públicas todo lo que tendría que hacer es enviar una solicitud con un incorrecto pin, y los pins correctos aparecerán en el mensaje de error.

Después de tomar un momento para darse cuenta de que OkHttpClent.Builder () se puede encadenar y puedo incluir el CertificatePinner antes de la construcción, a diferencia del ejemplo engañoso en la propuesta de Kudo (probablemente y una versión más antigua) me vino con este método.

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

Luego, reemplazando los llaveros públicos que recibí en el error, cedí el cuerpo de la página, indicando que había hecho una solicitud exitosa, cambié una letra de la clave para asegurarme de que funcionaba y sabía que estaba en camino.

Finalmente tuve este método en mi archivo 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()); } } 

Solución de Android extendiendo React Native's OkHttpClient

Después de haber descubierto la forma de enviar solicitud de http clavado era bueno, ahora puedo usar el método que creé, pero idealmente pensé que sería mejor ampliar el cliente existente, de modo de obtener inmediatamente el beneficio de la aplicación.

Esta solución es válida a partir de RN0.35 y no sé cómo será justo en el futuro.

Mientras que miraba en maneras de extender el OkHttpClient para RN me encontré con este artículo que explica cómo agregar la ayuda de TLS 1.2 con substituir el SSLSocketFactory.

Al leerlo aprendí que reacciona usa un OkHttpClientProvider para crear la instancia OkHttpClient utilizada por el objeto XMLHttpRequest y por lo tanto si reemplazamos esa instancia aplicaríamos pinning a toda la aplicación.

He añadido un archivo llamado OkHttpCertPin.java a mi 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; } } 

Este paquete tiene un método extend que toma un OkHttpClient existente y lo reconstruye agregando el certificatePinner y devuelve la instancia nuevamente construida.

A continuación, modifiqué mi archivo MainActivity.java siguiendo el consejo de esta respuesta añadiendo los siguientes métodos

 . . . 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); } . . . 

Esta solución se llevó a cabo en favor de la plena reimplementación de la OkHttpClientProvider createClient método, como la inspección del proveedor me di cuenta de que la versión maestra había implementado TLS 1.2 soporte, pero todavía no era una opción disponible para mí, y por lo que la reconstrucción se encontró a ser el Mejores medios de ampliar el cliente. Me pregunto cómo este enfoque será justo como actualizar, pero por ahora funciona bien.

Actualización Parece que a partir de 0.43 este truco ya no funciona. Por razones de tiempo, congelaré mi proyecto en 0,42 por ahora, hasta que la razón de por qué la reconstrucción dejó de funcionar es clara.

Solución IOS

Para IOS había pensado que tendría que seguir un método similar, otra vez comenzando con la propuesta de Kudo como mi ventaja.

Inspeccionando el módulo RCTNetwork Aprendí que se usó NSURLConnection, así que en lugar de intentar crear un módulo completamente nuevo con AFNetworking como se sugirió en la propuesta, descubrí TrustKit

Siguiendo su guía de introducción Simplemente agregé

 pod 'TrustKit' 

A mi podfile y corrió pod install

El GettingStartedGuide explicó cómo puedo configurar este pod de mi archivo pList.file pero prefiero usar código que los archivos de configuración He añadido las siguientes líneas a mi archivo 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]; . . . 

Conseguí los hashes de la llave pública de mi androide implementación y apenas trabajó (la versión de TrustKit que recibí en mis vainas es 1.3.2)

Me alegro de IOS resultó ser una respiración

Como nota secundaria, TrustKit advirtió que su Auto-swizzle no funcionará si NSURLSession y Connection ya están swizzled. Que dijo que parece estar funcionando bien hasta ahora.

Conclusión

Esta respuesta presenta la solución para Android e IOS, dado que fui capaz de implementar esto en código nativo.

Una posible mejora puede ser implementar un módulo de plataforma común donde la configuración de claves públicas y la configuración de los proveedores de red de android e IOS se pueden gestionar en javascript.

La propuesta de Kudo mencionada simplemente añadiendo las claves públicas al paquete js puede sin embargo exponer una vulnerabilidad, donde de alguna manera el archivo de paquete puede ser reemplazado.

No sé cómo puede funcionar ese vector de ataque, pero ciertamente el paso extra de firmar el bundle.js como se propone puede proteger el paquete js.

Otro enfoque puede ser simplemente codificar el paquete js en una cadena de 64 bits e incluirlo en el código nativo directamente como se menciona en la conversación de este número . Este enfoque tiene el beneficio de obfuscating así hardwiring el js paquete en la aplicación, haciéndolo inaccesible para los atacantes o así que creo.

Si usted lee esto lejos espero le aclaré en su búsqueda para corregir su insecto y desearle gozar de un día asoleado.

  • Error de lectura: error en la biblioteca SSL, generalmente un error de protocolo
  • Tablet no puede descargar el archivo .apk en algunos navegadores
  • Certificado particular de confianza solamente emitido por CA - Android
  • ¿Cómo habilitar la depuración de SSL en la plataforma Android?
  • La autenticación mutua de SSL FAIL en Android Client acepta el certificado de servidores pero el servidor no obtiene el certificado de cliente
  • Java.security.cert.CertPathValidatorException: Ancla de confianza para ruta de certificación no encontrada. Android 2.3
  • SSLHandshakeException: Ancla de confianza para la ruta de certificación no encontrada. Solo en la API de Android <19
  • Cómo llamar al servicio web https en Android
  • Certificado de Cliente de Webview de Android, Autenticación Mutua, SSL sobre Webview
  • ¿Cómo evito el error "No peer certificate" al conectarme a este sitio HTTPS en Android?
  • No es un certificado de confianza que utiliza ksoap2-android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.