背景
这几天在学习Android开发里的ndk开发,今天拿文件的加密解密做为练习。
功能很简单,俩按钮,一个加密一个解密,加密方式就是拿文件的字符跟密钥进行异或,解密方式也是异或
native层
首先动态注册,把java的native方法跟c函数绑定,详情参见文章安卓开发学习之ndk动态注册
以加密为例,我对应的c函数是fileEncrypt(),完整代码如下
jstring fileEncrypt(JNIEnv *env, jclass type, jstring filePath) { __android_log_print(ANDROID_LOG_INFO, "native", "进入加密过程"); fileIndex = 1; const char *path = (*env).GetStringUTFChars(filePath, JNI_FALSE); const char *newPath = getNewName(path); __android_log_print(ANDROID_LOG_INFO, "native", "new file path:%s", newPath); FILE *frp = fopen(path, "rb"); FILE *fwp = fopen(newPath, "wb"); if (frp == NULL) { __android_log_print(ANDROID_LOG_INFO, "native", "%s:此文件不存在或没有读权限", path); return NULL; } if (fwp == NULL) { __android_log_print(ANDROID_LOG_INFO, "native", "%s:没有写权限", newPath); return NULL; } int buf; int i = 0; while ((buf = fgetc(frp)) != EOF) { fputc(buf ^ key[i % keyLen], fwp); i++; } fclose(frp); fclose(fwp); free((void *) path); __android_log_print(ANDROID_LOG_INFO, "native", "文件加密结束"); return env->NewStringUTF(newPath); }
调用了getName()方法设定新文件的名字,代码如下
char *getNewName(const char *oldName) { int len = strlen(oldName); char *newName = new char[len + 1]; int i = 0; for (; i < len && oldName[i] != '.'; ++i) { newName[i] = oldName[i]; } newName[i] = '1' + fileIndex; fileIndex++; for (; i < len; ++i) { newName[i+1] = oldName[i]; } return newName; }
而后涉及的密钥信息如下
const char *key = "101010101"; int keyLen = strlen(key); int fileIndex = 1;
可以看到,所谓的加密就是跟复制粘贴一样的流程化代码,无需赘言
java层
首先在清单文件里声明写外存权限
而后由于Android6.0以后要动态申请权限,所以还要在MainActivity里进行相关操作,代码如下:
public class MainActivity extends Activity { public static final String TAG = "MainActivity"; private Button btn_encrypt, btn_decrypt; private String filePath = Environment.getExternalStorageDirectory() + "/new.jpg"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_encrypt = (Button)findViewById(R.id.btn_encrypt); btn_decrypt = (Button)findViewById(R.id.btn_decrypt); btn_encrypt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i(TAG, "待加密文件路径:"+filePath); if (!TextUtils.isEmpty(filePath) && new File(filePath).exists()) { filePath = FileUtil.fileEncrypt(filePath); } } }); btn_decrypt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i(TAG, "待解密文件路径:"+filePath); if (!TextUtils.isEmpty(filePath) && new File(filePath).exists()) { filePath = FileUtil.fileDecrypt(filePath); } } }); if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); btn_decrypt.setClickable(false); btn_encrypt.setClickable(false); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 0) { if (checkSelfPermission(permissions[0]) == PackageManager.PERMISSION_GRANTED) { btn_decrypt.setClickable(true); btn_encrypt.setClickable(true); } } } }
调用的FileUtil类的代码如下
public class FileUtil { static { System.loadLibrary("native-lib"); } public static native String fileEncrypt(String filePath); public static native String fileDecrypt(String filePath); }
代码很简单,只是做一下记录
结语和代码地址
网上还有文件的分割和合并的案例,其实本质也是文件的IO,所以此处略过
代码地址:
https://github.com/songzeceng/first/tree/file_encrypt_decrypt