OpenHarmony开发者论坛

标题: OpenHarmony三方库ImageKnife中新增https自定义证书功能 [打印本页]

作者: jinzhao    时间: 2024-10-24 17:08
标题: OpenHarmony三方库ImageKnife中新增https自定义证书功能
[md]# 一、https简介

HTTPS协议是基于HTTP进行通信的,同时增加了SSL(Secure Sockets Layer)/TLS(Transport Layer Security)加密层,为数据的安全传输提供了强有力的保障。简单来说,HTTPS的工作流程如下:

1. **客户端发起连接请求**:客户端(通常是浏览器)向服务器发送一个安全连接请求,使用HTTPS的URL或点击HTTPS链接触发。
2. **服务器证书发送**:服务器收到请求后,将自己的数字证书发送给客户端。证书中包含了服务器的公钥、数字签名和其他相关信息。
3. **客户端验证证书**:浏览器接收到服务器证书后,会进行一系列的验证步骤,包括验证证书是否由受信任的证书颁发机构签发,以及证书中的域名是否与目标服务器的域名相匹配。如果验证通过,客户端可以确定所连接的服务器是可信的。
4. **密钥协商**:一旦服务器证书被验证通过,客户端会生成一个随机的对称密钥,用于后续的数据加密和解密。该对称密钥通过服务器证书中的公钥进行加密,并发送给服务器。
5. **通信加密**:服务器使用其私钥解密客户端发送的对称密钥,并与客户端之间建立起一个加密的安全通道。从此之后,客户端和服务器之间的数据传输都会在此加密通道中进行,保证数据的机密性。
6. **安全数据传输**:在建立了安全通道后,客户端和服务器可以安全地传输数据了。数据在发送前,会使用对称密钥对数据进行加密,然后在接收端使用相同的对称密钥解密。
7. **完整性检查**:为了确保数据在传输过程中没有被篡改,HTTPS使用消息摘要函数进行完整性检查。接收方会对接收到的数据进行校验,并比对校验结果与发送方计算的结果是否相同。

服务器端在使用 HTTPS 前,需要向CA(Certification Authority)机构申领一份数字证书,数字证书里含有证书申请者信息、公钥信息等。客户端事先内置许多可信CA机构的证书,其中包含了CA的公钥和相关信息。当客户端收到服务器端的证书后,使用CA公钥进行解密,验证证书是否合法,从而能够有效防止中间人攻击,保证了HTTPS通信的安全性。

# 二、三方库ImageKnife简介

[ImageKnife](https://gitee.com/openharmony-tpc/ImageKnife) 是专门为OpenHarmony打造的一款图像加载缓存库,致力于更高效、更轻便、更简单。

该项目为参考开源库 [Glide](https://gitee.com/link?target=ht ... %2Fbumptech%2Fglide) 进行OpenHarmony的自研版本:

- 支持自定义内存缓存策略,支持设置内存缓存的大小(默认LRU策略)。
- 支持磁盘二级缓存,对于下载图片会保存一份至磁盘当中。
- 支持自定义实现图片获取/网络下载
- 支持监听网络下载回调进度
- 继承Image的能力,支持option传入border,设置边框,圆角
- 继承Image的能力,支持option传入objectFit设置图片缩放,包括objectFit为auto时根据图片自适应高度
- 支持通过设置transform缩放图片
- 并发请求数量,支持请求排队队列的优先级
- 支持生命周期已销毁的图片,不再发起请求
- 自定义缓存key
- 自定义http网络请求头
- 支持writeCacheStrategy控制缓存的存入策略(只存入内存或文件缓存)
- 支持preLoadCache预加载图片
- 支持onlyRetrieveFromCache仅用缓存加载
- 支持使用一个或多个图片变换,如模糊,高亮等

# 三、自定义证书功能实现

## 3.1  需求描述

作为图像加载缓存库,ImageKnife提供了加载来自网络的图片的功能。在请求图片资源时,如果使用的是https协议,则会使用系统中内置的CA证书来验证服务器证书的可靠性。

而在进行某些系统测试时,服务器端可能并未配置被CA机构认可的证书,此时若仍使用系统内置CA证书来进行验证,系统将认为服务器端证书不可信,导致连接失败。

本文通过在ImageKnife中新增自定义证书的功能,使系统使用自定义的CA证书验证服务器可信性,从而解决了上述场景下的问题。

## 3.2  功能开发

通过阅读ImageKnife的示例代码及其文档可知,ImageKnife组件提供了 `imageKnifeOption`接口,用于控制图片加载过程中的各种选项。

通过进一步深入阅读ImageKnife源码,可以发现,ImageKnife获取图片资源的核心操作是通过文件 `library/src/main/ets/ImageKnifeLoader.ets`中的函数 `getImageArrayBuffer()`实现的。在该函数中,首先通过传入属于文件 `library/src/main/ets/model/ImageKnifeData.ets`中定义的类 `RequestJobRequest`的对象 `request`来传递请求信息,接着利用 `@ohos.net.http`所提供的接口实现对网络资源的请求。

查阅OpenHarmony API的官方文档[@ohos.net.http (数据请求)](https://docs.openharmony.cn/page ... it/js-apis-http.md#完整示例),通过该API进行http请求时,有一个可选的 `caPath`字段可用来填写自定义证书的路径,若该字段未被填写,则默认使用系统预设CA证书。

因此,为了实现自定义证书的功能,需要在 `imageKnifeOption`接口中新增指定自定义证书路径的字段,并在自定义类 `RequestJobRequest`中也增加相关信息,在函数 `getImageArrayBuffer()`调用 `@ohos.net.http`接口时,将自定义证书的路径传入 `caPath`字段。对代码做具体修改如下:

- library/src/main/ets/model/ImageKnifeOption.ets:

  ```typescript
  ...

  interface ImageOption {

   ...

    // 自定义证书路径
    caPath?: string
  }

  @ObservedV2
  export class ImageKnifeOption {

    ...

    caPath?: string
    constructor(option?:ImageOption) {

      ...

      this.caPath = option?.caPath
    }
  }

  ...
  ```
- library/src/main/ets/model/ImageKnifeData.ets:

  ```typescript
  ...

  export interface RequestJobRequest {

    ...

    caPath?: string,
  }
  ```
- library/src/main/ets/ImageKnifeDispatcher.ets:

  ```typescript
  ...

  export class ImageKnifeDispatcher {

      ...

      let request: RequestJobRequest = {

        ...

        caPath: currentRequest.imageKnifeOption.caPath,
      }

      ...

    }

  ...
  }
  ```
- library/src/main/ets/ImageKnifeLoader.ets:

  ```typescript
  ...

  export class ImageKnifeLoader {
    static async getImageArrayBuffer(request: RequestJobRequest, requestList: List<ImageKnifeRequestWithSource> | undefined,fileKey:string): Promise<ArrayBuffer> {

              ...

              if(request.caPath === undefined) {  //采用默认证书
                let promise = httpRequest.requestInStream(request.src, {
                  header: headerObj,
                  method: http.RequestMethod.GET,
                  expectDataType: http.HttpDataType.ARRAY_BUFFER,
                  connectTimeout: 60000,
                  readTimeout: 0,
                });
                await promise.then((data: number) => {
                  if (data == 200 || data == 206 || data == 204) {
                    resBuf = combineArrayBuffers(arrayBuffers)
                  } else {
                    throw new Error("HttpDownloadClient has error, http code =" + JSON.stringify(data))
                  }
                }).catch((err: Error) => {
                  throw new Error("HttpDownloadClient download ERROR : err = " + JSON.stringify(err))
                });
                LogUtil.log("HttpDownloadClient.end:" + request.src)
              } else {  //采用自定义证书
                let promise = httpRequest.requestInStream(request.src, {
                  header: headerObj,
                  method: http.RequestMethod.GET,
                  expectDataType: http.HttpDataType.ARRAY_BUFFER,
                  connectTimeout: 60000,
                  readTimeout: 0,
                  caPath: request.caPath,
                });
                await promise.then((data: number) => {
                  if (data == 200 || data == 206 || data == 204) {
                    resBuf = combineArrayBuffers(arrayBuffers)
                  } else {
                    throw new Error("HttpDownloadClient has error, http code =" + JSON.stringify(data))
                  }
                }).catch((err: Error) => {
                  throw new Error("HttpDownloadClient download ERROR : err = " + JSON.stringify(err))
                });
                LogUtil.log("HttpDownloadClient.end:" + request.src)
              }

              ...

    }  
  }
  ```

# 四、测试验证

为了验证上文所开发的自定义证书功能是否有效,本文利用XAMPP搭建了一个简易的服务器环境,通过自签名的方式获得不受系统预置CA认可的服务器端证书,以简单验证调用ImageKnife的应用是否能够使用自定义证书正常获取服务器内图片资源。

> 注:本文测试验证时所使用的服务器环境为Windows 10,运行应用的客户端为RK3568开发板,其搭载OpenHarmony 5.0.0 Release系统。

## 4.1  安装XAMPP

XAMPP是一款优秀的Apache+MYSQL+PHP服务器系统开发套件,其完全免费,遵循GNU通用公众许可,同时支持Zend Optimizer,支持插件安装。在[XAMPP官网](https://www.apachefriends.org/index.html)下载安装包进行安装。安装完成后运行xampp-control.exe打开控制面板,点击Apache后对应的start按钮,启动Apache服务器,如下图所示:

![image-20241024114721825.png](https://forums-obs.openharmony.c ... qopoqqde32y359q.png "image-20241024114721825.png")

当Apache服务器成功启动后,在浏览器中输入localhost或127.0.0.1,即可访问到默认的欢迎界面,如下:

![image-20241024140342598.png](https://forums-obs.openharmony.c ... 75l5l44oo7rdnjx.png "image-20241024140342598.png")

## 4.2  设置自签名证书

1. 配置测试域名。生成自签名的证书,需要一个测试用的域名,比如 `mysite1.com`, 访问服务器网站的时候可以通过 `https://mysite1.com`来访问。首先在cmd中通过命令 `ipconfig`查询到主机的ip地址,接着修改 `C:\Windows\System32\drivers\etc\hosts`文件,将测试域名指向该ip。在hosts文件中增加以下代码(其中 `xx.xx.xx.xx`为 `ipconfig`命令所查询到的ipv4地址):

   ```
   xx.xx.xx.xx mysite1.com
   xx.xx.xx.xx www.mysite1.com
   ```
2. 在 `XAMPP安装目录\apache\conf\openssl.cnf`里启用 `v3_req`扩展和增加一个关键属性配置 `subjectAltName`。

   - 启用 `v3_req`扩展, 搜索文件里的内容 `v3_req`, 把 `#`注释去掉。

     ```
      req_extensions = v3_req # The extensions to add to a certificate request   
     ```
   - 在 `[ v3_req ]`下增加以下的配置,注意,在 `DNS.1`中增加域名配置,如果只需支持某个特定的二级域名,那么可以用该二级域名代替 `*`符号, 比如 `shop.mysite1.com`。

     ```
     basicConstraints = CA:FALSE
     keyUsage = nonRepudiation, digitalSignature, keyEncipherment

     # 新增下列代码:
     subjectAltName = @alt_names

     [ alt_names ]
     DNS.1 = *.mysite1.com
     DNS.2 = mysite1.com
     ```
3. 使用openssl工具生成私钥、公钥和证书。打开cmd,进入 `XAMPP安装目录\apache\bin`,输入下列命令。

   - 生成私钥 `ca.key`:

     ```
     openssl genrsa -out ca.key 2048
     ```
   - 生成服务端公钥 `server.key`:

     ```
     openssl rsa -in ca.key -out server.key
     ```
   - 生成服务端证书 `server.crt`:

     ```
     openssl req -new -subj "/CN=*.mysite1.com" -x509 -days 3650 -key server.key -out server.crt -config ../conf/openssl.cnf -extensions v3_req
     ```
4. 把生成的 `server.crt`和 `servier.key`分别复制覆盖到 `XAMPP安装目录\apache\conf\ssl.crt`和 `XAMPP安装目录\apache\conf\ssl.key`目录下,并配置使用这两个文件。在 `XAMPP安装目录\apache\conf\extra\httpd-ssl.conf`文件中,找到 `VirtualHost`部分,在其中增加启用 `SSLEngine`,并配置使用上述两个文件。注意 `ServerName`的值要填测试域名 `mysite1.com`。

   ```
   <VirtualHost _default_:443>
           SSLEngine on
           SSLCertificateFile "conf/ssl.crt/server.crt"
           SSLCertificateKeyFile "conf/ssl.key/server.key"
           ServerName mysite1.com
           ServerAdmin admin@mysite1.com
           ...
   </VirtualHost>
   ```
5. 配置SSL扩展支持。打开 `XAMPP安装目录\apache\conf\httpd.conf`文件。

   - 搜索 `ssl_module`,找到ssl模块配置,确认有关配置信息未被注释掉:

     ```
     # Secure (SSL/TLS) connections
     # Note: The following must must be present to support
     #       starting without SSL on platforms with no /dev/random equivalent
     #       but a statically compiled-in mod_ssl.
     #
     <IfModule ssl_module>
     Include conf/extra/httpd-ssl.conf
     SSLRandomSeed startup builtin
     SSLRandomSeed connect builtin
     </IfModule>
     ```
   - 搜索 `mod_ssl.so`,确认配置信息未被注释掉:

     ```
     LoadModule ssl_module modules/mod_ssl.so
     ```
   - 确认 `SRVROOT`路径配置正确(`D:/Program Files/XAMPP`为本文测试验证过程中XAMPP的安装路径)

     ```
     Define SRVROOT "D:/Program Files/XAMPP/apache"
     ServerRoot "D:/Program Files/XAMPP/apache"
     ```
6. 验证设置是否成功。

   - 双击 `server.crt`,选择安装证书,在证书导入向导中选择存储位置为 `受信任的根证书颁发机构`。
     ![image-20241024150309553.png](https://forums-obs.openharmony.c ... usfybz0kstlk3c5.png "image-20241024150309553.png")
   - 安装好证书之后,需要检查证书是否正确安装.。按下快捷键 `Win+R`, 输入 `certmgr.msc`并回车, 检查 `受信任的根证书颁发机构`中是否有之前安装的证书信息。
     ![image-20241024150654179.png](https://forums-obs.openharmony.c ... h1o4rmw4d71fah4.png "image-20241024150654179.png")
   - 通过XAMPP Control Panel启动Apache服务器,之后通过 `https://mysite1.com`访问测试网站,查看是否为安全连接。
     ![image-20241024151111212.png](https://forums-obs.openharmony.c ... ow1q6lrieto6uii.png "image-20241024151111212.png")

## 4.3  编写测试应用

本文在ImageKnife原有样例代码的基础上进行简单修改,以验证所新增的自定义证书功能。具体修改如下:

- 新增说明字符串。

  - 修改 `entry/src/main/resources/base/element/string.json`为:

    ```typescript
    {
      "string": [

        ...

        {
          "name": "Network_images_with_custom_cert",
          "value": "Network images with custom cert"
        },

        ...

      ]
    }
    ```
  - 修改 `entry/src/main/resources/zh_CN/element/string.json`为

    ```typescript
    {
      "string": [

        ...

        {
          "name": "Network_images_with_custom_cert",
          "value": "网络图片(使用自定义证书)"
        },

        ...

      ]
    }
    ```
- 配置自定义dns。在 `entry/src/main/ets/entryability/EntryAbility.ets`内的 `onWindowStageCreate()`函数中,将测试服务器的ip地址与域名作为自定义dns添加至应用的hosts列表(其中 `xx.xx.xx.xx`为上文中通过命令 `ipconfig`所查询到的ip地址):

  ```typescript
  ...

  import { connection } from '@kit.NetworkKit';

  ...

  export default class EntryAbility extends UIAbility {
    async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {

      ...

      //添加自定义dns以供测试自定义证书
      connection.addCustomDnsRule("www.mysite1.com",["xx.xx.xx.xx"]).then(() => {
        console.info("addCustomDnsRule www.mysite1.com success!");
      }).catch((error: BusinessError) => {
        console.error(JSON.stringify(error));
      })
      connection.addCustomDnsRule("mysite1.com",["xx.xx.xx.xx"]).then(() => {
        console.info("addCustomDnsRule mysite1.com success!");
      }).catch((error: BusinessError) => {
        console.error(JSON.stringify(error));
      })
    }
  ...
  }
  ```
- 添加测试用ImageKnife组件。在 `entry/src/main/ets/pages/SingleImage.ets`中,新增一个ImageKnife组件。将测试网站欢迎页中的XAMPP logo(见下图)作为测试用图片资源,设置该ImageKnife组件的 `loadSrc`为该图片资源网址。
  ![image-20241024154101912.png](https://forums-obs.openharmony.c ... o6lz6vaub1b3rzg.png "image-20241024154101912.png")

  同时设置该组件中的caPath字段为该应用的安装文件路径:

  ```typescript
          // 网络图片(https自定义证书)
          Text($r('app.string.Network_images_with_custom_cert'))
            .fontSize(30)
            .fontWeight(FontWeight.Bold)
          ImageKnifeComponent({
            imageKnifeOption: new ImageKnifeOption({
              loadSrc:"https://www.mysite1.com/dashboard/images/xampp-logo.svg",
              placeholderSrc: $r("app.media.loading"),
              errorholderSrc: $r("app.media.failed"),
              objectFit: ImageFit.Contain,
              progressListenerprogress:number)=>{console.info("ImageKnife:: call back progress = " + progress)},
              caPath: "/data/storage/el1/bundle/ca.pem",
            })
          }).width(100).height(100)
  ```

## 4.4  运行测试

1. 直接运行应用,点击“单个图片使用”按钮,进入到SingleImage.ets所写的页面中

   ![image-20241024155412264.png](https://forums-obs.openharmony.c ... awe1qb0kww33e0q.png "image-20241024155412264.png")

   可以看到由于尚未向caPath路径中上传证书文件,客户端在向服务器发出https请求后,由于无法验证服务器证书的可靠性,从而拒绝了https连接,导致该网络图片资源加载出错。

   ![image-20241024155526241.png](https://forums-obs.openharmony.c ... 0eesmu20ik4z9tw.png "image-20241024155526241.png")
2. 使用hdc向开发板指定路径中上传证书文件。将 `server.crt`重命名为 `ca.pem`并记录其路径(如 `D:\Desktop`)。打开cmd,进入到OpenHarmony sdk目录下的toolchains目录(如 `C:\Users\Administrator\AppData\Local\OpenHarmony\Sdk\12\toolchains`),输入命令将自定义证书上传至开发板:

   ```
   hdc file send D:\Desktop\ca.pem /data/app/el1/bundle/public/com.openharmony.imageknife
   ```
   注意hdc传送文件时使用的是绝对物理路径,测试应用中caPath字段应填写其对应的应用沙箱路径。有关绝对物理路径和应用沙箱路径间的对应关系、向应用沙箱推送文件的具体步骤,可分别参考链接[应用沙箱目录 (openharmony.cn)](https://docs.openharmony.cn/page ... ndbox-directory.md#应用沙箱目录与应用沙箱路径)、[向应用沙箱推送文件 (openharmony.cn)](https://docs.openharmony.cn/page ... e-to-app-sandbox.md)
3. 再次运行测试应用,进入到SingleImage.ets所写的页面中,可以看到测试图片资源被成功加载出来,这说明应用成功使用自定义证书与服务器建立了https连接,验证了本文所新增的自定义https证书功能。

   ![image-20241024163417130.png](https://forums-obs.openharmony.c ... op6j099zp00cbjd.png "image-20241024163417130.png")
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5