因为公司工作的需要,最近研究了在 android studio 中如何使用 Ndk 来进行 app url 签名的加密算法。在这个过程中遇到了许多的问题,所以想写下这篇博客来记录。在研究的过程中参考了网上许多资料,自己并未记下来这个参考资料的链接,如果作者您本人看见了我这篇博客,有侵犯您知识产权的地方,请告知我,我会进行修改。先在这里对你们无私的奉献表示感谢,谢谢你们!
本文 demo 在上。
这里我将我自己工作中使用到的环境列举一下,如果跟你自己使用的环境不一样,请您自行研究你所处环境 Ndk 的搭建。
(作为一名 android 开发人员,我相信您是有能力进行搭建自己的开发环境!)
1. 操作系统: Ubuntu 14.04.2 64 位 (linux 发行版的一种) [命令:uname -a]
2. gcc version 4.8.4 (我这里用 eclipse 来进行 C 语言测试) [命令:gcc -v]
3. android studio 1.5.1 (64 位和 32 位通用)
4. java version 1.8.0_65 (linux 版本 64 位)
5. android sdk (compileSdkVersion: 23 buildToolsVersion:"23.0.2")
6. android ndk (android-ndk-r10d-linux-x86_64.bin)
linux 下一些安装提示
一:1,2,3,4 点请自行搜索,不再转述;
二:android sdk 请保持最新 (API=23,buildtools=23.0.2);由于国家从安全方面考虑导致您更新不了 sdk,那么请你自己想办法解决;(sdk manager->tools->options->server:mirrors.neusoft.edu.cn port:80 勾上 force [这个是大连东软镜像,貌似每隔一段时间它会清除 android 相关镜像,所以成事在人谋事在天,good luck!])
三:android ndk 我这里用的是比较旧的版本,如果你要用最新的版本,那么请自行解决;(进入 bin 文件所处路径。1. sudo su 切换到 root;2.sudo chmod +x xxx.bin;3.sudo ./xxx.bin)
在你自己 module 中新建一个 java 文件;如我这里时 Signature.java 并在其中定义你自己的 native 方法;
- public class Signature {
- private Signature() {}
- private static Signature instance;
- public static Signature newInstance() {
- if (instance == null) {
- instance = new Signature();
- }
- return instance;
- }
- //native method
- private native String getStringFromNative(String timeStamp, String randStr);
- public String getGenerateSignString(String timeStamp, String randStr) {
- return getStringFromNative(timeStamp, randStr);
- }
- }
这时候你会发现你的 native 方法对应后面有红色提示错误,不用担心,现在不用管它。
接着在 studio 中点击 Build 选择 MakeProject(Ctrl+F9),这一步主要是为了生成与之对应的. class 文件。如下面所示:
如果你公司的项目有多个渠道打包的需求,那么这里的路径会有所变化,上面路径会变为:
../build/intermediates/classes/baidu/debug/…..[你的包名]
下面打开终端,进入你 module 所在路径的 main 文件夹下,如下图所示:
在这里输入下面命令:
- javah - classpath.. / .. / build / intermediates / classes / debug - d jni org.tuzhao.demo.activity.Safe
解释一下:
1. javah 头文件 (.h) 生成命令;
2. -classpath ../../build/intermediates/classes/debug 指定你的 classpath 路径
3. -d jni 在当前目录生成 jni 文件夹
4. org.tuzhao.demo.activity.Safe 包名. 类名 注意大小写
操作成功,这时你可以在你的 module 中看见如下图所示,
(请忽略除了 Signature.h 之外的三个文件)
如果您顺利的完成了前面两个步骤,那么恭喜你,距离调用 ndk demo 只差一小部分了!
根绝头文件生成源文件;我这里是将源文件与头文件的命名一样,这个就看自己的处理了。
下面是头文件 (Signature.h 自动生成的) 的内容:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include < jni.h >
- /* Header for class org_tuzhao_demo_activity_Signature */
- #ifndef _Included_org_tuzhao_demo_activity_Signature#define _Included_org_tuzhao_demo_activity_Signature#ifdef __cplusplus extern "C" {#endif
- /*
- * Class: org_tuzhao_ demo_activity_Signature
- * Method: getStringFromNative
- * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_org_tuzhao_demo_activity_Signature_getStringFromNative(JNIEnv * , jobject, jstring, jstring);#ifdef __cplusplus
- }#endif#endif
接着时源文件 (Signature.c 这里暂只做一个简单的字符串返回) 的内容:
- //
- // Created by tuzhao on 15-12-14.
- //
- #include "org_tuzhao_demo_activity_Signature.h"#include "md5.h"#include < jni.h > #include < stdio.h > #include < string.h > #include < malloc.h > #define LOG_TAG "md5"#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) JNIEXPORT jstring JNICALL Java_org_tuzhao_demo_activity_Signature_getStringFromNative(JNIEnv * env, jobject obj, jstring timeStamp, jstring randStr) {
- LOGI("ndk start...!");
- char * cTimeStamp = NULL;
- char * cRandStr = NULL;
- LOGI("-------cTimeStamp-------");
- cTimeStamp = (char * )( * env) - >GetStringUTFChars(env, timeStamp, 0);
- LOGI("%s ", cTimeStamp);
- LOGI("-------cRandStr-------");
- cRandStr = (char * )( * env) - >GetStringUTFChars(env, randStr, 0);
- LOGI("%s ", cRandStr); ( * env) - >ReleaseStringUTFChars(env, randStr, cRandStr); ( * env) - >ReleaseStringUTFChars(env, timeStamp, cTimeStamp);
- return ( * env) - >NewStringUTF(env, "this is come from ndk str!");
- }
注意
一:源文件方法申明的方式要与头文件一致;Java_包名类名方法名 (参数…),请仔细看一下方法名称的构成;
二:java 中 String 在 Jni 中对应的是 jstring,但是这还是不能被 c 语言使用,还必须经过转换,上面已经列出了转换的方法,请注意查看。因为 c 中涉及到了指针,用完 jstring 之后记得释放;
现在已经有了源文件和头文件,我们可以直接在 android 中调用 native 方法了吗?答案时否定的。
我们需要在类中加载我们的 c 库。
- static {
- System.loadLibrary("my-Jni");
- }
这里的 my-Jni 是自己随便取的,你可以更换为你自己想要的名字。这个是可配置的,但是在哪里配置呢?
在我们的 module 的 build.gradle 文件中进行配置,如下代码所示:
- defaultConfig {
- applicationId "org.tuzhao.demo.activity"minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0"ndk {
- moduleName "my-Jni"ldLibs "log"abiFilters("armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "mips64", "x86_64")
- }
- }
可以看见在 defaultConfig 中有一个 ndk 块,主要是在这里面进行相关配置;这里不同于 Eclipse 中开发 ndk。在 Eclipse 中需要手动编写 Android.mk 配置文件,这里 studio 中因为采用 Gradle,所以就不需要我们手动去编写了。但实际上 gradle 也还是编写了这个. mk 文件,您如果有兴趣可以自己在项目中找一找。
注意
1. 如果你是按照我上面所说的步骤来,那么你得注意的是你得必须把你的项目运行经过编译一次才能找到这个. mk 文件。
2. 关于 abiFilters,早期 android 支持的 cpu 架构只有两三种,现在随着技术演变,所以支持的 cpu 架构变多了。目前在我的项目中用到了这几种,请自己根据自己的需求去适配;记得去年刚弄 ndk 时不懂这个就只生成了 armeabi 这一种,那会儿用 Eclipse,apk 在其它手机上都是好的,但是在华为上一直打开奔溃,现在想起来好忧桑;如果你比较懂这个希望就能留言给我讲讲,在此先谢过了。
好了,到此为止,一个简单的 ndk demo 就成型了,自己运行一下吧。
这里其实 c 语言不止可以计算 MD5,这里只是举一个示例。你可以在 c 中进行一系列关于你公司的加密算法处理。这样对你自己写出来的 apk 也算是一个安全的保障,虽然没有破解不了的代码 (或者 apk),但是至少比直接用 java 写的代码破解系数高一点吧。
在这里我们看见在 studio 中编辑 c 文件这个提示一直都在,所以使用 c 是你值得商榷的一件事。
关于 c 加密处理与 java 加密处理效率的问题,这里不做探讨,我好像记得 java 的虚拟机和一些底层是用 c 写的。嗯,就是这样。
下面就例举一下我在项目中用到的 MD5 处理方式:
这是头文件,md5.h:
- //
- // Created by root on 15-12-17.
- //
- #ifndef EBOXSIGNATURE_MD5_H#define EBOXSIGNATURE_MD5_H typedef struct {
- unsigned int count[2];
- unsigned int state[4];
- unsigned char buffer[64];
- }
- MD5_CTX;#define F(x, y, z)((x & y) | (~x & z))#define G(x, y, z)((x & z) | (y & ~z))#define H(x, y, z)(x ^ y ^ z)#define I(x, y, z)(y ^ (x | ~z))#define ROTATE_LEFT(x, n)((x << n) | (x >> (32 - n)))#define FF(a, b, c, d, x, s, ac)\ {\a += F(b, c, d) + x + ac;\a = ROTATE_LEFT(a, s);\a += b;\
- }#define GG(a, b, c, d, x, s, ac)\ {\a += G(b, c, d) + x + ac;\a = ROTATE_LEFT(a, s);\a += b;\
- }#define HH(a, b, c, d, x, s, ac)\ {\a += H(b, c, d) + x + ac;\a = ROTATE_LEFT(a, s);\a += b;\
- }#define II(a, b, c, d, x, s, ac)\ {\a += I(b, c, d) + x + ac;\a = ROTATE_LEFT(a, s);\a += b;\
- }
- void MD5Init(MD5_CTX * context);
- void MD5Update(MD5_CTX * context, unsigned char * input, unsigned int inputlen);
- void MD5Final(MD5_CTX * context, unsigned char digest[16]);
- void MD5Transform(unsigned int state[4], unsigned char block[64]);
- void MD5Encode(unsigned char * output, unsigned int * input, unsigned int len);
- void MD5Decode(unsigned int * output, unsigned char * input, unsigned int len);
- char * join(char * a, char * b);
- unsigned char * md5ToHex(unsigned char * md5Decimalism, int memLength, int lowercase);
- char * md5_calculate(char * randStr, char * timeStamp);#endif //EBOXSIGNATURE_MD5_H
这是源文件 md5.c :
- /*
- * md5.c
- *
- * Created on: 2015年12月16日
- * Author: tuzhao
- */
- #include < memory.h > #include "md5.h"#define LOG_TAG "md5"#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) unsigned char PADDING[] = {
- 0x80,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- };
- void MD5Init(MD5_CTX * context) {
- context - >count[0] = 0;
- context - >count[1] = 0;
- context - >state[0] = 0x67452301;
- context - >state[1] = 0xEFCDAB89;
- context - >state[2] = 0x98BADCFE;
- context - >state[3] = 0x10325476;
- }
- void MD5Update(MD5_CTX * context, unsigned char * input, unsigned int inputlen) {
- unsigned int i = 0,
- index = 0,
- partlen = 0;
- index = (context - >count[0] >> 3) & 0x3F;
- partlen = 64 - index;
- context - >count[0] += inputlen << 3;
- if (context - >count[0] < (inputlen << 3)) context - >count[1]++;
- context - >count[1] += inputlen >> 29;
- if (inputlen >= partlen) {
- memcpy( & context - >buffer[index], input, partlen);
- MD5Transform(context - >state, context - >buffer);
- for (i = partlen; i + 64 <= inputlen; i += 64) MD5Transform(context - >state, &input[i]);
- index = 0;
- } else {
- i = 0;
- }
- memcpy( & context - >buffer[index], &input[i], inputlen - i);
- }
- void MD5Final(MD5_CTX * context, unsigned char digest[16]) {
- unsigned int index = 0,
- padlen = 0;
- unsigned char bits[8];
- index = (context - >count[0] >> 3) & 0x3F;
- padlen = (index < 56) ? (56 - index) : (120 - index);
- MD5Encode(bits, context - >count, 8);
- MD5Update(context, PADDING, padlen);
- MD5Update(context, bits, 8);
- MD5Encode(digest, context - >state, 16);
- }
- void MD5Encode(unsigned char * output, unsigned int * input, unsigned int len) {
- unsigned int i = 0,
- j = 0;
- while (j < len) {
- output[j] = input[i] & 0xFF;
- output[j + 1] = (input[i] >> 8) & 0xFF;
- output[j + 2] = (input[i] >> 16) & 0xFF;
- output[j + 3] = (input[i] >> 24) & 0xFF;
- i++;
- j += 4;
- }
- }
- void MD5Decode(unsigned int * output, unsigned char * input, unsigned int len) {
- unsigned int i = 0,
- j = 0;
- while (j < len) {
- output[i] = (input[j]) | (input[j + 1] << 8) | (input[j + 2] << 16) | (input[j + 3] << 24);
- i++;
- j += 4;
- }
- }
- void MD5Transform(unsigned int state[4], unsigned char block[64]) {
- unsigned int a = state[0];
- unsigned int b = state[1];
- unsigned int c = state[2];
- unsigned int d = state[3];
- unsigned int x[64];
- MD5Decode(x, block, 64);
- FF(a, b, c, d, x[0], 7, 0xd76aa478);
- /* 1 */
- FF(d, a, b, c, x[1], 12, 0xe8c7b756);
- /* 2 */
- FF(c, d, a, b, x[2], 17, 0x242070db);
- /* 3 */
- FF(b, c, d, a, x[3], 22, 0xc1bdceee);
- /* 4 */
- FF(a, b, c, d, x[4], 7, 0xf57c0faf);
- /* 5 */
- FF(d, a, b, c, x[5], 12, 0x4787c62a);
- /* 6 */
- FF(c, d, a, b, x[6], 17, 0xa8304613);
- /* 7 */
- FF(b, c, d, a, x[7], 22, 0xfd469501);
- /* 8 */
- FF(a, b, c, d, x[8], 7, 0x698098d8);
- /* 9 */
- FF(d, a, b, c, x[9], 12, 0x8b44f7af);
- /* 10 */
- FF(c, d, a, b, x[10], 17, 0xffff5bb1);
- /* 11 */
- FF(b, c, d, a, x[11], 22, 0x895cd7be);
- /* 12 */
- FF(a, b, c, d, x[12], 7, 0x6b901122);
- /* 13 */
- FF(d, a, b, c, x[13], 12, 0xfd987193);
- /* 14 */
- FF(c, d, a, b, x[14], 17, 0xa679438e);
- /* 15 */
- FF(b, c, d, a, x[15], 22, 0x49b40821);
- /* 16 */
- /* Round 2 */
- GG(a, b, c, d, x[1], 5, 0xf61e2562);
- /* 17 */
- GG(d, a, b, c, x[6], 9, 0xc040b340);
- /* 18 */
- GG(c, d, a, b, x[11], 14, 0x265e5a51);
- /* 19 */
- GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
- /* 20 */
- GG(a, b, c, d, x[5], 5, 0xd62f105d);
- /* 21 */
- GG(d, a, b, c, x[10], 9, 0x2441453);
- /* 22 */
- GG(c, d, a, b, x[15], 14, 0xd8a1e681);
- /* 23 */
- GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
- /* 24 */
- GG(a, b, c, d, x[9], 5, 0x21e1cde6);
- /* 25 */
- GG(d, a, b, c, x[14], 9, 0xc33707d6);
- /* 26 */
- GG(c, d, a, b, x[3], 14, 0xf4d50d87);
- /* 27 */
- GG(b, c, d, a, x[8], 20, 0x455a14ed);
- /* 28 */
- GG(a, b, c, d, x[13], 5, 0xa9e3e905);
- /* 29 */
- GG(d, a, b, c, x[2], 9, 0xfcefa3f8);
- /* 30 */
- GG(c, d, a, b, x[7], 14, 0x676f02d9);
- /* 31 */
- GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
- /* 32 */
- /* Round 3 */
- HH(a, b, c, d, x[5], 4, 0xfffa3942);
- /* 33 */
- HH(d, a, b, c, x[8], 11, 0x8771f681);
- /* 34 */
- HH(c, d, a, b, x[11], 16, 0x6d9d6122);
- /* 35 */
- HH(b, c, d, a, x[14], 23, 0xfde5380c);
- /* 36 */
- HH(a, b, c, d, x[1], 4, 0xa4beea44);
- /* 37 */
- HH(d, a, b, c, x[4], 11, 0x4bdecfa9);
- /* 38 */
- HH(c, d, a, b, x[7], 16, 0xf6bb4b60);
- /* 39 */
- HH(b, c, d, a, x[10], 23, 0xbebfbc70);
- /* 40 */
- HH(a, b, c, d, x[13], 4, 0x289b7ec6);
- /* 41 */
- HH(d, a, b, c, x[0], 11, 0xeaa127fa);
- /* 42 */
- HH(c, d, a, b, x[3], 16, 0xd4ef3085);
- /* 43 */
- HH(b, c, d, a, x[6], 23, 0x4881d05);
- /* 44 */
- HH(a, b, c, d, x[9], 4, 0xd9d4d039);
- /* 45 */
- HH(d, a, b, c, x[12], 11, 0xe6db99e5);
- /* 46 */
- HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
- /* 47 */
- HH(b, c, d, a, x[2], 23, 0xc4ac5665);
- /* 48 */
- /* Round 4 */
- II(a, b, c, d, x[0], 6, 0xf4292244);
- /* 49 */
- II(d, a, b, c, x[7], 10, 0x432aff97);
- /* 50 */
- II(c, d, a, b, x[14], 15, 0xab9423a7);
- /* 51 */
- II(b, c, d, a, x[5], 21, 0xfc93a039);
- /* 52 */
- II(a, b, c, d, x[12], 6, 0x655b59c3);
- /* 53 */
- II(d, a, b, c, x[3], 10, 0x8f0ccc92);
- /* 54 */
- II(c, d, a, b, x[10], 15, 0xffeff47d);
- /* 55 */
- II(b, c, d, a, x[1], 21, 0x85845dd1);
- /* 56 */
- II(a, b, c, d, x[8], 6, 0x6fa87e4f);
- /* 57 */
- II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
- /* 58 */
- II(c, d, a, b, x[6], 15, 0xa3014314);
- /* 59 */
- II(b, c, d, a, x[13], 21, 0x4e0811a1);
- /* 60 */
- II(a, b, c, d, x[4], 6, 0xf7537e82);
- /* 61 */
- II(d, a, b, c, x[11], 10, 0xbd3af235);
- /* 62 */
- II(c, d, a, b, x[2], 15, 0x2ad7d2bb);
- /* 63 */
- II(b, c, d, a, x[9], 21, 0xeb86d391);
- /* 64 */
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
- }
- /*不改变字符串a,b, 通过malloc,生成第三个字符串c, 返回局部指针变量*/
- char * join(char * a, char * b) {
- char * result = malloc(strlen(a) + strlen(b) + 1); //+1 for the zero-terminator
- strcpy(result, a);
- strcat(result, b);
- return result;
- }
- /*
- * 函数定义:
- * 用于把10进制表示的md5无符号字符数组,转换为16进制表示的md5字符串
- * 参数 md5Decimalism 是指向10进制表示形式的md5无符号字符数组的指针
- * 参数 memLength 是md5Decimalism指向的内存区域大小的字节表示
- * 参数 lowercase表示16进制字母是否采用小写,非零值为采用小写
- * 返回值 函数的返回值是一个指向字符串的指针,这个字符串是输入的md5的16进
- * 制表示,占用的内存字节数为memLength*2+1,异常情况返回NULL,请务必检测
- */
- unsigned char * md5ToHex(unsigned char * md5Decimalism, int memLength, int lowercase) {
- unsigned char * result = NULL;
- int i = 0;
- unsigned char offset = lowercase ? 87 : 55;
- /*16进制字母部分采用小写形式偏移87,采用大写形式偏移55*/
- /*输入校验*/
- if (memLength <= 0) {
- return result;
- }
- result = (unsigned char * ) malloc(memLength * 2 + 1);
- /*为结果请求内存地址*/
- if (result == NULL) return result;
- memset(result, 0, memLength * 2 + 1);
- /*重置内存结果*/
- while (i < memLength) {
- memset( & result[i * 2], md5Decimalism[i] / 16 < 10 ? md5Decimalism[i] / 16 + 48
- /*16进制非字母部分偏移48*/
- : md5Decimalism[i] / 16 + offset, sizeof(unsigned char));
- /*存储一字节8位的前四位*/
- memset( & result[i * 2 + 1], md5Decimalism[i] % 16 < 10 ? md5Decimalism[i] % 16 + 48
- /*16进制非字母部分偏移48*/
- : md5Decimalism[i] % 16 + offset, sizeof(unsigned char));
- /*存储一字节8位的后四位*/
- i++;
- }
- return result;
- }
- /**
- * calculate md5 for string
- */
- char * md5_calculate(char * randStr, char * timeStamp) {
- unsigned char decrypt[16];
- char * strTemp = NULL;
- strTemp = join(randStr, timeStamp);
- MD5_CTX md5;
- MD5Init( & md5);
- MD5Update( & md5, strTemp, strlen((char * ) strTemp));
- MD5Final( & md5, decrypt);
- unsigned char * b = NULL;
- b = md5ToHex(decrypt, 16 * sizeof(unsigned char), 1);
- /*函数调用*/
- LOGI("%s", b);
- return b;
- }
注意:
1.md5.h 和 md5.c 这两个文件参考自网上,自己做了一些调整。如果您是这两个文件的作者,请您与我联系,我好标明出处。如果您认为我侵犯你的知识权,那么请你通知我,我将将这两个文件从这篇博客中移除。
2. 关于这个 module 请查看,module 名称为:ndkDemoTwo;
3. 我之前用的另外一个 md5 计算方式,如果位数超过了 56 位就出现计算不准确的现象,我也将那两个文件上传到我中去了,名为 ndkDemoOne; 如果您有实践欢迎你来帮我解决这个难题,在
这里先对你表示感谢了!
具体请参考我在。
其实在 android studio 中导入 Eclipse 的 ndk 项目和导入普通的 android 项目一样。file->new->import module 即可。
导入后删除原项目中的. mk 文件,在 jni 文件夹中只保留 c 文件,关于 ndk 的配置任然在 defaultConfig 中配置即可。
我目前是这样做的,如果你认为有不对的地方,欢迎
来源: http://lib.csdn.net/article/android/43453