生活随笔
收集整理的這篇文章主要介紹了
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");}public static int cmdLine(String cmdLine){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]))
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"); }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;}}jclass cls_sb
= (*env
)->GetObjectClass(env
,sg_strbuf
);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;");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
++){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;if ((*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上的命令行风格封装的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。