Android本地服务器NanoHttpd配置Https双向认证

news2024/11/17 3:57:55

一、 了解数字证书

在HTTPS的传输过程中,有一个非常关键的角色——数字证书,那什么是数字证书?又有什么作用呢?

所谓数字证书,是一种用于电脑的身份识别机制。由数字证书颁发机构(CA)对使用私钥创建的签名请求文件做的签名(盖章),表示CA结构对证书持有者的认可。数字证书拥有以下几个优点:

使用数字证书能够提高用户的可信度 数字证书中的公钥,能够与服务端的私钥配对使用,实现数据传输过程中的加密和解密

在证认使用者身份期间,使用者的敏感个人数据并不会被传输至证书持有者的网络系统上 X.509证书包含三个文件:keycsrcrt

key【密钥/私钥 Private Key】是服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密
csr【证书认证签名请求 Certificate signing request】是证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名
crt【证书 Certificate】是由证书颁发机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息。
备注:在密码学中,X.509是一个标准,规范了公开秘钥认证、证书吊销列表、授权凭证、凭证路径验证算法等。

此外还会涉及到不同平台的相关不同格式证书,这些证书可以在不同格式间相互转换

  • cer【俗称数字证书】,目的就是用于存储公钥证书,任何人都可以获取这个文件
  • pem -【Privacy Enhanced Mail】打开看⽂本格式,以"-----BEGIN…"开头, "-----END…"结尾,内容是BASE64编码。Apache和*NIX服务器偏向于使⽤这种编码格式.
  • p12 【是PKCS12的缩写】同样是一个存储私钥的证书库,由.jks文件导出的,用户在PC平台安装,用于标示用户的身份
  • bksAndroid平台支持的证书库格式】由于Android平台不识别.keystore__和.jks**格式的证书库文件,因此Android平台引入一种的证书库格式,BKS。
  • jks 【数字证书库】。JKS里有KeyEntry和CertEntry,在库里的每个Entry都是靠别名(alias)来识别的。

二、生成数字证书

数字证书的生成需要使用到openssl,这里只是简单介绍一下,详细咨询请自行查阅相关资料

  1. 安装 openssl
yum install openssl -y
  1. 常用命令
-req 产生证书签发申请命令
-newkey 生成新私钥 rsa:4096 生成秘钥位数
-nodes 表示私钥不加密
-sha256 使用SHA-2哈希算法
-keyout 将新创建的私钥写入的文件名
-x509 签发X.509格式证书命令。X.509是最通用的一种签名证书格式。
-out 指定要写入的输出文件名
-subj 指定用户信息
-days 有效期(3650表示十年)
-storepass 设置密码
-import 导入证书
-importkeystore 导入已有证书(转换格式)
-export 导出证书
-genkeypair 生成相关证书
-genkey 生成密钥

三、双向认证

请添加图片描述

1. 单向认证

操作系统或者浏览器一般都内置了一堆相关CA的证书,所以可以直接访问;若是自签发的,浏览器会提示该证书不受信任。适用场景是站点访问,非高机密数据传输。

https一般是单向认证,通常由客户端来校验服务器证书。

2. 双向认证

双向认证,即不仅客户端校验服务器,服务器也校验客户端,通常用于高机密数据传输。

客户端持有服务端的公钥证书,并持有自己的私钥,服务端持有客户的公钥证书,并持有自己私钥。

建立连接的时候,客户端利用服务端的公钥证书来验证服务器是否上是目标服务器;服务端利用客户端的公钥来验证客户端是否是目标客户端。(请参考RSA非对称加密以及HASH校验算法)

服务端给客户端发送数据时,需要将服务端的证书发给客户端验证,验证通过才运行发送数据,同样,客户端请求服务器数据时,也需要将自己的证书发给服务端验证,通过才允许执行请求。

四、生成自签名私有证书

1. 单向认证(只需要服务端证书)

1、生成服务端密钥(配置了jdk的环境变量即可用keytool命令)
命令

keytool -genkey -keystore server_ks.jks -storepass server_password -keyalg RSA -keypass server_password

结果:会生成server_ks.jks密钥文件
操作:将生成的server_ks.jks密钥文件配置到服务端

2、生成服务端证书
命令

keytool -export -keystore server_ks.jks -storepass server_password -file server.cer

结果:会生成server.cer证书文件
操作:将生成的server.cer证书文件配置到客户端

2. 双向认证(还需要客户端证书,和信任证书)

1、生成客户端密钥
命令

keytool -genkey -keystore client_ks.jks -storepass client_password -keyalg RSA -keypass client_password

结果:会生成client_ks.jks密钥文件
操作:将生成的client_ks.jks密钥文件配置到客户端

2、生成客户端证书
命令

keytool -export -keystore client_ks.jks -storepass client_password -file client.cer

结果:会生成client.cer证书文件

下面重点:证书交换
将客户端证书导入服务端keystore中,再将服务端证书导入客户端keystore中, 一个keystore可以导入多个证书,生成证书列表。

3、将server端证书添加到serverTrust_ks.jks文件中
命令

keytool -import -keystore serverTrust_ks.jks -storepass client -file server.cer

结果:会生成serverTrust_ks.jks密钥文件
操作:将生成的serverTrust_ks.jks密钥文件配置到客户端

4、将client端证书添加到clientTrust_ks.jks文件中
命令

keytool -import -keystore clientTrust_ks.jks -storepass server -file client.cer

结果:会生成clientTrust_ks.jks密钥文件
操作:将生成的clientTrust_ks.jks密钥文件配置到服务端

有些人可能有疑问,为什么有些博主操作只需要共用一个证书文件,而这里需要生成二个Trust文件?

因为有时客户端可能需要访问多个服务器,而每个服务器的证书都不相同,因此客户端需要制作一个truststore来存储受信任的服务器的证书列表。因此为了规范创建一个truststore.jks用于存储受信任的服务器证书,创建一个client.jks来存储客户端自己的私钥。对于只涉及与一个服务端进行双向认证的应用,将server.cer导入到client.jks中也可

5将证书转换为Android平台支持的BKS格式
服务端信任证书jks转bks

keytool -importkeystore -srckeystore serverTrust_ks.jks -destkeystore serverTrust_ks.bks -srcstoretype JKS -deststoretype BKS -srcstorepass client -deststorepass client -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-154.jar

客户端信任证书jks转bks

keytool -importkeystore -srckeystore clientTrust_ks.jks -destkeystore clientTrust_ks.bks -srcstoretype JKS -deststoretype BKS -srcstorepass server -deststorepass server -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-154.jar

服务端证书jks转bks

keytool -importkeystore -srckeystore server_ks.jks -destkeystore server_ks.bks -srcstoretype JKS -deststoretype BKS -srcstorepass server_password -deststorepass server_password -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-154.jar

客户端证书jks转bks

keytool -importkeystore -srckeystore client_ks.jks -destkeystore client_ks.bks -srcstoretype JKS -deststoretype BKS -srcstorepass client_password -deststorepass client_password -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-154.jar

做完这些操作,目录下应该有这些文件
在这里插入图片描述

至此准备工作做完,开始coding

五、Android项目配置

  1. 服务端代码(如果是使用NanoHttpd,可以copy一份SimpleWebServer测试)
public void init() {
	//生成秘钥的manager
      KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
      //加载信任的证书
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
      //加载秘钥
      KeyStore keyStoreKey = KeyStore.getInstance("BKS");
      KeyStore keyStoreTrust = KeyStore.getInstance("BKS");

      //这里要把相关证书文件放到res-raw文件夹下,(放到assets下也行,读取方法不一样)
      InputStream keyStream = AppContext.getApp().getResources().openRawResource(R.raw.server_ks);
      InputStream trustStream = AppContext.getApp().getResources().openRawResource(R.raw.client_trust_ks);
      keyStoreKey.load(keyStream, "server_password".toCharArray());
      keyStoreTrust.load(trustStream, "server".toCharArray());

      //秘钥初始化
      keyManagerFactory.init(keyStoreKey, "server_password".toCharArray());
      trustManagerFactory.init(keyStoreTrust);

      //初始化SSLContext
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
      SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

//设置ServerSocketFactory,需重写相关方法
setServerSocketFactory(new DoubleSecureServerSocketFactory(sslServerSocketFactory, null));
}

//如果是使用NanoHttpd,需要重写SecureServerSocketFactory,将ss.setNeedClientAuth(false);中的值改为true,这样就开启了服务端校验客户端功能。

 /**
   * Creates a new SSLServerSocket
   */
  public static class DoubleSecureServerSocketFactory implements ServerSocketFactory {

    private SSLServerSocketFactory sslServerSocketFactory;

    private String[] sslProtocols;

    public DoubleSecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory,
        String[] sslProtocols) {
      this.sslServerSocketFactory = sslServerSocketFactory;
      this.sslProtocols = sslProtocols;
    }

    @Override
    public ServerSocket create() throws IOException {
      SSLServerSocket ss = null;
      ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket();
      if (this.sslProtocols != null) {
        ss.setEnabledProtocols(this.sslProtocols);
      } else {
        ss.setEnabledProtocols(ss.getSupportedProtocols());
      }
      ss.setUseClientMode(false);
      ss.setWantClientAuth(false);
      ss.setNeedClientAuth(true);
      return ss;
    }
  }
  1. 客户端代码
    以okhttp为例:
  /**
   * 关联Https请求验证证书
   */
  public static SSLSocketFactory getSSLContext(Context context) {

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
      //生成信任证书Manager,默认系统会信任CA机构颁发的证书,自定的证书需要手动的加载
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");

      KeyStore keyStoreKey = KeyStore.getInstance("BKS");
      KeyStore keyStoreTrust = KeyStore.getInstance("BKS");

     InputStream keyStream= AppContext.getApp().getResources().openRawResource(R.raw.client_ks);
     InputStream trustStream= AppContext.getApp().getResources().openRawResource(R.raw.server_trust_ks);
      //加载client端密钥
      keyStoreKey.load(keyStream, "client_password".toCharArray());
      //信任证书
      keyStoreTrust.load(trustStream, "client".toCharArray());
      keyManagerFactory.init(keyStoreKey, "client_password".toCharArray());
      trustManagerFactory.init(keyStoreTrust);

      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null/* new SecureRandom()*/);
      SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            return sslSocketFactory;
    } catch (CertificateException e) {
      e.printStackTrace();
    } catch (KeyStoreException e) {
      e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (KeyManagementException e) {
      e.printStackTrace();
    } catch (UnrecoverableKeyException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

val sslSocketFactory = HttpsUtils.getSSLContext(context)
    val okHttpClient = OkHttpClient.Builder()
      // .connectionSpecs(Collections.singletonList(spec))
      .sslSocketFactory(sslSocketFactory, HttpsTrustManager() )
      .hostnameVerifier(object : HostnameVerifier {
        override fun verify(
          hostname: String?,
          session: SSLSession?
        ): Boolean {
          //这里可以做域名检验
          return true
        }
      })
      .build()

    val request: Request = Request.Builder().url(url).build()
    okHttpClient.newCall(request).enqueue(object : Callback {
      override fun onResponse(
        call: Call,
        response: Response
      ) {
        val body = response.body
        if (response.isSuccessful) {
          Log.e("request", "success:${body?.string()}")
        } else {
          Log.e("request", "error,statusCode=${response?.code},body=${body?.string()}")
        }
      }

      override fun onFailure(
        call: Call,
        e: IOException
      ) {
        Log.e("request", "error:${e.toString()}")
        e.printStackTrace()
      }
    })

完整代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超实用的办公小技巧,上班族必看

技巧一:使用“PS”来合并 这个方法大家是不是觉得有点出乎意料呢?虽然PS是一个图像处理的工具,但它总是有些我们想不到的功能。下面就给大家介绍一下究竟要怎么利用它来合并PDF文件。 使用感受:的确是可以合并多个PDF文件的&…

《啊哈算法》第二章栈,队列,链表(解析+代码)

从无到有学算法 吾日三省吾身,今天有写代码乎?🙄 🙄抠门渣男语录:我的果汁分你一半(月亮弯弯 绵绵绵绵缠缠) - 李金源 - 单曲 - 网易云音乐 千年之后的你在哪里:星月神话 - 我觉得我还能再吃…

CACHE 概念

CACHE 概念 CPU 读写指令或者数据,可能直接从寄存器查取,也可能经过 TLB ,经过 MMU,经过高速缓存,经过内存,经过外部存储器。这里面有一个 Cache 的概念,想多了解下了,于是有这这一…

【自定义类型】带你走进结构体、枚举、联合

欢迎来到小王学代码的博客 在字符型函数之后,我们接下来要学习的是自定义类型中的结构体、枚举、联合 目录 前言 一、结构体 1.1结构体的声明 1.2 特殊声明和结构自引用 1.4结构体的自引用 1.5结构体变量的定义和初始化 1.6 结构体内存对齐 1.7修改默认对齐…

【杂烩】Latex中的一些技巧备忘录

1. subfigure 和 minipage 环境的运用 首先是多张图组合到一起,左侧和下方备注列标题和行标题。 使用的时候需要的包: minipage不需要 \usepackage{subfigure} \usepackage{graphicx}代码1: \begin{figure*}[htbp]\centering%第一行图片展…

微信小程序怎么实现拍照功能,以及授权,拍完照保存到本地。

写微信小程序就是要不停的翻阅官方文档查阅所需要的需求。API的使用说明wx.getSetting 获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限 scope.camera相机wx.authorize 提前向用户发起授权请求。调用后会立刻弹窗询问用户是否同意授权小程序使用某项功能或获…

复盘!!指针 ,地址 ,数组之间的联系

文章目录前言一、直接看题二、直接上代码总结前言 工作摸鱼 闲来无事 一、直接看题 二、直接上代码 所以,这个题目答案是5. 我来盘给你:int a[5] {1,2,3,4,5}; 1数组名本身就是表示数组首元素地址 2对数组名取地址:表示的是整个数组的地址…

Android抓包Charles入门教程

Android抓包工具有很多,有轻量的如httpCanary直接安装在手机上通过代理进行抓包,有功能强大的WireShark PC 端的抓包工具(也要设置代理啦),还有老牌抓包工具Finder和Charles以及mitmproxy 。 手机上的抓包工具远离基本差不多&…

MAC(m1)-CentOS8 Docker设置MySQL数据持久化

Docker安装部署Mysql8的过程(以作数据持久化)_docker_脚本之家 Docker安装Mysql并映射_小小小果子的博客-CSDN博客_docker mysql 映射 Docker中安装mysql并把配置等挂载在外面(开启多个mysql)_遗忘de神话的博客-CSDN博客_dock中安装了mysql,还能在外部再安装一个mysql吗 mys…

Windows实时运动控制软核(五):LOCAL高速接口测试之VC6.0

今天,正运动小助手给大家分享一下MotionRT7的安装和使用,以及使用VC6.0对MotionRT7开发的前期准备。 01 MotionRT7简介 MotionRT7是深圳市正运动技术推出的跨平台运动控制实时内核,也是国内首家完全自主自研, 自主可控的Windows…

电脑怎么录制屏幕视频,3种方法,轻松弄懂

在日常生活当中,电脑录屏的使用场景也变得越来越多了起来,网课录制,微课录制,直播录制,会议录制等等。那么问题来了,电脑怎么录制屏幕视频呢?今天小编就向大家分享3个电脑录制屏幕视频的方法&am…

osg fbo(一),生成颜色缓冲区图片

由于工作需要&#xff0c;重新捡了下shader。很明显&#xff0c;fbo是重中之重。好记性不如烂笔头&#xff0c;先记录下 1&#xff0c;生成一个颜色纹理(为了省事&#xff0c;可以将纹理宽高屏幕宽高) osg::ref_ptr<osg::Texture2D> tex createFloatRectangleTexture(…

Linux安装Docker详细教程

文章目录Docker架构环境说明安装步骤阿里云镜像加速Docker底层原理Docker架构 镜像&#xff08;image&#xff09;: Docker 镜像&#xff08;Image&#xff09;就是一个只读的模板。镜像可以用来创建 Docker 容器&#xff0c;一个镜像可以创建很多容器。 就好似 Java 中的 类和…

Day857.高性能限流器Guava RateLimiter -Java 并发编程实战

高性能限流器Guava RateLimiter Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于高性能限流器Guava RateLimiter的内容。 Guava RateLimiter 是如何解决高并发场景下的限流问题的。 Guava 是 Google 开源的 Java 类库&#xff0c;提供了一个工具类 RateLimiter。先…

2022智能制造世界与中国十大科技进展内容小结

2022智能制造世界与中国十大科技进展内容小结1. 全断面隧道掘进装备行业工业互联网平台2. 大型复杂构件机器人原位高效高质量铣削加工技术及装备3. 西门子SNC—原生数字化工厂4. 亚马逊 数字孪生服务IoT TwinMaker5. Nvidia 新一代智能移动机器人仿真平台6. 北京奔驰的基于大数…

Excel特殊符号及用法

一、公式中常用符号 &#xff1a; 表示一个单元格区域&#xff0c;如A1:B10 - * / 加减乘除运算符> 大于号> 大于等于号< 小于号< 小于等于号<> 不等于号&#xff0c;如IF(A1<>"销售部",,)^数字 乘方运算&#xff0c;如…

第二章Mybatis进阶操作学习

文章目录特殊SQL的执行模糊查询批量操作动态设置表名添加功能获取自增的主键自定义映射resultMap处理字段和属性的映射关系在SQL语句中使用别名使用核心配置文件中的驼峰对应方法使用resultMap自定义映射处理一对一映射处理级联方式处理association标签分步查询分步查询的优点&…

【网安神器篇】——Sqlmap详解

作者名&#xff1a;白昼安全主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右铭&a…

C语言 自定义类型 之 【联合体(共用体)】

文章目录前言联合体(UNION)类型的定义联合体的特点联合体大小的计算联合体使用的典型例题 — 判断机器的大小端写在最后前言 联合体&#xff08;union&#xff09;在C语言中是不常用的&#xff0c;不过存在就必然有其有用之处&#xff0c;这里不深入探讨联合体用在何处有什么用…

R语言实现牛顿插值

文章目录4 差商与牛顿插值差商牛顿插值4 差商与牛顿插值 差商 如果采取间隔不等的采样&#xff0c;差商会变得稍显复杂&#xff0c;对于x0,x1,…,xnx_0,x_1,\ldots,x_nx0​,x1​,…,xn​&#xff0c;若与y0,y1,…,yny_0,y_1,\ldots,y_ny0​,y1​,…,yn​通过映射fff一一对应&…