android常用JNI加密(基于openssl)

平时开发过程中,客户端会对数据进行加密,传入服务端,服务端进行解密。在客户端进行加密,密钥就配置到java层,容易被反编译拿到密钥,这里将常用的一些加密算法使用C++实现,密钥配置到JNI里面,减小泄密风险。
项目目录结构:

目录结构.png

openssl:存放opnssl的头文件和库
cpp:配置常量(密钥)、加解密算法调用

实现

CMakeLists.txt

# 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)
#配置加载头文件
include_directories(./openssl/include)
#将源码添加到NATIVE_SRC
aux_source_directory( ./src/main/cpp NATIVE_SRC )
# 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.
             encryption

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
            ${NATIVE_SRC}
            )

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

# 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.
                        encryption
                        openssl
                        ssl
                        android
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

NativeUtils.java

package com.zl;

import android.content.Context;

/**
 * Time: 2021/2/6 0006
 * Author: zoulong
 */
public class NativeUtils {
    static {
        System.loadLibrary("crypto");
        System.loadLibrary("encryption");
    }

    /**
     * aes加密
     * @param src 待加密byte数组
     * @return 加密后的byte数组
     */
    public static native byte[] aesEncryption(byte[] src);

    /**
     * aes解密
     * @param src 待解密数组
     * @return 解密后的byte数组
     */
    public static native byte[] aesCrypt(byte[] src);

    /**
     * rsa私钥加密
     * @param src 待加密byte数组
     * @return 加密后的byte数组
     */
    public static native byte[] rsaEncryptionPrivate(byte[] src);

    /**
     * rsa公钥解密
     * @param src 待解密byte数组
     * @return 解密后的byte数组
     */
    public static native byte[] rsaCryptPublic(byte[] src);

    /**
     * rsa公钥加密
     * @param src 待加密byte数组
     * @return 加密后的byte数组
     */
    public static native byte[] rsaEncryptionPublic(byte[] src);

    /**
     * rsa私钥解密
     * @param src 待解密byte数组
     * @return 解密后的byte数组
     */
    public static native byte[] rsaCryptPrivate(byte[] src);

    /**
     * rsa私钥签名
     * @param src 待签名byte数组
     * @return 签名
     */
    public static native byte[] rsaSignPrivate(byte[] src);

    /**
     * rsa公钥验证签名
     * @param src 待验证字符串byte数组
     * @param sign 签名byte数组
     * @return 1:验证成功 0:失败
     */
    public static native int rsaVerifyPublic(byte[] src, byte[] sign);

    /**
     * 用apk签名的hash1进行验证, 从apk获取的hash1的值与我们配置的常量hash1是否一致
     * @param mContext 上下文
     * @return true一致 false不一致(说明我们的包被反编译了/签名信息被篡改)
     */
    public static native boolean verifyApkSignHash1(Context mContext);
}

encryption.h

//
// Created by Administrator on 2021/2/6 0006.
//
#include 
#ifndef OPENSSL_ENCRYPTION_ENCRYPTION_H
#define OPENSSL_ENCRYPTION_ENCRYPTION_H

#endif //OPENSSL_ENCRYPTION_ENCRYPTION_H

//导入android日志
#define TAG "encrytion"
#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__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG ,__VA_ARGS__)
//aes加密的密钥和iv
const char *AES_SECRET_KEY = "JA2F8AKJF3D7HF16";
const char *AES_IV = "ngshaoyu16geziji";
//rsa的公钥
char *RSA_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n"
                       "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6ZHRaK0mUwyZzBPJeA6q\n"
                       "D3d6xH1jUtB0sZiczNbmOgDiMgbZ0NyGZDzet2skCRWF5qcYnUCbpKC0XULfbjTj\n"
                       "4bJ3GoNzulqVOQESh5RGQefExh+GC/WkyqElDW7eJiNn6vM5AvFA01sk5i1hu5C3\n"
                       "F2V+3mDamVJnvL1qMIMSI3eSNMYIl+mEyKxOA5gzbdDXLBEQbXwkgZ2SSLIM7QbD\n"
                       "eifo9oL9skcFjHwORa4Bi85pUmQp5c9TEuCamtsD5DRTEdUWmlHu27Ur/6+ZuhJ/\n"
                       "ffgt3TGu9CyGbLIMdIua4PitlTZZYYOSCM1mG+YdV+p6PW7YT++mDFJ59OJpIyF0\n"
                       "cQIDAQAB\n"
                       "-----END PUBLIC KEY-----";
//rsa的私钥
char *RSA_PRIVATION_KEY = "-----BEGIN PRIVATE KEY-----\n"
                                "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDpkdForSZTDJnM\n"
                                "E8l4DqoPd3rEfWNS0HSxmJzM1uY6AOIyBtnQ3IZkPN63ayQJFYXmpxidQJukoLRd\n"
                                "Qt9uNOPhsncag3O6WpU5ARKHlEZB58TGH4YL9aTKoSUNbt4mI2fq8zkC8UDTWyTm\n"
                                "LWG7kLcXZX7eYNqZUme8vWowgxIjd5I0xgiX6YTIrE4DmDNt0NcsERBtfCSBnZJI\n"
                                "sgztBsN6J+j2gv2yRwWMfA5FrgGLzmlSZCnlz1MS4Jqa2wPkNFMR1RaaUe7btSv/\n"
                                "r5m6En99+C3dMa70LIZssgx0i5rg+K2VNllhg5IIzWYb5h1X6no9bthP76YMUnn0\n"
                                "4mkjIXRxAgMBAAECggEABFDgeLmyWpiCAwZek6xZsh14FEdo3W/iqCF0zEgwSuQX\n"
                                "SetcfQKGLTX+u47sRIq0RbXSu50lAx7BFnQU4tlxWItOrhu9uLTRyxLc/8panf8l\n"
                                "YK/Wb0QjvmbJ43yn+DZxRiMma4p/sygc/2/ZPXkIGROUC5HomCqwpgkt/CV/4U3c\n"
                                "SOf3KwIQlQBaS67M2s5KOdlHh+ilZnI4Z0dYnx7rcPOd9wBqxlBdsXkRDgGyi+D7\n"
                                "wRVbbQJHRDt6ax8giWLFP3h56EoAtWwNhowHP/n+OUy7tlx5JJUK1s4bAGWCBW5k\n"
                                "dqQXG8Grti40+IE5KvgxxBAy/bPAdRP6xxl1AgSowQKBgQD5Fw8Dnn/Ea8rxF4Ll\n"
                                "pCr0deBRn0mdNl6EwAlKSbRxSJTw40AJVm71xrAaIpdtali0t0VzD/0R38k/zTWX\n"
                                "Emg3kdqznn27JbCsoAESiaocv0awMtsquZgJ0yz+i6e819kkGS6R0/Wl3bgrP9gL\n"
                                "tGcee2YQerEvhWPIFW4OCn3GOQKBgQDwDIn5/L8HzO8KeG358mqwKKjy+vnPJh7z\n"
                                "2XcaetSiINyNZq6aJlTIkP+iT767ijMxhs1uP8sCTqBpr4NAUWpPB4p6f4wd2PLA\n"
                                "W1Y1KqYSJCwZBWkDRA29G4orMAXQTsvYwDGwVPJypbWlrN9rcOm61NsbkQSBxFsr\n"
                                "1NXJWCDf+QKBgDlPw+WaR12DS7tzJGv//N4obQd6te5VPyQeJ0UPdlQGVjaiou5D\n"
                                "E96663Pn9512NZjG/lS+HgVJzz090xHCa3Y1ufNQCS/ROThOzFBemmRo4jPST7kh\n"
                                "4MiJ7TVYHq0FoPF8Vcm50jBqtmBFHUl8JanOzKoIANKlR1MXEy5p3YyJAoGBAJ5Z\n"
                                "pyshb2LV7Voa13FqWLacG9cteF0N6J0zdz4giOPqiZM9iTBm2Mb136xSrp9IKz0g\n"
                                "j6OKsYB0HZ2aChsDmf1IHDFysht+YaRCnDu2RpbxBaX7y6o72lRFNoAGzc78K7xw\n"
                                "DFclskmuxoTj5P4bHhQBFgi5QR/ZR8tCO0T2vbkBAoGBAPkSUCmsGyEsPqGLvPst\n"
                                "I6RnHqVAhbA0d+P7SpgXcJAbP0dmtOoz9e9NC4j8FDkvVc7xOP8ElOreA6AfHu/z\n"
                                "3KMUthDtIUOSRi6L53Ht+52eZTMCLXxp0Ct0tHR8p2vdlPX6X9S9dfu6dBHC3d3V\n"
                                "rfJ+5FI4sUN7AwogkfCE86kP\n"
                                "-----END PRIVATE KEY-----";
//签名hash1
const char *APK_HASH1 = "76778217DECC3B448F1CDDF613F418165A564D4B";
const char digest[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

//定义是否验证签名信息,如果默认为false,调用jni函数前需要验证签名,如果签名验证失败,调用jni函数时抛出异常
#define BOOL int
#define TRUE 1
#define FALSE 0
BOOL IS_SIGNED_FLAG = TRUE;

encryption.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include"encryption.h"
extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_aesEncryption(JNIEnv *env, jobject thiz, jbyteArray src_) {
    // TODO: implement aesEncryption()
    //    LOGD("AES->对称密钥,也就是说加密和解密用的是同一个密钥");
    if(IS_SIGNED_FLAG == FALSE){
        LOGE("ERROR : Signature error or tampering!");
        throw "Signature error or tampering!";
    }
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    int outlen = 0, cipherText_len = 0;

    unsigned char *out = (unsigned char *) malloc((src_Len / 16 + 1) * 16);
    //清空内存空间
    memset(out, 0, (src_Len / 16 + 1) * 16);

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
//    LOGD("AES->指定加密算法,初始化加密key/iv");
//这里可以修改签名算法:EVP_aes_128_cbc/EVP_aes_128_ecb/EVP_aes_128_cfb1/EVP_aes_128_cfb8
    EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, (const unsigned char *) AES_SECRET_KEY, (const unsigned char *) AES_IV);
//    LOGD("AES->对数据进行加密运算");
    EVP_EncryptUpdate(&ctx, out, &outlen, (const unsigned char *) src, src_Len);
    cipherText_len = outlen;

//    LOGD("AES->结束加密运算");
    EVP_EncryptFinal_ex(&ctx, out + outlen, &outlen);
    cipherText_len += outlen;

//    LOGD("AES->EVP_CIPHER_CTX_cleanup");
    EVP_CIPHER_CTX_cleanup(&ctx);

//    LOGD("AES->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(cipherText_len);
//    LOGD("AES->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, cipherText_len, (jbyte *) out);
//    LOGD("AES->释放内存");
    free(out);

    return cipher;
}

extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_aesCrypt(JNIEnv *env, jobject thiz, jbyteArray src_) {
    if(IS_SIGNED_FLAG == FALSE){
        LOGE("ERROR : Signature error or tampering!");
        throw "Signature error or tampering!";
    }
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    int outlen = 0, plaintext_len = 0;

    unsigned char *out  = (unsigned char *) malloc(src_Len);
    memset(out, 0, src_Len);

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
//    LOGD("AES->指定解密算法,初始化解密key/iv");
    EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, (const unsigned char *) AES_SECRET_KEY, (const unsigned char *) AES_IV);
//    LOGD("AES->对数据进行解密运算");
    EVP_DecryptUpdate(&ctx, out, &outlen, (const unsigned char *) src, src_Len);
    plaintext_len = outlen;

//    LOGD("AES->结束解密运算");
    EVP_DecryptFinal_ex(&ctx, out + outlen, &outlen);
    plaintext_len += outlen;

//    LOGD("AES->EVP_CIPHER_CTX_cleanup");
    EVP_CIPHER_CTX_cleanup(&ctx);

//    LOGD("AES->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(plaintext_len);
//    LOGD("AES->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, plaintext_len, (jbyte *) out);
//    LOGD("AES->释放内存");
    free(out);

    return cipher;
}

extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_rsaEncryptionPrivate(JNIEnv *env, jobject thiz, jbyteArray src_) {
//    LOGD("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
    if(IS_SIGNED_FLAG == FALSE){
        LOGE("ERROR : Signature error or tampering!");
        throw "Signature error or tampering!";
    }
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    int ret = 0, src_flen = 0, cipherText_offset = 0, desText_len = 0, src_offset = 0;

    RSA *rsa = NULL;
    BIO *keybio = NULL;

//    LOGD("RSA->从字符串读取RSA私钥");
    keybio = BIO_new_mem_buf(RSA_PRIVATION_KEY, -1);
//    LOGD("RSA->从bio结构中得到RSA结构");
    rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL);
//    LOGD("RSA->释放BIO");
    BIO_free_all(keybio);

    int flen = RSA_size(rsa);
    desText_len = flen * (src_Len / (flen - 11) + 1);

    unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
    unsigned char *cipherText = (unsigned char *) malloc(flen);
    unsigned char *desText = (unsigned char *) malloc(desText_len);
    memset(desText, 0, desText_len);

    memset(srcOrigin, 0, src_Len);
    memcpy(srcOrigin, src, src_Len);

//    LOGD("RSA->对数据进行私钥加密运算");
    //RSA_PKCS1_PADDING最大加密长度:128-11;RSA_NO_PADDING最大加密长度:128
    for (int i = 0; i <= src_Len / (flen - 11); i++) {
        src_flen = (i == src_Len / (flen - 11)) ? src_Len % (flen - 11) : flen - 11;
        if (src_flen == 0) {
            break;
        }

        memset(cipherText, 0, flen);
        ret = RSA_private_encrypt(src_flen, srcOrigin + src_offset, cipherText, rsa, RSA_PKCS1_PADDING);

        memcpy(desText + cipherText_offset, cipherText, ret);
        cipherText_offset += ret;
        src_offset += src_flen;
    }

    RSA_free(rsa);
//    LOGD("RSA->CRYPTO_cleanup_all_ex_data");
    CRYPTO_cleanup_all_ex_data();

//    LOGD("RSA->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(cipherText_offset);
//    LOGD("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, cipherText_offset, (jbyte *) desText);
//    LOGD("RSA->释放内存");
    free(srcOrigin);
    free(cipherText);
    free(desText);

    return cipher;
}

extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_rsaCryptPublic(JNIEnv *env, jobject thiz, jbyteArray src_) {
//    LOGD("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
    if(IS_SIGNED_FLAG == FALSE){
        LOGE("ERROR : Signature error or tampering!");
        throw "Signature error or tampering!";
    }
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    int ret = 0, src_flen = 0, plaintext_offset = 0, desText_len = 0, src_offset = 0;

    RSA *rsa = NULL;
    BIO *keybio = NULL;

//    LOGD("RSA->从字符串读取RSA公钥");
    keybio = BIO_new_mem_buf(RSA_PUBLIC_KEY, -1);
//    LOGD("RSA->从bio结构中得到RSA结构");
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
//    LOGD("RSA->释放BIO");
    BIO_free_all(keybio);

    int flen = RSA_size(rsa);
    desText_len = (flen - 11) * (src_Len / flen + 1);

    unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
    unsigned char *plaintext = (unsigned char *) malloc(flen - 11);
    unsigned char *desText = (unsigned char *) malloc(desText_len);
    memset(desText, 0, desText_len);

    memset(srcOrigin, 0, src_Len);
    memcpy(srcOrigin, src, src_Len);

//    LOGD("RSA->对数据进行公钥解密运算");
    //一次性解密数据最大字节数RSA_size
    for (int i = 0; i <= src_Len / flen; i++) {
        src_flen = (i == src_Len / flen) ? src_Len % flen : flen;
        if (src_flen == 0) {
            break;
        }

        memset(plaintext, 0, flen - 11);
        ret = RSA_public_decrypt(src_flen, srcOrigin + src_offset, plaintext, rsa, RSA_PKCS1_PADDING);

        memcpy(desText + plaintext_offset, plaintext, ret);
        plaintext_offset += ret;
        src_offset += src_flen;
    }

    RSA_free(rsa);
//    LOGD("RSA->CRYPTO_cleanup_all_ex_data");
    CRYPTO_cleanup_all_ex_data();

//    LOGD("RSA->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(plaintext_offset);
//    LOGD("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, plaintext_offset, (jbyte *) desText);
//    LOGD("RSA->释放内存");
    free(srcOrigin);
    free(plaintext);
    free(desText);

    return cipher;
}

extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_rsaEncryptionPublic(JNIEnv *env, jobject thiz, jbyteArray src_) {
//    LOGD("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
    if(IS_SIGNED_FLAG == FALSE){
        LOGE("ERROR : Signature error or tampering!");
        throw "Signature error or tampering!";
    }
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    int ret = 0, src_flen = 0, cipherText_offset = 0, desText_len = 0, src_offset = 0;

    RSA *rsa = NULL;
    BIO *keybio = NULL;

//    LOGD("RSA->从字符串读取RSA公钥");
    keybio = BIO_new_mem_buf(RSA_PUBLIC_KEY, -1);
//    LOGD("RSA->从bio结构中得到RSA结构");
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
//    LOGD("RSA->释放BIO");
    BIO_free_all(keybio);

    int flen = RSA_size(rsa);
    desText_len = flen * (src_Len / (flen - 11) + 1);

    unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
    unsigned char *cipherText = (unsigned char *) malloc(flen);
    unsigned char *desText = (unsigned char *) malloc(desText_len);
    memset(desText, 0, desText_len);

    memset(srcOrigin, 0, src_Len);
    memcpy(srcOrigin, src, src_Len);

//    LOGD("RSA->对数据进行公钥加密运算");
    //RSA_PKCS1_PADDING最大加密长度:128-11;RSA_NO_PADDING最大加密长度:128
    for (int i = 0; i <= src_Len / (flen - 11); i++) {
        src_flen = (i == src_Len / (flen - 11)) ? src_Len % (flen - 11) : flen - 11;
        if (src_flen == 0) {
            break;
        }

        memset(cipherText, 0, flen);
        ret = RSA_public_encrypt(src_flen, srcOrigin + src_offset, cipherText, rsa, RSA_PKCS1_PADDING);

        memcpy(desText + cipherText_offset, cipherText, ret);
        cipherText_offset += ret;
        src_offset += src_flen;
    }

    RSA_free(rsa);
//    LOGD("RSA->CRYPTO_cleanup_all_ex_data");
    CRYPTO_cleanup_all_ex_data();

//    LOGD("RSA->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(cipherText_offset);
//    LOGD("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, cipherText_offset, (jbyte *) desText);
//    LOGD("RSA->释放内存");
    free(srcOrigin);
    free(cipherText);
    free(desText);

    return cipher;
}

extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_rsaCryptPrivate(JNIEnv *env, jobject thiz, jbyteArray src_) {
//    LOGD("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
    if(IS_SIGNED_FLAG == FALSE){
        LOGE("ERROR : Signature error or tampering!");
        throw "Signature error or tampering!";
    }
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    int ret = 0, src_flen = 0, plaintext_offset = 0, descText_len = 0, src_offset = 0;

    RSA *rsa = NULL;
    BIO *keybio = NULL;

//    LOGD("RSA->从字符串读取RSA私钥");
    keybio = BIO_new_mem_buf(RSA_PRIVATION_KEY, -1);
//    LOGD("RSA->从bio结构中得到RSA结构");
    rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL);
//    LOGD("RSA->释放BIO");
    BIO_free_all(keybio);

    int flen = RSA_size(rsa);
    descText_len = (flen - 11) * (src_Len / flen + 1);

    unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
    unsigned char *plaintext = (unsigned char *) malloc(flen - 11);
    unsigned char *desText = (unsigned char *) malloc(descText_len);
    memset(desText, 0, descText_len);

    memset(srcOrigin, 0, src_Len);
    memcpy(srcOrigin, src, src_Len);

//    LOGD("RSA->对数据进行私钥解密运算");
    //一次性解密数据最大字节数RSA_size
    for (int i = 0; i <= src_Len / flen; i++) {
        src_flen = (i == src_Len / flen) ? src_Len % flen : flen;
        if (src_flen == 0) {
            break;
        }

        memset(plaintext, 0, flen - 11);
        ret = RSA_private_decrypt(src_flen, srcOrigin + src_offset, plaintext, rsa, RSA_PKCS1_PADDING);

        memcpy(desText + plaintext_offset, plaintext, ret);
        plaintext_offset += ret;
        src_offset += src_flen;
    }

    RSA_free(rsa);
//    LOGD("RSA->CRYPTO_cleanup_all_ex_data");
    CRYPTO_cleanup_all_ex_data();

//    LOGD("RSA->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(plaintext_offset);
//    LOGD("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, plaintext_offset, (jbyte *) desText);
//    LOGD("RSA->释放内存");
    free(srcOrigin);
    free(plaintext);
    free(desText);

    return cipher;
}

extern "C" JNIEXPORT jbyteArray
Java_com_zl_NativeUtils_rsaSignPrivate(JNIEnv *env, jobject thiz, jbyteArray src_) {
//    LOGD("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jsize src_Len = env->GetArrayLength(src_);

    unsigned int siglen = 0;
    unsigned char digest[SHA_DIGEST_LENGTH];

    RSA *rsa = NULL;
    BIO *keybio = NULL;

//    LOGD("RSA->从字符串读取RSA公钥");
    keybio = BIO_new_mem_buf(RSA_PRIVATION_KEY, -1);
//    LOGD("RSA->从bio结构中得到RSA结构");
    rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL);
//    LOGD("RSA->释放BIO");
    BIO_free_all(keybio);

    unsigned char *sign = (unsigned char *) malloc(129);
    memset(sign, 0, 129);

//    LOGD("RSA->对数据进行摘要运算");
    SHA1((const unsigned char *) src, src_Len, digest);
//    LOGD("RSA->对摘要进行RSA私钥加密");
    RSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH, sign, &siglen, rsa);

    RSA_free(rsa);
//    LOGD("RSA->CRYPTO_cleanup_all_ex_data");
    CRYPTO_cleanup_all_ex_data();

//    LOGD("RSA->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);

    jbyteArray cipher = env->NewByteArray(siglen);
//    LOGD("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
    env->SetByteArrayRegion(cipher, 0, siglen, (jbyte *) sign);
//    LOGD("RSA->释放内存");
    free(sign);
    return cipher;
}

extern "C" JNIEXPORT jint JNICALL
Java_com_zl_NativeUtils_rsaVerifyPublic(JNIEnv *env, jobject thiz, jbyteArray src_, jbyteArray sign_) {
//    LOGD("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
    jbyte *src = env->GetByteArrayElements(src_, NULL);
    jbyte *sign = env->GetByteArrayElements(sign_, NULL);

    jsize src_Len = env->GetArrayLength(src_);
    jsize siglen = env->GetArrayLength(sign_);

    int ret;
    unsigned char digest[SHA_DIGEST_LENGTH];

    RSA *rsa = NULL;
    BIO *keybio = NULL;

//    LOGD("RSA->从字符串读取RSA公钥");
    keybio = BIO_new_mem_buf(RSA_PUBLIC_KEY, -1);
//    LOGD("RSA->从bio结构中得到RSA结构");
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
//    LOGD("RSA->释放BIO");
    BIO_free_all(keybio);

//    LOGD("RSA->对数据进行摘要运算");
    SHA1((const unsigned char *) src, src_Len, digest);
//    LOGD("RSA->对摘要进行RSA公钥验证");
    ret = RSA_verify(NID_sha1, digest, SHA_DIGEST_LENGTH, (const unsigned char *) sign, siglen, rsa);

    RSA_free(rsa);
//    LOGD("RSA->CRYPTO_cleanup_all_ex_data");
    CRYPTO_cleanup_all_ex_data();

//    LOGD("RSA->从jni释放数据指针");
    env->ReleaseByteArrayElements(src_, src, 0);
    env->ReleaseByteArrayElements(sign_, sign, 0);

    return ret;
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_zl_NativeUtils_verifyApkSignHash1(JNIEnv *env, jclass clazz, jobject m_context) {
    // TODO: implement verifyApkSignHash1()
    //上下文对象
    jclass c_clazz = env->GetObjectClass(m_context);
    //反射获取PackageManager
    jmethodID methodID = env->GetMethodID(c_clazz, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jobject package_manager = env->CallObjectMethod(m_context, methodID);
    if (package_manager == NULL) {
//        LOGD("sha1OfApk->package_manager is NULL!!!");
        return NULL;
    }

    //反射获取包名
    methodID = env->GetMethodID(c_clazz, "getPackageName", "()Ljava/lang/String;");
    jstring package_name = (jstring) env->CallObjectMethod(m_context, methodID);
    if (package_name == NULL) {
//        LOGD("sha1OfApk->package_name is NULL!!!");
        return NULL;
    }
    env->DeleteLocalRef(c_clazz);

    //获取PackageInfo对象
    jclass pack_manager_class = env->GetObjectClass(package_manager);
    methodID = env->GetMethodID(pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    env->DeleteLocalRef(pack_manager_class);
    jobject package_info = env->CallObjectMethod(package_manager, methodID, package_name, 0x40);
    if (package_info == NULL) {
//        LOGD("sha1OfApk->getPackageInfo() is NULL!!!");
        return NULL;
    }
    env->DeleteLocalRef(package_manager);

    //获取签名信息
    jclass package_info_class = env->GetObjectClass(package_info);
    jfieldID fieldId = env->GetFieldID(package_info_class, "signatures", "[Landroid/content/pm/Signature;");
    env->DeleteLocalRef(package_info_class);
    jobjectArray signature_object_array = (jobjectArray) env->GetObjectField(package_info, fieldId);
    if (signature_object_array == NULL) {
//        LOGD("sha1OfApk->signature is NULL!!!");
        return NULL;
    }
    jobject signature_object = env->GetObjectArrayElement(signature_object_array, 0);
    env->DeleteLocalRef(package_info);

    //签名信息转换成sha1值
    jclass signature_class = env->GetObjectClass(signature_object);
    methodID = env->GetMethodID(signature_class, "toByteArray", "()[B");
    env->DeleteLocalRef(signature_class);

    jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature_object, methodID);
    jclass byte_array_input_class = env->FindClass("java/io/ByteArrayInputStream");
    methodID = env->GetMethodID(byte_array_input_class, "", "([B)V");
    jobject byte_array_input = env->NewObject(byte_array_input_class, methodID, signature_byte);
    jclass certificate_factory_class = env->FindClass("java/security/cert/CertificateFactory");
    methodID = env->GetStaticMethodID(certificate_factory_class, "getInstance", "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
    jstring x_509_jstring = env->NewStringUTF("X.509");
    jobject cert_factory = env->CallStaticObjectMethod(certificate_factory_class, methodID, x_509_jstring);
    methodID = env->GetMethodID(certificate_factory_class, "generateCertificate", ("(Ljava/io/InputStream;)Ljava/security/cert/Certificate;"));
    jobject x509_cert = env->CallObjectMethod(cert_factory, methodID, byte_array_input);
    env->DeleteLocalRef(certificate_factory_class);

    jclass x509_cert_class = env->GetObjectClass(x509_cert);
    methodID = env->GetMethodID(x509_cert_class, "getEncoded", "()[B");
    jbyteArray cert_byte = (jbyteArray) env->CallObjectMethod(x509_cert, methodID);
    env->DeleteLocalRef(x509_cert_class);

    jclass message_digest_class = env->FindClass("java/security/MessageDigest");
    methodID = env->GetStaticMethodID(message_digest_class, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;");
    jstring sha1_jstring = env->NewStringUTF("SHA1");
    jobject sha1_digest = env->CallStaticObjectMethod(message_digest_class, methodID, sha1_jstring);
    methodID = env->GetMethodID(message_digest_class, "digest", "([B)[B");
    jbyteArray sha1_byte = (jbyteArray) env->CallObjectMethod(sha1_digest, methodID, cert_byte);
    env->DeleteLocalRef(message_digest_class);

    //转换成char
    jsize arraySize = env->GetArrayLength(sha1_byte);
    jbyte *sha1 = env->GetByteArrayElements(sha1_byte, NULL);
    char *hex = new char[arraySize * 2 + 1];
    for (int i = 0; i < arraySize; ++i) {
        hex[2 * i] = digest[((unsigned char) sha1[i]) / 16];
        hex[2 * i + 1] = digest[((unsigned char) sha1[i]) % 16];
    }
    hex[arraySize * 2] = '\0';

//    LOGD("sha1OfApk->sha1 %s ", hex);

    //比较签名
    if (strcmp(hex, APK_HASH1) == 0) {
//        LOGD("sha1OfApk->签名验证成功");
        IS_SIGNED_FLAG = TRUE;
        return static_cast(true);
    }
//    LOGD("sha1OfApk->签名验证失败");
    IS_SIGNED_FLAG = FALSE;
    return static_cast(false);
}

工具类没得啥好说的,代码注释挺详细的,可以参考着来看
代码传送门

你可能感兴趣的:(android常用JNI加密(基于openssl))