significa que not found for certification certificado android objective-c cordova ssl react-native

android - que - ¿Cómo puedo implementar la fijación de certificados SSL mientras uso React Native?



trust anchor for certification path not found que significa (1)

Después de agotar el espectro actual de opciones disponibles de Javascript, decidí simplemente implementar el anclaje del certificado de forma nativa, todo parece tan simple ahora que he terminado.

Vaya a encabezados titulados Solución Android y Solución IOS si no desea leer el proceso para 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 creando un módulo de brindis. Luego lo extendí con un método para enviar una solicitud simple

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

Después de enviar una solicitud, pasé a enviar una solicitud.

Usé 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 era claro en cuanto a dónde obtendría las claves públicas o cómo generarlas. Afortunadamente, los documentos okhttp3 además de proporcionar una demostración clara de cómo usar el CertificatePinner indicaron que para obtener las claves públicas todo lo que tendría que hacer es enviar una solicitud con un pin incorrecto, y los pines correctos aparecerán en el mensaje de error.

Después de tomarme un momento para darme cuenta de que OkHttpClent.Builder () puede estar encadenado y puedo incluir el CertificatePinner antes de la compilación, a diferencia del ejemplo engañoso en la propuesta de Kudo (probablemente y una versión anterior), se me ocurrió 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, al reemplazar los llaveros públicos que obtuve en el error, obtuve el cuerpo de la página, lo que indica que hice una solicitud exitosa, cambié una letra de la clave para asegurarme de que funcionaba y sabía que estaba en buen 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 Android que extiende el OkHttpClient de React Native

Habiendo descifrado cómo enviar la solicitud de PIN pinned fue bueno, ahora puedo usar el método que creé, pero idealmente pensé que sería mejor extender el cliente existente, para obtener el beneficio de implementar inmediatamente.

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

Mientras buscaba maneras de extender el OkHttpClient para RN me encontré con este artículo que explica cómo agregar soporte TLS 1.2 mediante el reemplazo de SSLSocketFactory.

Al leerlo, aprendí que reaccionar usa un OkHttpClientProvider para crear la instancia de OkHttpClient utilizada por el objeto XMLHttpRequest y, por lo tanto, si reemplazamos esa instancia, aplicaremos la fijación a toda la aplicación.

OkHttpCertPin.java 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 extendido que toma un OkHttpClient existente y lo reconstruye agregando el certificatePinner y devuelve la instancia recién construida.

Luego modifiqué mi archivo MainActivity.java siguiendo los consejos de esta respuesta al agregar 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 a favor de volver a implementar por completo el método OkHttpClientProvider createClient, al inspeccionar el proveedor me di cuenta de que la versión maestra había implementado el soporte TLS 1.2 pero aún no era una opción disponible para mí, por lo que la reconstrucción fue la mejor medio de extender el cliente. Me pregunto cómo será este enfoque justo cuando actualizo, 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 el momento, hasta que quede claro el motivo por el cual la reconstrucción dejó de funcionar.

Solución IOS

Para IOS, pensé que tendría que seguir un método similar, nuevamente comenzando con la propuesta de Kudo como mi líder.

Al inspeccionar el módulo RCTNetwork, supe que se usó NSURLConnection, así que, en lugar de tratar de crear un módulo completamente nuevo con AFNetworking, como se sugiere en la propuesta, descubrí TrustKit

siguiendo su Guía de introducción simplemente agregué

pod ''TrustKit''

a mi podfile y corrió la pod install

GettingStartedGuide explicó cómo puedo configurar este pod desde mi pList.file pero prefiero usar código que archivos de configuración Agregué 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 [email protected] if you need a free dashboard to see your App''s reports kTSKReportUris: @[@"https://overmind.datatheorem.com/trustkit/report"] }, } }; [TrustKit initializeWithConfiguration:trustKitConfig]; . . .

Obtuve los hashes de clave pública de mi implementación de android y simplemente funcionó (la versión de TrustKit que recibí en mis pods es 1.3.2)

Me alegré de que IOS resultara ser un aliento

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

Conclusión

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

Una posible mejora puede ser la implementación de un módulo de plataforma común en el que el establecimiento de claves públicas y la configuración de los proveedores de red tanto de Android como de iOS se pueden gestionar en javascript.

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

No sé cómo puede funcionar ese vector de ataque, pero ciertamente el paso adicional de firmar 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 tema . Este enfoque tiene el beneficio de ofuscar y cablear el paquete js en la aplicación, haciéndolo inaccesible para los atacantes o eso creo.

Si lees esto hasta ahora, espero que te haya iluminado en tu búsqueda por arreglar tu error y desearía que disfrutes de un día soleado.

Necesito implementar la fijación de certificados SSL en mi aplicación nativa de reacción.

Sé muy poco acerca de SSL / TLS y mucho menos de fijar. Tampoco soy un desarrollador móvil nativo, aunque conozco Java y aprendí Objective-C en este proyecto lo suficiente como para moverme.

Empecé a buscar cómo ejecutar esta tarea.

¿Reactivo nativo ya no implementa esto?

No, mi búsqueda inicial me llevó a esta propuesta que no ha recibido ninguna actividad desde el 2 de agosto de 2016.

De esto aprendí que react-native usa OkHttp, que no admite Pinning, pero no podría sacarlo de Javascript, que no es realmente un requisito sino un plus.

Implementarlo en Javascript.

Aunque reaccionar parece que usa el tiempo de ejecución nodejs, es más parecido a un navegador que a un nodo, lo que significa que no admite todos los módulos nativos, específicamente el módulo https, para el que implementé la fijación de certificados después de este artículo . Por lo tanto, no podría llevarlo a reaccionar como nativo.

Intenté usar rn-nodeify pero los módulos no funcionaron. Esto ha sido cierto desde RN 0.33 a RN 0.35 que estoy actualmente.

Implementar usando el plugin phonegap

Pensé en usar un phongape-plugin sin embargo, dado que tengo una dependencia en las bibliotecas que requieren reaccionar 0.32+, no puedo usar react-native-cordova-plugin

Solo hazlo de forma nativa

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

Android tiene fijación de certificado

Me enteré de que Android admite SSL Pinning, pero no tuvo éxito, ya que parece que este enfoque no funciona antes de Android 7. Además, solo funciona para Android.

La línea de fondo

He agotado varias direcciones y continuaré buscando más implementación nativa, tal vez descubra cómo configurar OkHttp y RNNetworking, y luego tal vez establecer un puente hacia la reacción nativa.

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