当前位置: 首页 > 知识库问答 >
问题:

如何在使用React Native时实现SSL证书锁定

蔚和风
2023-03-14

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

我对SSL/TLS知之甚少,更不用说钉扎了。我也不是本地的移动开发者,尽管我了解Java,并且在这个项目上学习了Objective-C,足以四处走动。

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

不,我的初步搜索导致我提交了该提案,该提案自2016年8月2日以来未收到任何活动。

从中我了解到react native使用OkHttp,它确实支持固定,但我无法从Javascript中实现它,这不是真正的需求,而是一个优点。

虽然react看起来像是使用nodejs运行时,但它更像是一个浏览器而不是节点,这意味着它不支持所有本机模块,特别是https模块,我在本文之后为其实现了证书固定。这样就不能把它带进你的房间了。

我尝试使用rn nodeify,但模块不起作用。这是真实的,从RN 0.33到RN 0.35,我目前正在使用。

我曾想过使用phongape插件,但是因为我依赖于需要react 0.32的库,所以我不能使用react原生cordova插件

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

我了解到,Android支持SSL锁定,但不成功,因为它似乎这种方法不工作之前的Android 7.以及只为Android工作。

我已经穷尽了几个方向,并将继续追求更多的本机实现,可能会找到如何配置OkHttp和RNNetworking,然后可能会桥接回react native。

但是,IOS和android已经有任何实现或指南了吗?

共有2个答案

苗盛
2023-03-14

你可以使用这个libhttps://github.com/nlt2390/react-native-pinning-ssl

它使用SHA1密钥而不是证书来验证SSL连接。

明阳旭
2023-03-14

在用尽了Javascript中当前可用选项的范围后,我决定简单地本地实现证书锁定现在我完成了,这一切看起来都很简单。

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

根据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();

我首先开始学习如何创建一个原生的android桥与反应原生的烤面包模块。然后我用一个简单的请求发送方法扩展了它

@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;

工藤的方法不清楚我将在哪里获得公钥或者如何生成它们。幸运的是,okhttp p3文档除了提供如何使用证书的清晰演示外,还指出要获得公钥,我所需要做的就是发送请求引脚不正确,正确的引脚将出现在错误消息中。

花了一会儿才意识到OkHttpClent。Builder()可以被链接,我可以在构建之前包含证书Pner,不像工藤的建议(可能是旧版本)中的误导性示例,我想出了这个方法。

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

然后替换错误中的公钥链,返回页面的正文,表明我已提出了成功的请求,我更改了密钥的一个字母,以确保它正常工作,并且我知道我已步入正轨。

我终于在我的演讲模块中使用了这种方法。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());
    }
}

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

这个解决方案是有效的RN0.35,我不知道它将如何公平的未来。

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

阅读它,我了解到反应使用OkHttpClientProvider来创建XMLHttpClient对象使用的OkHttpClient实例,因此如果我们替换该实例,我们将对所有应用程序应用锁定。

我添加了一个名为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;
   }
}

这个包有一个方法extend,它接受一个现有的OkHttpClient并通过添加CertificatePaner重新构建它,然后返回新构建的实例。

然后我修改了我的主要活动。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,我认为我需要遵循类似的方法,再次从工藤的提议开始。

在检查RCTNetwork模块时,我了解到使用了NSURLConnection,因此我没有像提案中建议的那样尝试使用AFNetworking创建一个全新的模块,而是发现了TrustKit

在《入门指南》之后,我简单地添加了

pod 'TrustKit'

到我的pod文件并运行pod安装

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];
.
.
.

我从我的android实现中得到了公钥散列,它刚刚起作用(我在我的pods中收到的TrustKit版本是1.3.2)

我很高兴IOS变成了一口气

作为一个附加说明,TrustKit警告说,如果NSURL会话和连接已经被切换,它的自动切换将不起作用。也就是说,到目前为止,它似乎运行良好。

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

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

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

我不知道攻击向量是如何运作的,但肯定是额外的步骤。建议的js可以保护js包。

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

如果你读到这里,我希望我能启发你修复bug的方法,并祝你度过一个阳光明媚的日子。

 类似资料:
  • 问题:由于“无法联系服务器”,用户无法登录移动应用程序 调试消息:“类型错误: 网络请求失败” 尝试修复:重新启动服务器,验证数据库正在运行并且没有任何更改,重新启动服务器正在运行的VM,我使用postman检查了api。当我运行一个简单的POST请求时,收到以下消息: 连接到时出错https://app.something.com/api/Accounts/5076/sometest?filte

  • 我目前正在为我的React本机应用程序实施SSL证书固定。 我找到了一个很好的答案,详细解释了为和存档此文件的步骤 但不幸的是,解决方案只能在使用API发出请求的情况下工作,我正在尝试找到一个还包括API的解决方案

  • 我想在我的应用程序中使用SSL锁定(公钥锁定)。我是新来的。有人可以回复我简单的步骤生成公钥,csr文件和SSL证书iOS。

  • 我创建了两个类:-这一个扩展了OkHttp,并有一个新方法getUnsafeOkHttpClient()。 和其他主要类别: 但我仍然有一个错误: 线程“main”javax中出现异常。网ssl。SSLHandshakeException:太阳。安全验证器。ValidatorException:PKIX路径生成失败:sun。安全供应商。证书路径。SunCertPathBuilderExceptio

  • 问题内容: 如何使用Python解码经过pem编码的(base64)证书?例如,这来自github.com: 根据ssl-shopper的说法,应该是这样的: 如何使用python获取此纯文本? 问题答案: Python的标准库,即使是最新版本,也不包含任何可解码X.509证书的内容。但是,附加软件包确实支持此功能。引用文档中的示例: 另一个可选的附加软件包是。这是围绕OpenSSL C API的

  • 我已经成功地为我的Android Studio开发的应用程序实现了SSL证书固定。 我使用Web服务请求和响应使用XML协议。 我已经在我的应用程序中完成了Java层ssl固定,为此,我使用了两个证书和存储在app raw文件夹中的文件。 但是,当审核团队获得对应用程序的访问权限时,他们破坏了SSL固定,并建议使用本机层SSL证书固定实现,而不是Java层SSL证书固定。 我已经阅读了这个关于本机