OpenSSL实践-Android下的编译和使用

2019-08-30 21:53:51

参考地址 OpenSSL实践-Android下的编译和使用

1. 概述


openssl可以编译成ARM下面的二进制代码(动态库或者静态库),方便APP使用,APP在使用的时候,需要使用JNI来进行调用。

官方WIKI有写如何为android编译openssl,地址是:https://wiki.openssl.org/index.php/Android
因此也是参考这篇文章实现的。

编译不太复杂,基本步骤如下:

  1. 下载NDK

  2. 下载openssl源码和设置环境变量所需要的setenv-android.sh

  3. 配置编译参数

  4. 编译

2. 准备工作

2.1 环境准备


官方给的方法,是linux下的,因此这里建议使用下面三种方式来实现编译:

  • ubuntu物理机或者虚拟机

  • windows上使用docker,然后使用ubuntu/debian一类的image来编译(建议使用Android SDK/NDK一类的image,省去环境部署的麻烦)

  • windows 10上面使用WSL

上述三种其实配置和编译过程都是一致的,只是后两者方便使用windows为主的用户。因此我这里给出的WSL上的配置和编译过程(实际上和前两种没什么区别)。

2.2 安装linux版本的NDK


编译Openssl需要使用NDK,先下载NDK:

# 在D盘下建立NDK目录mkdir /mnt/d/ndk
cd /mnt/d/ndk
wget https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip

下载还是挺快的,不需要翻墙,网速慢的朋友可以使用迅雷来进行下载。

# 解压unzip android-ndk-r14b-linux-x86_64.zip# 如果提示unzip为无效命令,可以使用apt-get进行安装sudo apt-get install unzip

注意:请勿在windows下直接解压,因为内部会有同名文件(只是大小写不同),引发文件覆盖

最后:NDK被解压在: D盘的NDK目录下,也就是:/mnt/d/ndk/android-ndk-r14b

2.3 准备openssl源码


# 下载源码cd /mnt/d
wget https://www.openssl.org/source/openssl-1.1.0f.tar.gz# 解压源码tar -zxvf openssl-1.1.0f.tar.gz#拉取setenv-android.sh,我们需要这个脚本来给我们配置环境cd /mnt/d/openssl-1.1.0f
wget https://wiki.openssl.org/images/7/70/Setenv-android.sh# 注意这个文件是大写开头的,我们稍微修改一下名称,方便使用mv Setenv-android.sh setenv-android.sh

最后,openssl的源码所在位置是:/mnt/d/openssl-1.1.0f

3. 配置环境


用编辑器编辑(可以使用windows下的编辑器,我这里使用 notepad++setenv-android.sh, 修改以下参数:

_ANDROID_NDK="android-ndk-r9"# 修改为:_ANDROID_NDK="android-ndk-r14b"# 因为我们使用的是R14B这个版本的NDK
_ANDROID_EABI="arm-linux-androideabi-4.8"
修改为GCC 4.9
_ANDROID_EABI="arm-linux-androideabi-4.9"

注意,这个脚本在WSL上执行可能存在一些问题,我下载下来的是\r\n换行的,在WSL上执行会报错,我们可以使用notepad++来替换掉\r,具体做法如下图所示:

替换掉\r

4. 开始编译


  • 设置环境变量

我们需要通过环境变量来指定NDK所在的位置,按照之前的安装位置,我们只需要执行:

export ANDROID_NDK_ROOT=/mnt/d/ndk/android-ndk-r14b
source ./setenv-android.sh# 输出ANDROID_NDK_ROOT: /mnt/d/ndk/android-ndk-r14b
ANDROID_ARCH: arch-arm
ANDROID_EABI: arm-linux-androideabi-4.9
ANDROID_API: android-18
ANDROID_SYSROOT: /mnt/d/ndk/android-ndk-r14b/platforms/android-18/arch-arm
ANDROID_TOOLCHAIN: /mnt/d/ndk/android-ndk-r14b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin
FIPS_SIG: /mnt/d/openssl-1.1.0f/util/incore
CROSS_COMPILE: arm-linux-androideabi-
ANDROID_DEV: /mnt/d/ndk/android-ndk-r14b/platforms/android-18/arch-arm/usr

注意,这一步之后,请勿关闭cmd/终端窗口,因为编译器等参数是通过环境变量传递给make的

  • 创建输出目录

mkdir /mnt/d/openssl-output
  • 配置和编译

# 配置openssl./config no-shared no-ssl2 no-ssl3 no-comp no-hw no-engine \
     --openssldir=/mnt/d/openssl-output/$ANDROID_API --prefix=/mnt/d/openssl-output/$ANDROID_API#编译make depend
make all -j8 
# -j8 表示并发的编译"线程数",建议设置成CPU线程数一致

这里需要注意的是,no-shared表示不编译动态库,这样编译出来的openssl命令,不依赖动态库,同时也没有so产生。更多的编译参数,详见源码目录下的:INSTALL(该文件没有后缀)

  • 安装

make install

执行完上面的命令,openssl的头文件、库文件、文档以及命令就被复制在: /mnt/d/openssl-output 目录里了。

目录主要结构是:

bin 存放openssl命令
include 头文件
lib 库文件
share 文档一类的

5. 测试APP


  • 新建一个测试APP,并勾选 include C++ support

  • 把include下的openssl目录,整个复制到项目src下的cpp子目录

  • 把lib下的libcrypto.alibssl.a复制到src->cpp->libs子目录

最终目录结构如下:

├─androidTest
│  └─java
│      └─com
│          └─example
│              └─summer
│                  └─myapplication
├─main
│  ├─cpp
│  │  ├─libs
│  │  └─openssl
│  ├─java
│  │  └─com
│  │      └─example
│  │          └─summer
  • 修改CMakeLists.txt,在 cmake_minimum_required(VERSION 3.4.1) 后面加:

# 表示把src/main/cpp加入include目录,这样在代码中,使用:#include <...>就能访问到头文件include_directories(src/main/cpp)# 添加两个预编译库add_library(openssl-crypto
    STATIC
    IMPORTED)set_target_properties(openssl-crypto
                      PROPERTIES IMPORTED_LOCATION                      ${CMAKE_SOURCE_DIR}/src/main/cpp/libs/libcrypto.a )add_library(openssl-ssl
  STATIC
  IMPORTED)set_target_properties(openssl-ssl
                    PROPERTIES IMPORTED_LOCATION                    ${CMAKE_SOURCE_DIR}/src/main/cpp/libs/libssl.a )

把最后的:

target_link_libraries( # Specifies the target library.
                       native-lib                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib})

修改成:

target_link_libraries( # Specifies the target library.
                       native-lib                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} openssl-ssl openssl-crypto)
  • 修改app目录下的:build.gradle

android {
    compileSdkVersion 25
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "com.example.summer.myapplication"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions"
                // 下面这样是增加的
                arguments "-DANDROID_ABI=armeabi-v7a"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

实际上增加的只是一行代码: ** arguments "-DANDROID_ABI=armeabi-v7a"。默认情况下,gradle会通过多次调用,通过传递ABI类型来让cmake产生各个ABInija构建脚本,从而编译出各个ABI下的二进制文件。因为openssl默认编译出来的只有armeabi-v7a的,因此我们留下这个ABI**就行了。

  • 修改native-lib.cpp

在第一行增加:

#include "openssl/crypto.h"

下面的代码替换一下:

    std::string hello = "Hello from C++";    return env->NewStringUTF(hello.c_str());

替换为:

return env->NewStringUTF(OpenSSL_version(OPENSSL_VERSION));
  • 启动APP

编译之后,如无意外,可以直接在手机上运行了,会到看主界面上输入openssl的版本信息:

openssl test app

6. 其他


  • 编译器兼容性

细心的朋友可能发现了,我编译的时候使用的是GCC 4.9,而APP在链接的时候,默认使用了CLANG,在google的文档里,GCC已经是不推荐的工具链了(详见:https://developer.android.com/ndk/guides/cmake.html ) , 至于两者的ABI兼容性,我也没发现一个比较权威的答案,可以看看这篇讨论: https://stackoverflow.com/questions/20875924/can-clang-compile-code-with-gcc-compiled-a-libs 鉴于C++的复杂性,建议C++不要这样做。 可以在build.gradle里面设置成GCC编译,这样工具链就一致了。

  • android的abi

目前默认构建出来的是armea-v7a的,基本上大部分CPU都是这个类型了。而目前这个脚本不能直接构建出64位的arm64-v8a,有需要的朋友,可以参考:http://doc.qt.io/qt-5/opensslsupport.html。 至于X86,可以通过修改:setenv-android.sh里面的:** _ANDROID_ARCH=arch-x86** 来实现。至于MIPS目前不能直接编译。




  • 2019-12-04 10:48:18

    vue 项目资源文件 static 和 assets 不说区别直接使用?

    assets中资源会webpack构建压缩到你代码中,而static文件直接引用。 static 中长存放类包、插件等第三方的文件,assets里放属资源文件比如自己资源图片、css文件、js文件。 引入资源的方式static文件夹可以使用~/static/方式引入, assets文件夹可以使用 ~@/assets 方式引入

  • 2019-12-05 17:01:36

    Vue 结合 Axios 接口超时统一处理

    当网路慢的时候。又或者公司服务器不在内地的时候,接口数据请求不回来超时报错的情况相信大家肯定遇到过的,这里我把我公司项目请求超时的处理方法分享下,希望看过后有帮助。

  • 2019-12-05 17:13:40

    JS模板工具lodash.template的简单用法

    lodash是从underscore分支的一个项目,之前我写了一篇JS模板工具underscore.template的简单用法,lodash跟underscore很相似,这也简单介绍一下lodash的template方法。 先把underscore的文章中用过的代码贴过来,把underscore的js文件换成lodash的js,其他一字不改,然后我们试试:

  • 2019-12-06 10:47:29

    date-fns日期工具的使用方法详解

    isToday() 判断传入日期是否为今天 isYesterday() 判断传入日期是否为昨天 isTomorrow() 判断传入日期是否为 format() 日期格式化 addDays() 获得当前日期之后的日期 addHours() 获得当前时间n小时之后的时间点 addMinutes() 获得当前时间n分钟之后的时间 addMonths() 获得当前月之后n个月的月份 subDays() 获得当前时间之前n天的时间 subHours() 获得当前时间之前n小时的时间 subMinutes() 获得当前时间之前n分钟的时间 subMonths() 获得当前时间之前n个月的时间 differenceInYears() 获得两个时间相差的年份 differenceInWeeks() 获得两个时间相差的周数 differenceInDays() 获得两个时间相差的天数 differenceInHours() 获得两个时间相差的小时数 differenceInMinutes() 获得两个时间相差的分钟数

  • 2019-12-06 10:49:39

    npm 查看源 换源

    npm,cnpm,查看源,切换源,npm config set registry https://registry.npmjs.org

  • 2019-12-06 11:01:31

    npm发布包流程详解 有demo

    npm发布包步骤,以及踩过的坑(见红颜色标准): 1.注册npm账号,并完成Email认证(否则最后一步提交会报Email错误) 2.npm添加用户或登陆:npm adduser 或 npm login

  • 2019-12-06 13:16:18

    vue mixins组件复用的几种方式

    最近在做项目的时候,研究了mixins,此功能有妙处。用的时候有这样一个场景,页面的风格不同,但是执行的方法,和需要的数据非常的相似。我们是否要写两种组件呢?还是保留一个并且然后另个一并兼容另一个呢? 不管以上那种方式都不是很合理,因为组件写成2个,不仅麻烦而且维护麻烦;第二种虽然做了兼容但是页面逻辑造成混乱,必然不清晰;有没有好的方法,有那就是用vue的混合插件mixins。混合在Vue是为了提出相似的数据和功能,使代码易懂,简单、清晰。

  • 2019-12-06 13:26:30

    vue的mixins混入合并规则

    混入minxins:分发vue组件中可复用功能的灵活方式。混入对象可以包含任意组件选项。组件使用混入对象时,所有混入对象的选项将混入该组件本身的选项。