日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

GmSSL3.0 在Android上的命令行风格封装

發布時間:2023/12/10 Android 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GmSSL3.0 在Android上的命令行风格封装 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

GmSSL 3.0 Java簡易封裝

3.0版編譯說明

按照 GmSSL庫 的說明,新版3.0的編譯使用cmake。在Android上使用cmake編譯基本上只改動一點點就可以很方便移植成功

這篇文章拋磚引玉,演示一下對gmssl命令行的java封裝, AS版本為 2021.1.1 patch2,gradle 為 4.1.1,注意此demo只是把原先gmssl的字符串傳給編譯它的main函數,所以如果命令涉及其他命令會報錯。比如下面的echo 是不支持,利用管道把結果當作gmssl的輸入是行不通的

echo hello | gmssl sm2encrypt -pubkey sm2pub.pem -out sm2.der

一、移植步驟預覽

1. 新建Android項目 和 Demo 測試用的UI界面 2. 新建Native Library Module3. 改寫CMakeLists.txt 文件4. 編寫JNI 和 java封裝類

這里UI不是我們主要關心的,所以只講第2、3、4點,具體的工程項目 https://download.csdn.net/download/b2259909/85708892

二、Native Library Module

  • 新建C++庫模塊
  • 配置C++模塊的build.gradle
  • 注意 ndkVersion 太高可能不支持 armeabi,另外可能報錯so庫重復之類的,加個packagingOption


    4. 將 GmSSL-Master.zip 或者git clone后的文件拷貝到項目中

    5. 修改 CMakeLists.txt 文件

    注意: 由于我打算支持類似命令行的調用方式,所以需要把tools下的文件也編譯進去,同時我們修改一下 gmssl.c中的 main函數為 gmssl_main方法,并新增一個 gmssl.h文件,在里面聲明這個方法。等會在gmsslNative.c中調用它,具體看github工程

  • 編寫JNI的Java類

    package com.zbh.gmssl;import android.util.Log;import java.util.ArrayList;import java.util.regex.Matcher;import java.util.regex.Pattern;public class GmSSLv3 {public static StringBuffer resultBuffer;static {resultBuffer = new StringBuffer();System.loadLibrary("gmssl.v3.Alpha");}/*** 發送命令給 gmssl* 執行結果可以使用 resultBuffer.toString()獲取* @param cmdLine 終端的命令字符串* @return */public static int cmdLine(String cmdLine){//TODO 這里提前用正則表達式處理字符串參數 --> 字符串數組。Log.d("zbh","cmdLine = " + cmdLine);resultBuffer.setLength(0);//處理參數ArrayList<String> list= new ArrayList<>();Matcher matcher = Pattern.compile("\"(\\\\\"|[^\"])*?\"|[^ ]+").matcher(cmdLine);while(matcher.find()) {list.add(matcher.group());}String[] cmdArgs = new String[list.size()];for (int i = 0; i < list.size(); i++) {cmdArgs[i] = list.get(i);}return cmdLine(cmdArgs,resultBuffer);}private static native int cmdLine(String[] cmdString,StringBuffer stringBuffer);}
  • 編寫JNI的C文件

  • 這里比較有趣的是:
    為了能像使用命令行一樣執行,上層java已經將命令行參數按照空格分割成數組。
    C層還需要組織成 二級指針的 argv 和 argc ,以便調用 int gmssl_main(int argc, char **argv)
    而且為了不需要改動原GMSSL的代碼就能從內存拿到結果,我這里使用了 dlsym方法將標準庫的 printf 和 fprintf 給hook了,因為gmssl中的結果是輸出到stdout和stderr了,之后我就能將結果(字符串)返回到java層了,當然如果某命令是涉及讀寫文件,文件路徑必須是有權限的才能成功。

    #include <jni.h> #include <android/log.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <gmssl/gmssl.h> #include <dlfcn.h>#define LOG_TAG "GMSSL_JNI" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define NUM_ARRAY_ELEMENTS(p) ((int) sizeof(p) / sizeof(p[0]))//hook fprintf typedef int(*fprintf_t)(FILE * __restrict , const char * __restrict, ...); typedef int(*printf_t)(const char * __restrict, ...); fprintf_t fprintf_old = NULL; printf_t printf_f=NULL;static JavaVM *sg_javaVm = NULL; static jobject sg_strbuf=NULL;int fprintf(FILE * __restrict file, const char * __restrict format, ...){if(fprintf_old == NULL){fprintf_old = dlsym(RTLD_NEXT, "fprintf"); //除了RTLD_NEXT 還有一個參數RTLD_DEFAULT}JNIEnv *env;int isAttached = 0;int getEnvStat = (*sg_javaVm)->GetEnv(sg_javaVm, (void**)&env, JNI_VERSION_1_6);if (getEnvStat != JNI_OK) {if (getEnvStat == JNI_EDETACHED) {__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,"GetEnv: not attached");if ((*sg_javaVm)->AttachCurrentThread(sg_javaVm, (JNIEnv**)&env, 0) != 0) {__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,"Failed to attach");return 0;}isAttached = 1;__android_log_print(ANDROID_LOG_INFO,LOG_TAG,"AttachCurrentThread, GetEnv");} else if (getEnvStat == JNI_EVERSION) {__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,"GetEnv: version not supported");return 0;}else{__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,"GetEnv: other error");return 0;}}//獲取java StringBuffer類jclass cls_sb = (*env)->GetObjectClass(env,sg_strbuf);//獲取StringBuffer的 setLength 和 add方法jmethodID mid_setLength = (*env)->GetMethodID(env,cls_sb,"setLength","(I)V");jmethodID mid_append = (*env)->GetMethodID(env,cls_sb,"append","(Ljava/lang/String;)Ljava/lang/StringBuffer;");//調用StringBuffer 的 setLength方法,先清空//(*env)->CallVoidMethod (env,sg_strbuf, mid_setLength, 0);char *parg;va_list argptr;va_start(argptr,format);vasprintf(&parg,format,argptr);jstring rstring = (*env)->NewStringUTF(env,parg);(*env)->CallObjectMethod (env,sg_strbuf, mid_append, rstring);va_end(argptr);if (isAttached==1){(*sg_javaVm)->DetachCurrentThread(sg_javaVm);}(*env)->DeleteLocalRef(env, cls_sb);(*env)->DeleteLocalRef(env, rstring);return fprintf_old(file,"%s",parg); }int printf(const char * __restrict format, ...){if(printf_old == NULL){printf_old = dlsym(RTLD_NEXT, "printf");}char *parg;va_list argptr;va_start(argptr,format);vasprintf(&parg,format,argptr);LOGD(parg);va_end(argptr);return printf_old("%s",parg); }JNIEXPORT jint JNICALL cmdLine(JNIEnv *env, jclass thiz, jobjectArray cmdString,jobject strbuf){sg_strbuf = strbuf;if (cmdString == NULL || (*env)->GetArrayLength(env,cmdString) == 0)return -999;jsize size = (*env)->GetArrayLength(env,cmdString);int argc = size;int** p = (int **)malloc(size*sizeof(int*));//申請 存放指針的數組for(int i=0;i<size;i++){jstring* obj = (jstring *)(*env)->GetObjectArrayElement(env,cmdString,i);char *value = (char *)(*env)->GetStringUTFChars(env,obj,NULL);//得到字符串int len = strlen(value)+1;p[i] = (char*)malloc(len);//再申請字符串長度的內存空間memset(p[i],0,len);memcpy(p[i],value, strlen(value));(*env)->ReleaseStringUTFChars(env,obj,value); //釋放引用}int ret = gmssl_main(argc, p);for(int i=0; i<size; i++){//printf("cmd[%d] = %s",i,p[i]);free(p[i]);//記住要釋放內存}free(p);//釋放兩次,兩次釋放的不一樣return ret; }static JNINativeMethod methods[] = {{"cmdLine", "([Ljava/lang/String;Ljava/lang/StringBuffer;)I", (void *)cmdLine}, };jint registerNativeMethods(JNIEnv *env, const char *class_name, JNINativeMethod *methods,int num_methods) {jclass clazz = (*env)->FindClass(env,class_name);if (clazz == NULL) {LOGD("registerNativeMethods: class'%s' not found", class_name);return JNI_FALSE;}jint result = (*env)->RegisterNatives(env,clazz, methods, num_methods);if (result < 0) {LOGD("registerNativeMethods failed(class=%s)", class_name);return JNI_FALSE;}return result; }JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {LOGD("JNI_OnLoad");sg_javaVm = vm;JNIEnv *env = NULL;//獲取JNIEnvif ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {LOGD("JNI_OnLoad GetEnv fail");return -1;}assert(env != NULL);const char *className = "com/zbh/gmssl/GmSSLv3";registerNativeMethods(env, className, methods, NUM_ARRAY_ELEMENTS(methods));return JNI_VERSION_1_6; }
  • 寫個測試

    當然如果你想調用Gmssl的方法,可以在jni的c和java文件中自行增加接口和實現。
  • 總結

    以上是生活随笔為你收集整理的GmSSL3.0 在Android上的命令行风格封装的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。