上一篇提到了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; } }