android中使用jni对字符串加解密实现分析
近期项目有个需求。就是要对用户的敏感信息进行加密处理,比方用户的账户password,手机号等私密信息。在java中,就对字符串的加解密我们能够使用AES算法加密字符串。使用它的优点就不必多说了,但我们又知道android的源码是能够被反编译的,所以使用纯Java方式的AES加密是不安全的,所以想到了使用android中的jni来对字符串加解密处理,它会产生一个.so文件,更重要的是它通过C/C++代码实现。所以安全行比較高。它能够被反编译成机器码,但差点儿不能被还原反编译。那么以下就具体介绍下这样的的加密处理。
鉴于全然使用C/C++代码进行字符串的加解密,我们须要考虑不同系统平台上数据类型的差异问题。这里推荐还有一种易于实现的方法。即使用Java中的AES加解密逻辑,而将AES加解密所须要的核心秘钥放入到C中,通过调用jni来从静态类库中读取须要的秘钥,详细实现例如以下:
项目代码结构图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2h5XzIwMTJfZ29nbw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
Java中的AES算法逻辑:
publicclass SecurityUtil {
privatestaticbyte[]keyValue;
privatestaticbyte[]iv;
privatestatic SecretKeykey;
privatestatic AlgorithmParameterSpecparamSpec;
privatestatic Cipherecipher;
static {
System.loadLibrary("cwtlib");
keyValue = getKeyValue();
iv = getIv();
if(null
!= keyValue &&
null !=iv)
{
KeyGeneratorkgen;
try {
kgen = KeyGenerator.getInstance("AES");
// Java项目中的写法
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
// Android项目中的写法
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","Crypto");
random.setSeed(keyValue);
kgen.init(128,random);
key =kgen.generateKey();
paramSpec =new
IvParameterSpec(iv);
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmExceptione)
{
} catch (NoSuchPaddingExceptione)
{
}
}
}
publicstaticnativebyte[]
getKeyValue();
publicstaticnativebyte[]
getIv();
publicstatic String
encode(Stringmsg) {
String str ="";
try {
//用密钥和一组算法參数初始化此 cipher
ecipher.init(Cipher.ENCRYPT_MODE,key,paramSpec);
//加密并转换成16进制字符串
str = asHex(ecipher.doFinal(msg.getBytes()));
} catch (BadPaddingExceptione)
{
} catch (InvalidKeyExceptione)
{
} catch (InvalidAlgorithmParameterExceptione)
{
} catch (IllegalBlockSizeExceptione)
{
}
returnstr;
}
publicstatic String
decode(Stringvalue) {
try {
ecipher.init(Cipher.DECRYPT_MODE,key,paramSpec);
returnnew String(ecipher.doFinal(asBin(value)));
} catch (BadPaddingExceptione)
{
} catch (InvalidKeyExceptione)
{
} catch (InvalidAlgorithmParameterExceptione)
{
} catch (IllegalBlockSizeExceptione)
{
}
return"";
}
privatestatic String
asHex(bytebuf[]) {
StringBuffer strbuf =new
StringBuffer(buf.length * 2);
inti;
for (i = 0;i
<buf.length;i++)
{
if (((int)buf[i]
& 0xff) < 0x10)//小于十前面补零
strbuf.append("0");
strbuf.append(Long.toString((int)buf[i]
& 0xff, 16));
}
returnstrbuf.toString();
}
privatestaticbyte[]
asBin(Stringsrc) {
if (src.length()
< 1)
returnnull;
byte[]encrypted
=newbyte[src.length()
/ 2];
for (inti
= 0;i <src.length() / 2;i++)
{
inthigh = Integer.parseInt(src.substring(i
* 2, i * 2 + 1), 16);//取高位字节
intlow = Integer.parseInt(src.substring(i
* 2 + 1, i * 2 + 2), 16);//取低位字节
encrypted[i]
= (byte) (high * 16 +low);
}
returnencrypted;
}
C中的读取秘钥的实现:
#include<stdio.h>
#include"cwtlib.h"
constchar 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
};
constchar iv[] = {
-33, 32, -25, 25, 35, -27, 55, -12, -15, 23, 45, -26, 32, 5 - 2, 74, 54
};
JNIEXPORT jbyteArray JNICALL Java_com_cwtlib_aesencript_SecurityUtil_getKeyValue
(JNIEnv *env, jclass 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 jbyteArray JNICALL Java_com_cwtlib_aesencript_SecurityUtil_getIv
(JNIEnv *env, jclass 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;
}
在android中怎样调用:
publicclass MainActivityextends
Activity {
privatestaticfinal
StringTAG ="MainActivity";
private StringencriptStr
="18721002361";//加密的字符串
@Override
protectedvoid
onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//加密后
String
enstr = SecurityUtil.encode(encriptStr);
Log.d(TAG,"加密后:"
+enstr);
//解密后
String
destr = SecurityUtil.decode(enstr);
Log.d(TAG,"解密后:"
+destr);
}
}
这里以一个手机号为例进行加解密处理,详细的效果图能够在日志中查看到,详细例如以下。
加解密的对比效果图:
好了,到这里我已经罗列出了全部主要文件实现,假设有问题能够在评论或是群(179914858)中进行讨论。另外,原创作品来自不易,转载请注明出处,谢谢。
最后附上原代码供參考,下载地址:请点这里