Android CMake轻松实现基于OpenSSL的HmacSHA1签名

安全加密C语言库OpenSSL,在Android中服务器和客户端之间的签名验证和数据加密通信等。

OpenSSL系列文章:

一、Android CMake轻松实现基于OpenSSL的HmacSHA1签名
二、Android CMake轻松实现基于OpenSSL的SHA(1-512)签名
三、Android CMake轻松实现基于OpenSSL的MD5信息摘要&异或加解密
四、Android CMake轻松实现基于OpenSSL的AES加解密
五、Android CMake轻松实现基于OpenSSL的RSA加解密
六、Android CMake轻松实现基于OpenSSL的RSA签名和验证
七、在Retrofit的基础上结合OpenSSL实现服务器和客户端之间数据加密通信
最近有这么一个需求,要对接口进行签名验证以防止被刷。开始想到了在Java中实现HmacSHA1签名,但由于Java代码较容易反编译直接获取秘钥,而否定了这个方案。为了解决这个问题,把实现签名的逻辑代码用C/C++来编写,编译成xxx.so库,大大提高了反编译的门槛,从而降低了被反编译的风险。

在C/C++中要实现HmacSHA1签名,只有依赖一个很出名的C语言中常用的加解密库OpenSSL。什么是OpenSSL请自行百度,随便搜都是一大堆。难点在于在Android中进行Jni开发,在C/C++文件中要调用第三方OpenSSL的xxx.so库,那怎样才能把OpenSSL的xxx.so库引入我们的C/C++文件中呢?换句话说就是最终我们的C/C++文件会被编译成一个.so库,就叫libsignature.so吧!而这个库又引用了第三方xxx.so库。不过不用担心,自从Android支持CMake后,一切都变得简单了。网络上也有很多博客,也有很多代码片段,但是对于初学者来说,根本不知道那是什么玩意儿!所以我想写细一点。

一:新建项目
如下图勾选include C++ support,然后一路next,直到finish。


新建NDK工程.png

二:编译OpenSSL源码得到.so库
OpenSSL源码:github上搜索关键字【OpenSSL for Android】下载并编译,拷贝所有.so到app/alley/lib/目录下,拷贝OpenSSL所有头文件到app/alley/include/openssl/目录下。


alley文件目录.png

三:gradle配置

#app目录下build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.alley.openssl"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions"
                //目标平台,若需要更多平台的请自行配置
                abiFilters 'armeabi'
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            //第三方.so库路径
            jniLibs.srcDirs = ['E:\\AndroidSpace\\OpenSSL\\app\\alley\\lib']
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
    testCompile 'junit:junit:4.12'
}

四:CMakeLists脚本
CMakeLists脚本语法请自行查阅

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)
#C++编译
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

#配置加载头文件
include_directories(E:/AndroidSpace/OpenSSL/app/alley/include)

#动态方式加载
add_library(openssl SHARED IMPORTED )
add_library(ssl SHARED IMPORTED )
#引入第三方.so库
set_target_properties(openssl PROPERTIES IMPORTED_LOCATION E:/AndroidSpace/OpenSSL/app/alley/lib/${ANDROID_ABI}/libcrypto.so)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION E:/AndroidSpace/OpenSSL/app/alley/lib/${ANDROID_ABI}/libssl.so)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             signature

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/signature.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       signature
                       openssl
                       ssl
                       android

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

五:加解密
HmacSHA1算法得到签名结果为十六进制数。在本项目中对其进行Base64编码,发现编码结果为28位的,这和用Java代码实现的完全不同(Java实现的是56位),在线base64编码测试结果也为56位。原因是什么呢?
待编码数据是按照** %02x 格式输出的值,然后用这个值再次进行base64编码。在这里解释一下 %02x:按照十六进制进行数据输出,不足两位,则在之前补0,否则按实际数据输出。 所以用到了sprintf(char *, const char *, ...)和strcat(char , const char )函数,这才是重点啊,不太容易发现。在开发过程中注意C++函数名与native方法的关系。将最后编译生成的libsignature.so拷贝你的项目中就可以使用了。秘钥自己设定,注意不要泄露了。更多详情请阅读源码。

#include <jni.h>
#include <string>
#include <Android/log.h>
#include <openssl/hmac.h>


#define TAG "JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_alley_openssl_util_JniUtils_getSignature(JNIEnv *env, jobject instance, jbyteArray value_) {
    const char *key = "5fd6s4gs8f7s1dfv23sdf4ag65rg4arhb4fb1f54bgf5gbvf1534as";
    LOGI("HmacSHA1->准备获取待加密数据");
    jbyte *value = env->GetByteArrayElements(value_, NULL);
    LOGI("HmacSHA1->准备计算待加密数据长度");
    size_t value_Len = env->GetArrayLength(value_);

    unsigned int result_len;
    unsigned char result[EVP_MAX_MD_SIZE];
    char buff[EVP_MAX_MD_SIZE];
    char hex[EVP_MAX_MD_SIZE];

    LOGI("HmacSHA1->准备进行加密计算");
    HMAC(EVP_sha1(), key, strlen(key), (unsigned char *) value, value_Len, result, &result_len);
    LOGI("HmacSHA1->加密计算结束");

    strcpy(hex, "");
    for (int i = 0; i != result_len; i++) {
        sprintf(buff, "%02x", result[i]);
        strcat(hex, buff);
    }
    LOGI("HmacSHA1->");
    LOGI("%s", hex);

    env->ReleaseByteArrayElements(value_, value, 0);
    LOGI("HmacSHA1->jni释放数据结束");
    jbyteArray signature = env->NewByteArray(strlen(hex));
    env->SetByteArrayRegion(signature, 0, strlen(hex), (jbyte *) hex);
    LOGI("HmacSHA1->准备以ByteArray格式返回数据");
    return signature;
}

六:测试
待签名数据:Android CMake轻松实现基于OpenSSL的HmacSHA1签名
秘钥:5fd6s4gs8f7s1dfv23sdf4ag65rg4arhb4fb1f54bgf5gbvf1534as
在线签名校验:http://tool.oschina.net/encrypt?type=2,经测试代码签名和在线签名结果相同。

下载代码运行,在控制台中输入“body”,将看到所有调试信息。欢迎star,fork,转载。

源码:https://github.com/GitPhoenix/OpenSSL

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容