• Lv0
    粉丝0

积分4 / 贡献0

提问0答案被采纳0文章2

作者动态

[经验分享] OpenHarmony三方库ImageKnife中新增https自定义证书功能

深开鸿_金昭 显示全部楼层 发表于 前天 17:08

一、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 是专门为OpenHarmony打造的一款图像加载缓存库,致力于更高效、更轻便、更简单。

该项目为参考开源库 Glide 进行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 (数据请求),通过该API进行http请求时,有一个可选的 caPath字段可用来填写自定义证书的路径,若该字段未被填写,则默认使用系统预设CA证书。

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

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

    ...
    
    interface ImageOption {
    
     ...
    
      // 自定义证书路径
      caPath?: string
    }
    
    @ObservedV2
    export class ImageKnifeOption {
    
      ...
    
      caPath?: string
      constructor(option?:ImageOption) {
    
        ...
    
        this.caPath = option?.caPath
      }
    }
    
    ...
  • library/src/main/ets/model/ImageKnifeData.ets:

    ...
    
    export interface RequestJobRequest {
    
      ...
    
      caPath?: string,
    }
  • library/src/main/ets/ImageKnifeDispatcher.ets:

    ...
    
    export class ImageKnifeDispatcher {
    
        ...
    
        let request: RequestJobRequest = {
    
          ...
    
          caPath: currentRequest.imageKnifeOption.caPath,
        }
    
        ...
    
      }
    
    ...
    }
  • library/src/main/ets/ImageKnifeLoader.ets:

    ...
    
    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官网下载安装包进行安装。安装完成后运行xampp-control.exe打开控制面板,点击Apache后对应的start按钮,启动Apache服务器,如下图所示:

image-20241024114721825.png

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

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.xxipconfig命令所查询到的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.crtservier.key分别复制覆盖到 XAMPP安装目录\apache\conf\ssl.crtXAMPP安装目录\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
    • 安装好证书之后,需要检查证书是否正确安装.。按下快捷键 Win+R, 输入 certmgr.msc并回车, 检查 受信任的根证书颁发机构中是否有之前安装的证书信息。 image-20241024150654179.png
    • 通过XAMPP Control Panel启动Apache服务器,之后通过 https://mysite1.com访问测试网站,查看是否为安全连接。 image-20241024151111212.png

4.3 编写测试应用

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

  • 新增说明字符串。

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

      {
        "string": [
      
          ...
      
          {
            "name": "Network_images_with_custom_cert",
            "value": "Network images with custom cert"
          },
      
          ...
      
        ]
      }
    • 修改 entry/src/main/resources/zh_CN/element/string.json

      {
        "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地址):

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

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

            // 网络图片(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,
                progressListener:(progress: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

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

    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)向应用沙箱推送文件 (openharmony.cn)

  3. 再次运行测试应用,进入到SingleImage.ets所写的页面中,可以看到测试图片资源被成功加载出来,这说明应用成功使用自定义证书与服务器建立了https连接,验证了本文所新增的自定义https证书功能。

    image-20241024163417130.png

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部