why
1、Apk包很容易被破解(应该有共识)
2、Java语言不安全,容易被反编译从而看到源码
3、C语言相对安全,因为编译后是二进制文件
4、秘钥不应该在java代码中保存,容易被反编译看到,所以秘钥必须放在C中
5、只把秘钥放在C中也不安全,因为如果反编译看到java调用so包方法并且拿到so包也是可以拿到秘钥的,所以没有意义。因此需要把整个加解密过程都放在C端。
6、如果旧的版本使用java加密,而新的版本使用Jni加密,那么就得考虑兼容性的问题,即Java和JNI可以互相加解密
how
JNI使用配置过程我就不说了网上很多,直接从代码开始。
demo目录 如图:JniUtil就是负责本地数据的加解密,代码如下:
package com.example.xingchang.jni_aes256_demo;
public class JniUtil {
static {
System.loadLibrary("JniUtil");
}
public native String encrypt(String plainText);
public native String decrypt(String cipherText);
}
真正进行加解密操作的是JniUtil.c文件,代码如下:
//
// Created by xing.chang on 2018/10/24.
//
#include "com_example_xingchang_jni_aes256_demo_JniUtil.h"
#include "base64.h"
#include <stdlib.h>
#include <aes256.h>
#include<android/log.h>
const char *DES_KEY = "b5e52765b81d101510dc0afdc52b1d64";
const char *VIPARA = "1982051319810208";
#define TAG "myDemo-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
jstring charToJstring(JNIEnv* envPtr, char *src) {
JNIEnv env = *envPtr;
jsize len = strlen(src);
jclass clsstring = env->FindClass(envPtr, "java/lang/String");
jstring strencode = env->NewStringUTF(envPtr, "UTF-8");
jmethodID mid = env->GetMethodID(envPtr, clsstring, "<init>", "([BLjava/lang/String;)V");
jbyteArray barr = env->NewByteArray(envPtr, len);
env->SetByteArrayRegion(envPtr, barr, 0, len, (jbyte*) src);
return (jstring) env->NewObject(envPtr, clsstring, mid, barr, strencode);
}
jstring getImportInfo(JNIEnv* envPtr, jstring mingwen) {
JNIEnv env = *envPtr;
//b5e52765b81d101510dc0afdc52b1d64 秘钥
unsigned char key[32] = {0x62,0x35,0x65,0x35,0x32,0x37,0x36,0x35,
0x62,0x38,0x31,0x64,0x31,0x30,0x31,0x35,
0x31,0x30,0x64,0x63,0x30,0x61,0x66,0x64,
0x63,0x35,0x32,0x62,0x31,0x64,0x36,0x34};
//****************************************开始加密******************************************************
//1.初始化数据
//初始化向量
uint8_t iv[16] = { 0x31,0x39,0x38,0x32,0x30,0x35,0x31,0x33,
0x31,0x39,0x38,0x31,0x30,0x32,0x30,0x38 };
//初始化加密参数
aes256_context ctx;
aes256_init(&ctx, key);
//2.将jstring转为char
const char *mwChar = env->GetStringUTFChars(envPtr, mingwen, JNI_FALSE);
//3.分组填充加密
int i;
int mwSize = strlen(mwChar);
int remainder = mwSize % 16;
jstring entryptString;
if (mwSize < 16) { //小于16字节,填充16字节,后面填充几个几 比方说10个字节 就要补齐6个6 11个字节就补齐5个5
uint8_t input[16];
for (i = 0; i < 16; i++) {
if (i < mwSize) {
input[i] = (unsigned char) mwChar[i];
} else {
input[i] = (unsigned char) (16 - mwSize);
}
}
//加密
uint8_t output[16];
aes256_encrypt_cbc(&ctx, input, iv, output);
//base64加密后然后jstring格式输出
char *enc = base64_encode((const char *) output, sizeof(output));
entryptString = charToJstring(envPtr, enc);
free(enc);
} else { //如果是16的倍数,填充16字节,后面填充0x10
int group = mwSize / 16;
int size = 16 * (group + 1);
uint8_t input[size];
for (i = 0; i < size; i++) {
if (i < mwSize) {
input[i] = (unsigned char) mwChar[i];
} else {
if (remainder == 0) {
input[i] = 0x10;
} else { //如果不足16位 少多少位就补几个几 如:少4为就补4个4 以此类推
int dif = size - mwSize;
input[i] = (unsigned char) dif;
}
}
}
//加密
uint8_t output[size];
aes256_encrypt_cbc(&ctx, input, iv, output);
//base64加密后然后jstring格式输出
LOGD("encrypt output size=%d",size);
char *enc = base64_encode((const char *) output, sizeof(output));
LOGD("encrypt enc=%s",enc);
entryptString = charToJstring(envPtr, enc);
free(enc);
}
//释放mwChar
env->ReleaseStringUTFChars(envPtr, mingwen, mwChar);
return entryptString;
}
JNIEXPORT jstring JNICALL Java_com_example_xingchang_jni_1aes256_1demo_JniUtil_encrypt
(JNIEnv *env, jobject instance, jstring jstr){
if (jstr == NULL) {
return NULL;
}
return getImportInfo(env,jstr);
}
jstring doecrypt(JNIEnv* env, jstring miwen) {
jstring result;
//b5e52765b81d101510dc0afdc52b1d64 秘钥
unsigned char key[32] = {0x62,0x35,0x65,0x35,0x32,0x37,0x36,0x35,
0x62,0x38,0x31,0x64,0x31,0x30,0x31,0x35,
0x31,0x30,0x64,0x63,0x30,0x61,0x66,0x64,
0x63,0x35,0x32,0x62,0x31,0x64,0x36,0x34};
//1.初始化数据
//初始化向量
uint8_t iv[16] = { 0x31,0x39,0x38,0x32,0x30,0x35,0x31,0x33,
0x31,0x39,0x38,0x31,0x30,0x32,0x30,0x38 };
aes256_context ctx;
aes256_init(&ctx, key);
//2.将jstring转为char
const char *mwChar = (*env)->GetStringUTFChars(env, miwen, JNI_FALSE);
char *enc = base64_decode(mwChar, strlen(mwChar));
uint8_t output[4096];
aes256_decrypt_cbc(&ctx, (unsigned char *) enc, iv, output);
int size = strlen((const char *) output);
LOGD("output size=%d",size);
int i;
for(i=0;i<size;i++){
LOGD("cha %d = %c",i,output[i]);
if(output[i]>=1&&output[i]<=16){
output[i] = 0;
}
}
result = charToJstring(env, (char *) output);
LOGD("result=%s",(char *) output);
free(enc);
//释放mwChar
(*env)->ReleaseStringUTFChars(env, miwen, mwChar);
aes256_done(&ctx);
return result;
}
JNIEXPORT jstring JNICALL Java_com_example_xingchang_jni_1aes256_1demo_JniUtil_decrypt
(JNIEnv *env, jobject instance, jstring jstr){
if (jstr == NULL) {
return NULL;
}
return doecrypt(env,jstr);
}
getImportInfo方法是加密,步骤是
1、准备秘钥和向量
2、按照PKCS5Padding方式进行填充
3、调用aes256_encrypt_cbc进行cbc加密
4、然后再用base64_encode进行加密
5、最后输出密文
doecrypt方法是解密,步骤是:
1、准备秘钥和向量
2、调用base64_decode解密
3、调用aes256_decrypt_cbc解密
4、去掉填充
5、输出明文
在main中调用jni和java加解密:
package com.example.xingchang.jni_aes256_demo
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//jni加解密
val jniUtil = JniUtil()
val str= "abcdefg1234567"
val encryptResult= jniUtil.encrypt(str)
Log.i("jniUtil","JNI encryptResult=$encryptResult")
val decryptResult= jniUtil.decrypt(encryptResult)
Log.i("jniUtil","JNI decryptResult=$decryptResult")
//java加解密
val key_md5 ="b5e52765b81d101510dc0afdc52b1d64"
val javaEncrypt= AesUtils.aesEncrypt(str,key_md5)
Log.i("jniUtil","Java encryptResult=$javaEncrypt")
val javaDecrypt = AesUtils.aesDecrypt(javaEncrypt,key_md5)
Log.i("jniUtil","Java decryptResult=$javaDecrypt")
}
}
执行结果如下:
JNI encryptResult=/IeeaSmRmkAGCzmZYvNOKw==
JNI decryptResult=abcdefg1234567
Java encryptResult=/IeeaSmRmkAGCzmZYvNOKw==
Java decryptResult=abcdefg1234567