在 Android 上使用客户端/服务器证书进行双向身份验证 SSL 套接字

2023-11-24

我正在开发一个需要客户端和服务器证书身份验证的 Android 应用程序。我创建了一个 SSLClient 类,该类在常规桌面 Java SE 6 上运行良好。我已将其移至我的 Android 项目中,但收到以下错误:“未找到 KeyStore JKS 实现”。

我在网上查了一下,看起来 Android 上可能不支持 Java 密钥库(太棒了!),但我有一种感觉,事情远不止于此,因为我找到的示例代码都与我的代码不相似。我正在努力做所有事情。我发现的所有内容都涉及使用 http 客户端而不是原始 SSL 套接字。我需要此应用程序的 SSL 套接字。

以下是我的 SSLClient.java 文件中的代码。它读取密钥库和信任库,创建到服务器的 SSL 套接字连接,然后在等待来自服务器的输入行时运行循环,然后通过调用不同类中的方法来处理它们。我非常有兴趣听取任何有在 Android 平台上使用 SSL 套接字经验的人的意见。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.AccessControlException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import otherpackege.OtherClass;

import android.content.Context;
import android.util.Log;

public class SSLClient 
{
    static SSLContext ssl_ctx;

    public SSLClient(Context context)
    {
        try
        {
            // Setup truststore
            KeyStore trustStore = KeyStore.getInstance("BKS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            InputStream trustStoreStream = context.getResources().openRawResource(R.raw.mysrvtruststore);
            trustStore.load(trustStoreStream, "testtest".toCharArray());
            trustManagerFactory.init(trustStore);

            // Setup keystore
            KeyStore keyStore = KeyStore.getInstance("BKS");
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            InputStream keyStoreStream = context.getResources().openRawResource(R.raw.clientkeystore);
keyStore.load(keyStoreStream, "testtest".toCharArray());
            keyManagerFactory.init(keyStore, "testtest".toCharArray());

            Log.d("SSL", "Key " + keyStore.size());
            Log.d("SSL", "Trust " + trustStore.size());

            // Setup the SSL context to use the truststore and keystore
            ssl_ctx = SSLContext.getInstance("TLS");
            ssl_ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

            Log.d("SSL", "keyManagerFactory " + keyManagerFactory.getKeyManagers().length);
            Log.d("SSL", "trustManagerFactory " + trustManagerFactory.getTrustManagers().length);
        }
        catch (NoSuchAlgorithmException nsae)
        {
            Log.d("SSL", nsae.getMessage());
        }
        catch (KeyStoreException kse)
        {
            Log.d("SSL", kse.getMessage());
        }
        catch (IOException ioe)
        {
            Log.d("SSL", ioe.getMessage());
        }
        catch (CertificateException ce)
        {
            Log.d("SSL", ce.getMessage());
        }
        catch (KeyManagementException kme)
        {
            Log.d("SSL", kme.getMessage());
        }
        catch(AccessControlException ace)
        {
            Log.d("SSL", ace.getMessage());
        }
        catch(UnrecoverableKeyException uke)
        {
            Log.d("SSL", uke.getMessage());
        }

        try
        {
            Handler handler = new Handler();
            handler.start();
        }
        catch (IOException ioException) 
        {
            ioException.printStackTrace();
        }
     }  
}

//class Handler implements Runnable 
class Handler extends Thread
{
    private SSLSocket socket;
    private BufferedReader input;
    static public PrintWriter output;

    private String serverUrl = "174.61.103.206";
    private String serverPort = "6000";

    Handler(SSLSocket socket) throws IOException
    {

    }
    Handler() throws IOException
    {

    }

    public void sendMessagameInfoge(String message)
    {
        Handler.output.println(message);
    }

    @Override
    public void run() 
    {
        String line;

        try 
        {
            SSLSocketFactory socketFactory = (SSLSocketFactory) SSLClient.ssl_ctx.getSocketFactory();
            socket = (SSLSocket) socketFactory.createSocket(serverUrl, Integer.parseInt(serverPort));
            this.input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            Handler.output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
            Log.d("SSL", "Created the socket, input, and output!!");

            do
            {
                line = input.readLine();
                while (line == null)
                {
                    line = input.readLine();
                }

                // Parse the message and do something with it
                // Done in a different class
                OtherClass.parseMessageString(line);
            }
            while ( !line.equals("exit|") );
        }
        catch (IOException ioe)
        {
            System.out.println(ioe);
        }
        finally 
        {
            try 
            {
                input.close();
                output.close();
                socket.close();
            } 
            catch(IOException ioe) 
            {
            } 
            finally 
            {

            }
        }
    }
}

Update:
在这个问题上取得了一些良好的进展。发现确实不支持JKS,也不直接选择SunX509类型。我已经更新了上面的代码以反映这些更改。我仍然有一个问题,显然没有加载密钥库和信任库。当我了解更多信息时,我会更新。


Update2:
我正在以桌面 Java 方式而不是正确的 Android 方式加载密钥库和信任库文件。文件必须放在 res/raw 文件夹中并使用 getResources() 加载。我现在得到的密钥库和信任库大小计数为 1 和 1,这意味着它们正在加载。我仍然因异常而崩溃,但越来越接近了!当我开始工作时我会更新。


Update3:
看起来现在一切正常,除了我的密钥库设置不正确。如果我在服务器上禁用客户端身份验证,它的连接不会出现问题。当我启用它时,我会得到一个handling exception: javax.net.ssl.SSLHandshakeException: null cert chain错误。所以看来我没有正确设置证书链。我发布了另一个问题,询问如何使用正确的证书链创建 BKS 格式的客户端密钥库:如何创建包含客户端证书链的 BKS (BouncyCastle) 格式 Java 密钥库


Android支持BKS、P12等格式的证书。

对于 BKS 格式: 使用portecle将您的证书(.p12 和 .crt)转换为 .bks。

您的目录中需要 2 个文件/res/raw文件夹:truststore.bks服务器的信任证书(从 .cer 文件转换而来)

client.bks/client.p12- 客户端证书(从包含客户端证书和客户端密钥的 .p12 文件转换而来)

import java.io.*;
import java.security.KeyStore;

import javax.net.ssl.*;

import org.apache.http.*;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.*;
import org.apache.http.conn.scheme.*;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.*;

import android.app.Activity;
import android.os.Bundle;

public class SslTestActivity extends Activity {

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    try {
      // setup truststore to provide trust for the server certificate

      // load truststore certificate
      InputStream clientTruststoreIs = getResources().openRawResource(R.raw.truststore);
      KeyStore trustStore = null;
      trustStore = KeyStore.getInstance("BKS");
      trustStore.load(clientTruststoreIs, "MyPassword".toCharArray());

      System.out.println("Loaded server certificates: " + trustStore.size());

      // initialize trust manager factory with the read truststore
      TrustManagerFactory trustManagerFactory = null;
      trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init(trustStore);

      // setup client certificate

      // load client certificate
      InputStream keyStoreStream = getResources().openRawResource(R.raw.client);
      KeyStore keyStore = null;
      keyStore = KeyStore.getInstance("BKS");
      keyStore.load(keyStoreStream, "MyPassword".toCharArray());

      System.out.println("Loaded client certificates: " + keyStore.size());

      // initialize key manager factory with the read client certificate
      KeyManagerFactory keyManagerFactory = null;
      keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      keyManagerFactory.init(keyStore, "MyPassword".toCharArray());


      // initialize SSLSocketFactory to use the certificates
      SSLSocketFactory socketFactory = null;
      socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, keyStore, "MyTestPassword2010",
          trustStore, null, null);

      // Set basic data
      HttpParams params = new BasicHttpParams();
      HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
      HttpProtocolParams.setContentCharset(params, "UTF-8");
      HttpProtocolParams.setUseExpectContinue(params, true);
      HttpProtocolParams.setUserAgent(params, "Android app/1.0.0");

      // Make pool
      ConnPerRoute connPerRoute = new ConnPerRouteBean(12);
      ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
      ConnManagerParams.setMaxTotalConnections(params, 20);

      // Set timeout
      HttpConnectionParams.setStaleCheckingEnabled(params, false);
      HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
      HttpConnectionParams.setSoTimeout(params, 20 * 1000);
      HttpConnectionParams.setSocketBufferSize(params, 8192);

      // Some client params
      HttpClientParams.setRedirecting(params, false);

      // Register http/s shemas!
      SchemeRegistry schReg = new SchemeRegistry();
      schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
      schReg.register(new Scheme("https", socketFactory, 443));
      ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
      DefaultHttpClient sClient = new DefaultHttpClient(conMgr, params);

      HttpGet httpGet = new HttpGet("https://server/path/service.wsdl");
      HttpResponse response = sClient.execute(httpGet);
      HttpEntity httpEntity = response.getEntity();

      InputStream is = httpEntity.getContent();
      BufferedReader read = new BufferedReader(new InputStreamReader(is));
      String query = null;
      while ((query = read.readLine()) != null)
        System.out.println(query);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

Update:

您还可以直接加载信任存储的 .crt 文件,而无需将其转换为 BKS:

    private static KeyStore loadTrustStore(String[] certificateFilenames) {
        AssetManager assetsManager = GirdersApp.getInstance().getAssets();

        int length = certificateFilenames.length;
        List<Certificate> certificates = new ArrayList<Certificate>(length);
        for (String certificateFilename : certificateFilenames) {
          InputStream is;
          try {
            is = assetsManager.open(certificateFilename, AssetManager.ACCESS_BUFFER);
            Certificate certificate = KeyStoreManager.loadX509Certificate(is);
            certificates.add(certificate);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
        }

        Certificate[] certificatesArray = certificates.toArray(new Certificate[certificates.size()]);
          return new generateKeystore(certificatesArray);
      }

 /**
   * Generates keystore congaing the specified certificates.
   *
   * @param certificates certificates to add in keystore
   * @return keystore with the specified certificates
   * @throws KeyStoreException if keystore can not be generated.
   */
  public KeyStore generateKeystore(Certificate[] certificates) throws RuntimeException {
      // construct empty keystore
      KeyStore keyStore = KeyStore.getInstance(keyStoreType);

      // initialize keystore
      keyStore.load(null, null);

      // load certificates into keystore
      int length = certificates.length;
      for (int i = 0; i < length; i++) {
        Certificate certificate = certificates[i];
        keyStore.setEntry(String.valueOf(i), new KeyStore.TrustedCertificateEntry(certificate),
            null);
      }
      return keyStore;
  }

带有客户端证书的KeyStore也是如此,您可以直接使用.p12文件,而无需将其转换为BKS。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Android 上使用客户端/服务器证书进行双向身份验证 SSL 套接字 的相关文章

  • 合并 dex 程序类型已存在时出错:android.support.v4.os.ResultReceiver$MyResultReceiver

    合并dex时出错 以下是依赖项 ext anko version 0 10 5 support lib 1 0 0 alpha1 room lib 1 1 0 dependencies implementation org jetbrain
  • LocalDate 减去 period 得到错误的结果

    LocalDate减去一个Period 如 28年1个月27天 得到错误的结果 但减去一个Period 只有天单位 如 10282 天 得到正确的结果 有什么需要注意的吗 public static void main String arg
  • 将现有 eclipse 项目导出到 war 文件时出现“模块名称无效”

    我正在尝试将现有 Eclipse 项目导出到 war 文件 但无论我在 WAR Export 对话框页面中输入什么 系统总是返回 模块名称无效 我不知道如何解决这个问题 谢谢您的帮助 我有同样的问题 我修复了它 请按照以下步骤操作 您可以创
  • Flutter - 删除 ListView 中项目之间的空间

    我正在使用 ListView builder 函数来创建项目列表 然而 iOS 中每个项目之间的空间很大 截图 你知道如何删除项目吗 看来是默认的 因为我没有添加它 code 列表显示 return Scaffold body ListVi
  • 查找数组中的组合

    我在java中有一个像这样的二维数组 transmission communication tv television approach memorycode methodact 我需要获得所有组合 例如 transmission appr
  • Android 媒体播放器搜索栏

    我有一个创建 播放和处理媒体播放器 只是音频 的服务 但我在主要活动中有一个搜索栏 我想自然地显示音频文件的进度并允许用户搜索到不同的位置 我花了很长时间才弄清楚 将 UI 中的搜索栏连接到服务中的媒体播放器的最佳或正确方法是什么 我将这样
  • 如何使用 swagger-codegen-plugin (maven) 生成客户端代码?

    我需要使用 swagger codegen plugin for maven 在 eclipse 中生成服务器存根代码 你能帮忙怎么做吗 以及需要什么配置 在 pom xml 中 我找到了这个答案 您只需要像下面这样更改 pom xml 即
  • 如何使用共享首选项在两个 Android 应用程序之间共享数据?

    我有两个应用程序 App1 和 App2 我想使用共享首选项在 App1 中保存数据并在 App2 中访问 反之亦然 我可以在 App1 中保存数据并在 App2 中访问数据 但反之则不行 这就是我现在正在做的 在清单中 android s
  • 如何使用 MotionLayout 调整 TextView 的大小

    我正在尝试创建一个CollapsingToolbar动画使用MotionLayout 我已经成功地将所有内容设置为动画 使其表现得像CollapsingToolbar具有高度的灵活性 这意味着我可以轻松创建很棒的动画 而无需编写大量代码 我
  • 调整 SwipeRefreshLayout 高度,将 View 置于其底部

    I have SwipeRefreshLayout里面一个RelativeLayout 问题是SwipeRefreshLayout占据了屏幕上的所有位置 我需要放置一个视图after这个观点 看图片 https i stack imgur
  • 分离 Fragment 和删除 Fragment 有什么区别?

    在 Android 文档中碎片交易 http developer android com reference android app FragmentTransaction html我注意到两种非常相似的方法 detach and remo
  • java swing:向 JTree 项目添加自定义图形按钮

    我想在 JTree 中的项目右侧添加一个带有小图标的附加按钮 这可以做到吗 如果是这样 怎么办 thanks Clamp 你在这方面成功了吗 我想做同样的事情 但很难让 JButton 响应用户 设置渲染器以显示按钮的过程很顺利 但所有鼠标
  • JAXB 编组器无参数默认构造函数

    我想从 java 库中编组一个 java 对象 当使用 JAXB marschaller 编组 java 对象时 我遇到了一个问题 A 类没有无参数默认构造函数 我使用Java Decompiler来检查类的实现 它是这样的 public
  • 在循环中按名称访问变量

    我正在开发一个 Android 项目 并且有很多可绘制对象 这些绘图的名称都类似于icon 0 png icon 1 png icon 100 png 我想将这些可绘制对象的所有资源 ID 添加到整数 ArrayList 中 对于那些不了解
  • Android项目中使用java获取电脑的IP地址

    我在用ksoap2 android http code google com p ksoap2 android 我需要使用java获取IP地址 这样我就不必每次都手动输入它 我所说的 IP 地址是指 例如 如果我这样做ipconfig使用命
  • Java 中处理异步响应的设计模式

    我读过类似问答的答案 如何在 JAVA 中创建异步 HTTP 请求 https stackoverflow com questions 3142915 how do you create an asynchronous http reque
  • 使用自定义比较器在 Java 中创建 SortedMap

    我想创建一个TreeMap在 Java 中具有自定义排序顺序 排序后的键是字符串 需要根据第二个字符进行排序 这些值也是字符串 示例地图 Za FOO Ab Bar 您可以像这样使用自定义比较器 Comparator
  • 如何使用注释处理 Hibernate 和 Spring 中的连接查询?

    我正在使用 Spring 和 Hibernate 以及 MySQL 开发应用程序 我是 Hibernate 新手 完成了基本任务 现在我需要在选择查询中应用联接以使用注释从多个表中获取数据 我已经搜索过但仍然没有任何想法 这是我的数据库表和
  • @Embeddable 中的 @GenerateValue

    我已将实体的 id 分离到一个单独的 Embeddable 类中 该实体如下 Entity Table name users public class Users EmbeddedId private Users pk id private
  • 应用程序关闭时单击 Firebase 通知后打开特定活动/片段

    我知道这个问题似乎重复 但根据我的要求 我在网上搜索了很多帖子 但没有任何对我有用 我的要求 我正在使用 Firebase 来获取推送通知 当应用程序打开时意味着一切正常 但我的问题是 如果有任何推送通知出现 应用程序处于后台 关闭意味着我

随机推荐

  • getBooleanExtra() 仅使用默认参数,而不使用 putExtra() 传递的参数

    这是代码 在这一部分中 answerIsTrue变量应该初始化为 true 它正确地执行了 我调试和检查 并且也正确地传递到 putExtra 再次 我调试和检查 mCheatButton setOnClickListener new Vi
  • 如何利用纬度/经度数据进行有效的范围搜索+计数?

    我正在处理由纬度 经度对表示的大量点 这些点不一定是唯一的 该集中可能有多个点位于同一位置 这些点存储在数据库中 我需要做的是找出一种有效执行搜索的方法 以获取任意点给定半径 例如 25 英里 内的点数 计数不需要 100 准确 更重要的是
  • 如何编写全屏 Linux 控制台应用程序/脚本?

    我什至很难用谷歌搜索这个 因为我不知道正确的关键字 一些命令行应用程序 例如 vi 和 less 接管整个控制台屏幕并向用户呈现交互式界面 退出此类应用程序后 屏幕将返回到应用程序启动之前的状态 我想编写一个以这种方式运行的程序 但同样 我
  • 在半尺寸父控制器中呈现模态视图控制器

    我试图在大小为一半父视图控制器的其他视图控制器上呈现模态视图控制器 但它始终以全屏视图显示 我在故事板中创建了具有固定帧大小的自由大小的视图控制器 320 250 var storyboard UIStoryboard name Main
  • 如何确定旅行商问题的起点和终点?

    我有一个求解器可以解决正常的对称 TSP 问题 该解决方案意味着经过所有节点的最短路径 并且不限制哪些节点是路径中的第一个和最后一个节点 有没有办法转化问题 保证一个特定的节点作为起始节点 另一个节点作为结束节点 一种方法是将 I 非常大的
  • 在 Dask DataFrame.apply() 上,在处理实际行之前接收 n 行值 1

    在下面的代码片段中 我希望日志打印数字 0 4 我知道数字可能不是按这个顺序 因为任务将被分解为多个并行操作 代码片段 from dask import dataframe as dd import numpy as np import p
  • 使用 Hyper 显示响应正文仅显示正文的大小

    我尝试使用 Hyper 将 URL 的内容 正文 显示为文本 extern crate hyper use hyper client Client use std io Read fn main let client Client new
  • PostgreSQL 将数据库存储在哪里?

    PostgreSQL 数据库的文件存储在哪里 要查看数据目录所在的位置 请使用此查询 show data directory 要查看所有运行时参数 请使用 show all 您可以创建表空间来在文件系统的其他部分存储数据库对象 要查看可能不
  • 删除 Fetch API 默认超时

    我使用 Google Chrome 或 Mozilla Firefox 的本机获取向我的服务器发送查询 fetch url method POST body formData credentials include 我设置了一个服务器在 3
  • 在 ElasticSearch 中返回部分嵌套文档

    我想搜索嵌套文档数组并仅返回符合特定条件的文档 映射示例如下 book properties title type string chapters type nested properties title type string lengt
  • angularjs:只允许在文本框中输入数字

    在 AngularJS 中 是否有任何可用功能只允许在文本框中输入数字like 此代码显示了如何防止输入非数字符号的示例 angular module app directive onlyDigits function return res
  • 问:回调的调用顺序是否与注册的顺序相同?

    我正在使用Q承诺图书馆 我的代码依赖于这样一个事实 单个 Promise 的回调按照注册的顺序执行 http jsfiddle net HgYtK 1 var deferred Q defer var promise deferred pr
  • android:name 中的前导点真的需要吗? [复制]

    这个问题在这里已经有答案了 可能的重复 注册活动时的 点 是什么意思 在所有 Android 示例中 活动 服务等名称均以点开头
  • 查看设备方向是否已锁定(检测是否启用/禁用自动旋转)

    如何查明设备的屏幕方向是否已锁定 我正在使用 OrientationEventListener 来触发我的应用程序内的一些操作 如果用户锁定了屏幕 我想禁用这些操作 我知道我通常可以这样定位 但如何找出这个锁定方向 int orientat
  • 神秘的形式(function(x){})$x

    物体是什么formals function x x 它存在于函数的形式中 绑定到没有默认值的参数 还有其他方法来引用这个奇怪的对象吗 除了表示空函数参数之外 它还有其他作用吗 以下是可以在控制台中检查的一些属性 gt is formals
  • Android 和桌面上 Java 下的 SVG 处理

    我正在尝试编写一个基于 XML 文件生成 SVG 图像的 Java 应用程序 该应用程序还应该能够显示 SVG 文件 我的应用程序应该在 Android 平台和台式电脑上运行 我读到过有关 Swing Batik 的内容 但据我所知 它在
  • Objective-C 中自定义对象的分组

    我有 Person 类的自定义对象数组 Person NSObject NSString firstName NSString lastName NSString age NSMutableArray personsArray NSMuta
  • #在C中定义一个元组

    我希望能够定义一个元组来表示其他宏所需的参数 我认为展示我想要的最好方法是展示一个例子 include
  • 如何将 InMemoryUploadedFile 对象复制到磁盘

    我试图捕获通过表单发送的文件 并在保存之前对其执行一些操作 所以我需要在临时目录中创建该文件的副本 但我不知道如何访问它 Shutil 的函数无法复制该文件 因为没有该文件的路径 那么有没有办法以其他方式执行此操作 我的代码 image f
  • 在 Android 上使用客户端/服务器证书进行双向身份验证 SSL 套接字

    我正在开发一个需要客户端和服务器证书身份验证的 Android 应用程序 我创建了一个 SSLClient 类 该类在常规桌面 Java SE 6 上运行良好 我已将其移至我的 Android 项目中 但收到以下错误 未找到 KeyStor