Android使用JNI实现本地数据加解密

why

1、Apk包很容易被破解(应该有共识)

2、Java语言不安全,容易被反编译从而看到源码

3、C语言相对安全,因为编译后是二进制文件

4、秘钥不应该在java代码中保存,容易被反编译看到,所以秘钥必须放在C中

5、只把秘钥放在C中也不安全,因为如果反编译看到java调用so包方法并且拿到so包也是可以拿到秘钥的,所以没有意义。因此需要把整个加解密过程都放在C端。

6、如果旧的版本使用java加密,而新的版本使用Jni加密,那么就得考虑兼容性的问题,即Java和JNI可以互相加解密

how

JNI使用配置过程我就不说了网上很多,直接从代码开始。

demo目录 如图:
TimLine图片20181123152201.png

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

源码Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 14,466评论 2 59
  • 本文主要介绍移动端的加解密算法的分类、其优缺点特性及应用,帮助读者由浅入深地了解和选择加解密算法。文中会包含算法的...
    苹果粉阅读 13,954评论 5 29
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 175,008评论 25 709
  • 雨后,青苔生密的石板路上,少年迎面走来。 一瞥黑发及耳鬓,两眼青衣宽身,三低头小心石板下的水洼,四目对视匆匆擦肩而...
    Miss_kiwi阅读 3,921评论 4 5
  • 前言: 其实,在看到这个题目的时候我就已经开始浮想联翩了。 陌生,到底是怎样的一种感觉呢? 又或者是一种气质? 陌...
    穆勒书信时光阅读 3,229评论 4 2