从 6.0 开始,Google 要求不要使用系统的 OpenSSL,请见:https://developer.android.com...。所以,请不要再使用本文介绍的方法,请自行交叉编译 OpenSSL 或者使用别人编译好的版本。
2017年3月注html
因为Java较为容易被反编译,所以把一些重要代码放在so文件中成为了一个代价不过高的选择。虽然so文件依旧能够反编译,但对so进行逆向分析的门槛则要比分析Java字节码的门槛高出很多。不少安全相关的代码都依赖OpenSSL,然而网络上在NDK中使用OpenSSL的教程并很少见,通过一天的探索,我终于能够成功在NDK中调用OpenSSL了。本文将以调用OpenSSL中的HMAC算法为例,介绍如何使用Gradle配置NDK并在NDK中使用OpenSSL。java
2015年7月,Google发布了新的Gradle插件,提供了对NDK的支持,今后,编写NDK程序再也不须要编写Android.mk
文件,也再也不须要使用ndk-build
脚本,只须要在Gradle中简单的配置一下,便可方便的编译程序了。android
目前,新的插件仍处在beta版本,本文选用当前时间(2016年3月2日)最新的0.6.0-beta5
做介绍。要获取最新的更新,请访问这里。git
从传统的Android Gradle插件迁移到新的插件并不困难,只须要修改原有目录结构中的三个文件便可。以以下目录为例:github
. ├── 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
文件。算法
分别来看对它们的修改:api
每一个版本的插件都只支持特定的Gradle版本,所以请务必对照上文给出的连接填写正确的版本。安全
./local.properties
网络
须要在文件中指定ndk.dir
属性,指向NDK
的路径。app
./gradle/wrapper/gradle-wrapper.properties
这个文件定义了Gradle的版本,这里须要使用gradle-2.10
,所以须要把最后一行的版本替换掉。
./build.gradle
这里定义了构建时使用的插件,须要替换为com.android.tools.build:gradle-experimental:0.6.0-beta5
。
./app/build.gradle
这个文件变化较大,主要的变化包括:
插件名由原来的com.android.application
变为com.android.model.application
。
全部的配置放置在model { }
块中。
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
,
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"); }
https://github.com/terro/andr...
须要注意的是,这里只是一个简单的DEMO,不要直接在项目中保存密钥之类的信息。