如何在鸿蒙上使用OpenSSL

784 阅读4分钟

本文主要是简单讲解如何在鸿蒙原生应用开发中如何使用 OpenSSL 并且通过 ohos-rs 基于 Rust 的reqwest 模块开发一个简单的网络请求方法。

在开始之前,如果你有以下经验背景,那么你将获得最佳的阅读体验:

  1. 了解鸿蒙开发,知道其基本开发套路。
  2. 了解 OpenSSL,了解 Rust 基本开发。

本文的示例代码在:openssl
为了减少 Git 仓库体积,ohos-openssl 采用 submodule 的模式引入。如果需要在本地运行,在拉取仓库之后请执行:git submodule update --init

背景

为什么我们需要使用 OpenSSL 作为应用开发的基础依赖?

OpenSSL 不仅为我们提供了 SSL/TLS 相关的能力,同时也提供了大量的基础加解密方式,很多基础库都将使用 OpenSSL 作为最基础的加解密库依赖,因此我们在开发应用的大部分时候可能都需要使用到它。

  • 网络请求库
  • SQLite
  • ···

因此鸿蒙上如何使用成为了我们必须解决的一个问题。基于这种背景下,本文将讲解如何在鸿蒙开发中使用 OpenSSL 依赖,同时将使用 reqwest 模块来使用 OpenSSL 并且请求 HTTPS 接口作为一个实例。

  1. 由于 OpenSSL 由 C 语言编写而成,因此我们在使用的时候只能通过 Native 模块为应用提供能力
  2. 在后半部分的实践中,最好由了解 Rust 以及 ohos-rs 的基础知识。

开始

OpenSSL 作为一个著名的开源库,基本上为全平台提供了基础的构建脚本和构建配置环境,我们可以使用预购建完成的库或者通过源码自行构建出目标平台的产物。

作者在 OpenSSL 的基础上已经构建出了鸿蒙下的预购建产物,我们可以直接使用。

Github:github.com/ohos-rs/oho…

目前支持主流的三大架构:

  • arm64-v8a
  • armeabi-v7a
  • x86_64

在此基础上,我们可以直接使用其能力。

C++

首先我们介绍如何直接在鸿蒙应用开发的官方支持的 C/C++ 原生模块中如何引入并且使用 OpenSSL。

跟普通的项目无异,我们只需要创建一个基础 Native 模板项目即可。

17166478419679.jpg

接下来,我们只需要简单的 clone 已经构建完成的 OpenSSL 到本地即可。

git clone https://github.com/ohos-rs/ohos-openssl.git

接下来我们需要为我们的 Native 模块配置一些信息以确保 cmake 在构建的时候能够顺利的找到我们的 OpenSSL 依赖。

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(test_openssl)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)
# 配置查询路径 openssl 确保我们能够找到
add_library(openssl INTERFACE IMPORTED)

target_include_directories(openssl INTERFACE ../../../../ohos-openssl/prelude/arm64-v8a/include)
target_link_directories(openssl INTERFACE ../../../../ohos-openssl/prelude/arm64-v8a/lib)
target_link_libraries(openssl INTERFACE ssl crypto)

add_library(entry SHARED hello.cpp)
# 将openssl依赖注入到环境中
target_link_libraries(entry PUBLIC libace_napi.z.so openssl)

注意:请确保target_xx的路径是存在并且是正确的 OpenSSL 对应的路径。

17166509425391.jpg

接下来,我们就可以正常的在我们项目的 C++ 代码引入 OpenSSL 的能力了,这里我们以 md5 作为示例。

17166510106217.jpg

正常情况下,现在在代码编辑器中已经会进行提示一些代码了。

我们现在只需要接受参数并且使用 OpenSSL 提供的能力计算出最终的 md5 结果即可,这里就不再赘述,我们直接看最终的调用效果即可。

17166835112950.jpg

现在我们已经成功的在 C++ 中调用到了我们的 OpenSSL 能力。

不过在这里有两点需要我们额外注意:

1. OpenSSL 依赖 在调用之前,还需要将 OpenSSL 的构建依赖加入到 entry/src/libs/arm64-v8a文件中以确保我们的程序最终在运行时能够在系统中找到我们需要的libssl.so.3以及libcrypto.so.3文件。

2. CMake构建配置 如果你仔细看的话,会发现一个问题:我们在配置 CMake 的文件时直接写死了arm64-v8a架构的目录,那么假设我们需要构建armeabi-v7a或者x86_64架构的产物的话,怎么办呢?

如果需要构建对应的架构产物,直接修改其路径即可。或者也可以需要根据 CMake 的动态环境变量来读取对应的架构,从而动态修改引入的内容。

当然在这里如果只需要构建一个架构的产物,那么只需要在build-profile.json5文件中加入如下内容即可:

17166838690967.jpg

至此,我们完成了在 C++ 模块中调用 OpenSSL 的全部流程,已经可以快乐的在程序中使用 OpenSSL 了,接下来我们将尝试在 Rust 中使用 相同的 OpenSSL 来体验这个过程。

Rust

Rust 生态中同样有相当多用于网络请求的 crate ,比如最常用的 reqwest 等。这里我们直接使用 reqwest 作为最基础的请求库来简单示范一下如何在 Rust 中使用 OpenSSL 的能力。

同样的我们通过 ohos-rs 新建一个项目,并且添加如下依赖:

[dependencies]
# 注意 这里我们关闭了默认的 TLS 功能并且开启了同步请求的能力
reqwest = { version = "*",default-features = false, features = ["blocking"] }

并将我们的src/lib.rs文件内容修改为如下:

use napi_derive_ohos::napi;
use napi_ohos::{Error, Result, Status};

#[napi]
pub fn fetch() -> Result<String> {
    let client = reqwest::blocking::Client::new();
    // 请求 HTTPS 服务
    let res = client
        .post("https://www.baidu.com")
        .body("the exact body that is sent")
        .send()
        .map_err(|e| Error::new(Status::GenericFailure, format!("{:?}", e)))?;
    let txt = res
        .text()
        .map_err(|e| Error::new(Status::GenericFailure, format!("{:?}", e)))?;
    Ok(txt)
}

构建并且在项目中调用,我们会发现有如下报错:

17166854431694.jpg

错误指我们在未开启 TLS 的功能时,不允许使用 HTTPS 协议的请求地址。这里其实是 reqwest 做了一些前置的校验,避免了一些长时间错误查询。

假设你没有关闭默认的 TLS 能力,有时候你完全可能在构建的时候就获得错误信息如下所示:

17166856902203.jpg

这就意味着 reqwest 尝试使用 openssl-sys 也就是 OpenSSL 的 Rust-binding 库来使用一些 OpenSSL 的能力,但是在构建过程中查询对应的 OpenSSL 失败了。

那么怎么解决呢?

依旧是可以通过我们预购建完成的 OpenSSL 来提供相应的能力,从而使得其能够完成构建并且最终调用成功。

我们在项目下新增一个build.sh脚本,其内容如下所示:

#!/bin/sh
# 告诉 openssl-sys 预购建的 OpenSSL 产物目录
export AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR="${PWD}/../ohos-openssl/prelude/arm64-v8a/"
export ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_DIR="${PWD}/../ohos-openssl/prelude/armeabi-v7a/"
export X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR="${PWD}/../ohos-openssl/prelude/x86_64/"

ohrs build

尝试运行,发现已经能够正常编译了。

17166859831566.jpg

这时候我们再去尝试发起请求,发现已经能够正常请求到了。

17166905161305.jpg

注意:

  1. 对于不同的设备来说,部分设备需要指定 CA 证书。对于 OpenHarmony4.x 以及比较新的Harmony Next的设备来说,默认的系统证书目前在/etc/ssl/certs/cacert.pem路径下。
  2. 系统证书某些服务端校验会直接失败,可以尝试使用自签名证书。

到现在,我们就完成了在 Rust 中使用 OpenSSL 的全流程了,当然使用 Rust 你也可以尝试使用 rustls 可以脱离 OpenSSL 实现 SSL/TLS 等能力。

其实在鸿蒙中使用 OpenSSL 看起来内容非常多,但是实际上相对来说比较简单,主要掌握两个核心点即可:

  1. 如何让编译器知道你的 OpenSSL 预购建产物所在位置
  2. 在最终运行前,需要将对应架构的预购建产物的libssl.so.3 libcrypto.so.3复制到模块的libs文件夹中的架构文件夹中,以确保最终应用运行时能够正确的找到动态链接库。

而本文中提供的 OpenSSL 预购建产物是全功能包,在体积上可能会有一定的影响。如果对你的应用来说需要更简单的部分,可以参考构建脚本和 OpenSSL 的构建指导来进行一定的裁剪。

到此本文的内容就全部结束,希望对你有所帮助。