在Android NDK中使用OpenSSL

AustinSmith 8年前

来自: https://segmentfault.com/a/1190000004522943

由于Java较为容易被反编译,因此把一些重要代码放在so文件中成为了一个代价不太高的选择。虽然so文件依旧可以反编译,但对so进行逆向分析的门槛则要比分析Java字节码的门槛高出不少。很多安全相关的代码都依赖OpenSSL,然而网络上在NDK中使用OpenSSL的教程并不多见,经过一天的探索,我终于可以成功在NDK中调用OpenSSL了。本文将以调用OpenSSL中的HMAC算法为例,介绍如何使用Gradle配置NDK并在NDK中使用OpenSSL。

在Gradle中配置NDK

2015年7月,Google发布了新的Gradle插件,提供了对NDK的支持,从此,编写NDK程序不再需要编写 Android.mk 文件,也不再需要使用 ndk-build 脚本,只需要在Gradle中简单的配置一下,即可方便的编译程序了。

目前,新的插件仍处在beta版本,本文选用当前时间(2016年3月2日)最新的 0.6.0-beta5 作介绍。要获取最新的更新,请访问 这里

从传统的Android Gradle插件迁移到新的插件并不困难,只需要修改原有目录结构中的三个文件即可。以如下目录为例:

.  ├── app/  │   ├── build.gradle  │   └── src/  ├── build.gradle  ├── gradle/  │   └── wrapper/  │       ├── gradle-wrapper.jar  │       └── gradle-wrapper.properties  ├── gradle.properties  ├── gradlew  ├── gradlew.bat  ├── local.properties  └── settings.gradle

需要修改的文件包括两个 build.gradle 、 gradle-wrapper.properties 和 local.properties 文件。

分别来看对它们的修改:

每个版本的插件都只支持特定的Gradle版本,因此请务必对照上文给出的链接填写正确的版本。

  • ./local.properties

需要在文件中指定 ndk.dir 属性,指向 NDK 的路径。

  • ./gradle/wrapper/gradle-wrapper.properties

这个文件定义了Gradle的版本,这里需要使用 gradle-2.10 ,因此需要把最后一行的版本替换掉。

  • ./build.gradle

这里定义了构建时使用的插件,需要替换为 com.android.tools.build:gradle-experimental:0.6.0-beta5 。

  • ./app/build.gradle

这个文件变化较大,主要的变化包括:

  1. 插件名由原来的 com.android.application 变为 com.android.model.application 。

  2. 所有的配置放置在 model { } 块中。

  3. minSdkVersion 和 targetSdkVersion 都需要配置它们的 apiLevel 属性。

下面是一个完整的示例:

apply plugin: 'com.android.model.application'    model {      android {          compileSdkVersion 23          buildToolsVersion "23.0.2"            defaultConfig {              applicationId "com.example.openssltest"              minSdkVersion.apiLevel 14              targetSdkVersion.apiLevel 23              versionCode 1              versionName "1.0"          }            ndk {              moduleName = "openssl-jni"              platformVersion = 14              ldFlags.add("-lcrypto")              abiFilters.add("armeabi-v7a")          }      }  }    dependencies {      compile 'com.android.support:appcompat-v7:23.1.1'  }

这个配置中,多出了 ndk 部分,下面就来解释一下这部分的配置:

  • moduleName 决定了编译出来的库的名称,这个选项是必须的。

  • platformVersion 指定了NDK的platform版本,这里使用14是因为 minSdkVersion 使用14。

  • ldFlags 指定了链接时的参数,由于本例中只用到了HMAC算法,因此这里添加了对 crypto ,如果还需要TLS的支持,这里需要改为 ldFlags.addAll(["-lcrypto", "-lssl"]) 。

  • abiFilters 里指定了abi的版本 armeabi-v7a ,

在NDK中使用OpenSSL

Android里已经内置了OpenSSL,但NDK中并没有提供相应的库。只需要把OpenSSL的 .so 文件放在NDK中即可:

$adb pull /system/lib/libssl.so /myndk/platforms/android-14/arch-arm/usr/lib    $adb pull /system/lib/libcrypto.so /myndk/platforms/android-14/arch-arm/usr/lib

然后把OpenSSL的头文件放在 /myndk/platforms/android-14/arch-arm/usr/include 目录中即可。

编写代码请参考JNI的文档,下面给出一个调用 HMAC-SHA256 的实现:

#include <jni.h>  #include <openssl/hmac.h>    #ifdef __cplusplus  extern "C" {  #endif  jbyteArray  Java_com_example_openssltest_MainActivity_hmacSha256(JNIEnv *env,                                                       jobject obj,                                                       jbyteArray content) {    unsigned char key[] = {0x6B, 0x65, 0x79};      unsigned int result_len;    unsigned char result[EVP_MAX_MD_SIZE];      // get data from java array    jbyte *data = env->GetByteArrayElements(content, NULL);    size_t dataLength = env->GetArrayLength(content);      HMAC(EVP_sha256(),         key, 3,         (unsigned char *) data, dataLength,         result, &result_len);      // release the array    env->ReleaseByteArrayElements(content, data, JNI_ABORT);      // the return value    jbyteArray return_val = env->NewByteArray(result_len);    env->SetByteArrayRegion(return_val, 0, result_len, (jbyte *) result);    return return_val;  }  #ifdef __cplusplus  }  #endif

在Java中调用也很容易,只需要引用 build.gradle 中指定的库即可:

public native byte[] hmacSha256(byte[] data);    static {      System.loadLibrary("openssl-jni");  }

DEMO项目链接

https://github.com/terro/android-ndk-openssl

需要注意的是,这里只是一个简单的DEMO,不要直接在项目中保存密钥之类的信息。

原文链接: https://dangfan.me/zh-Hans/posts/android-ndk-openssl </div>