平时开发过程中,客户端会对数据进行加密,传入服务端,服务端进行解密。在客户端进行加密,密钥就配置到java层,容易被反编译拿到密钥,这里将常用的一些加密算法使用C++实现,密钥配置到JNI里面,减小泄密风险。
项目目录结构:
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 <android/log.h>
#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 <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <openssl/hmac.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/md5.h>
#include <jni.h>
#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, "<init>", "([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<jboolean>(true);
}
// LOGD("sha1OfApk->签名验证失败");
IS_SIGNED_FLAG = FALSE;
return static_cast<jboolean>(false);
}
工具类没得啥好说的,代码注释挺详细的,可以参考着来看
代码传送门