背景
这几天在学习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