android 安全之——文件加密jni实现
上一篇提到了AES加密方式基本实现,这一篇我们不得提出一个问题,就是代码的安全性。我们知道java层代码很容易被反编译,很有可能泄漏我们加密方式与密钥内容,那我们该怎么办呢?我们可以使用c/c++实现加密,编译成So库的形式,可供java实现调用,这样就大大增强程序安全性,因为so反编译结果是arm指令,没有java中smali那么易懂。
完全使用c/c++实现可能会比较麻烦,其实我们也可以简化一部分,只将密钥使用jni实现,其它还是用java实现,这样会简单一些,下面是具体操作;
核心密钥jni实现:
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
const char keyValue[] = {
21, 25, 21, -45, 25, 98, -55, -45, 10, 35, -45, 35,
26, -5, 25, -65, -78, -99, 85, 45, -5, 10, -0, 11,
-35, -48, -98, 65, -32, 14, -67, 25, 36, -56, -45, -5,
12, 15, 35, -15, 25, -14, 62, -25, 33, -45, 55, 12, -8,
};
const char iv[] = { //16 bit
-33, 32, -25, 25, 35, -27, 55, -12, -15,32,
23, 45, -26, 32, 5,16
};
jbyteArray Java_com_xiaoyu_jnirelate_JNIDeclaration_getKeyValue(JNIEnv *env, jobject obj)
{
jbyteArray kvArray = (*env)->NewByteArray(env, sizeof(keyValue));
jbyte *bytes = (*env)->GetByteArrayElements(env,kvArray,0);
int i;
for (i = 0; i < sizeof(keyValue);i++){
bytes[i] = (jbyte)keyValue[i];
}
(*env)->SetByteArrayRegion(env,kvArray, 0, sizeof(keyValue),bytes);
(*env)->ReleaseByteArrayElements(env,kvArray,bytes,0);
return kvArray;
}
//JNIEXPORT JNICALL
jbyteArray Java_com_xiaoyu_jnirelate_JNIDeclaration_getIv(JNIEnv *env, jobject obj)
{
jbyteArray ivArray = (*env)->NewByteArray(env, sizeof(iv));
jbyte *bytes = (*env)->GetByteArrayElements(env,ivArray, 0);
int i;
for (i = 0; i < sizeof(iv); i++){
bytes[i] = (jbyte)iv[i];
}
(*env)->SetByteArrayRegion(env,ivArray, 0, sizeof(iv), bytes);
(*env)->ReleaseByteArrayElements(env,ivArray,bytes,0);
return ivArray;
}
本地方法声明:
public class JNIDeclaration {
//AES Key
public native byte[] getKeyValue();
public native byte[] getIv();
}
java加解密实现:
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
public class SecurityMag {
private SecretKey key;
private AlgorithmParameterSpec paramSpec;
private Cipher cipher;
public SecurityMag(SecretKey mkey,AlgorithmParameterSpec mparamSpec,Cipher mcipher){
this.key=mkey;
this.paramSpec=mparamSpec;
this.cipher=mcipher;
}
public String encode(String msg) {
String strHex = "";
try {
// 用密钥和一组算法参数初始化此 cipher
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
// 对要加密的内容进行编码处理,
strHex = byte2hex(cipher.doFinal(msg.getBytes()));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return strHex;
}
public String decode(String value) {
String strContent="";
try {
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
// 对要解密的内容进行编码处理
strContent = new String(cipher.doFinal(hex2byte(value)));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return strContent;
}
/**
* 将二进制转化为16进制字符串
*
* @param b
* 二进制字节数组
* @return String
*/
public String byte2hex(byte[] b) {
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
}
/**
* 十六进制字符串转化为2进制
*
* @param hex
* @return
*/
public byte[] hex2byte(String hex) {
byte[] ret = new byte[8];
byte[] tmp = hex.getBytes();
for (int i = 0; i < 8; i++) {
ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
}
return ret;
}
/**
* 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF
*
* @param src0
* byte
* @param src1
* byte
* @return byte
*/
public byte uniteBytes(byte src0, byte src1) {
byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 }))
.byteValue();
_b0 = (byte) (_b0 << 4);
byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 }))
.byteValue();
byte ret = (byte) (_b0 ^ _b1);
return ret;
}
}
android 调用测试:
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class TestActivity extends Activity {
final String tag="TestActivity";
private byte[] keyValue;
private byte[] iv;
private JNIDeclaration declaration_native;
private SecurityMag secMag;
static {
System.loadLibrary("aeslib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initKey();
String mimi="hello are you good!";
String encrypmimi=encryContent(mimi);
decrypContent(encrypmimi);
}
private void initKey(){
declaration_native=new JNIDeclaration();
keyValue = declaration_native.getKeyValue();
iv = declaration_native.getIv();
if (null != keyValue && null != iv) {
KeyGenerator kgen;
try {
kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(keyValue));
SecretKey key = kgen.generateKey();
IvParameterSpec paramSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
secMag=new SecurityMag(key,paramSpec,cipher);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
}
String encryContent(String str){
String encrypStr=secMag.encode(str);
Log.i(tag, "encryption content "+encrypStr);
return encrypStr;
}
String decrypContent(String str){
String decrypStr=secMag.decode(str);
Log.i(tag, "encryption content "+decrypStr);
return decrypStr;
}
}

浙公网安备 33010602011771号