一、任务目标:服务器端对一个32位密钥加密,传到客户端后再解密。加解密算法已知,是C++所写。
二、简单分析:
1、服务器端:myeclipse+tomcat+mysql+servlet搭建的简单服务器,存放加密视频,和加密的密钥。
2、客户端:android客户端,获取加密的密钥再解密,然后再利用解密后的密钥解密视频
3、服务器客户端都是java,加解密是C++,所以采用jni技术,视频加密解密不再多说,主要研究字符串的加解密
三、服务器端java+jni:
与android jni调so不用 ,java 可以调用dll,所以大致步奏如下
1、生成jni头文件
打开命令行窗口 跳转到java源代码目录,然后生成.class文件,命令
javac ****.java
再生成头文件:命令
javah -jni ****(没有扩展名)
2、生成dll
打开VS2012创建dll工程,把上面头文件导入,并导入jni.h jni_md.h等头文件,总之提示没啥就去jdk里面找吧(C:\Program Files\Java\jdk1.7.0_60\include)。项目如下:
AES_Jni_Test.h是刚才生成的头文件,下面的cpp就是具体实现接口函数,enctypt_and_decrypt_algorithm就是已知的加解密的源代码。
之后选择所在平台需要的dll(32or64位)
再把生成的dll放入C:\Program Files\Java\jdk1.7.0_60\bin
最后在java代码中先调用System.loadLibrary(“****”);(DLL文件名,没有扩展名)
3、参数传递
上层java给C++传递的参数有3个,len是加密字符串长度,byte是加密后的字符串,这里有byte[],没用string,是减少麻烦,因为java和c++的数据类型不同,
字符串编码也不同,传字符串问题很多,key就是密钥
package AES.Jni;
public class Test {
static {
System.loadLibrary("AES");
}
public native static byte[] encrypt(int len,byte[] str,String key);
public native static byte[] decrypt(int len,byte[] str,String key);
}
C++接口 AES_Jni_Test.cpp,接收上面的参数,并调用C++解密代码(实现未给出),注意的就是C和C++中jniEnv是不同的 格式也不同<pre name="code" class="cpp">JNIEXPORT jbyteArray JNICALL Java_AES_Jni_Test_encrypt
(JNIEnv *jniEnv, jclass, jint length, jbyteArray jarrByte,jstring str)
{
//接收明文字节数组
jbyte* pJbyte = jniEnv->GetByteArrayElements(jarrByte, 0);
if (pJbyte == NULL) {
return NULL;
}
char* szByte = (char *)pJbyte;
//接受密钥
const char * src = jniEnv->GetStringUTFChars(str, NULL);
char* skey=(char *)src;
char buf[255];
//存明文到buffer并加密
memset( buf, 0, sizeof(buf) );
strcpy( buf, szByte );
buf[length] = '\0';
//生成结果并返回
jbyteArray jarrRet = jniEnv->NewByteArray( strlen(buf) );
strcpy( szByte, buf );
jniEnv->SetByteArrayRegion( jarrRet, 0, strlen(buf), (jbyte *)szByte );
jniEnv->ReleaseByteArrayElements(jarrRet, pJbyte, 0);
return jarrRet;
}
JNIEXPORT jbyteArray JNICALL Java_AES_Jni_Test_decrypt(JNIEnv *jniEnv,jclass,jint length,jbyteArray jarrByte,jstring str){
//接收明文
jbyte* pJbyte = jniEnv->GetByteArrayElements(jarrByte, 0);
if (pJbyte == NULL) {
return NULL;
}
char* szByte = (char *)pJbyte;
const char * src = jniEnv->GetStringUTFChars(str, NULL);
char* skey=(char *)src;
char buf[255];
//存明文到buffer并加密
memset( buf, 0, sizeof(buf) );
strcpy( buf, szByte );
buf[length] = '\0';
//生成结果并返回
jbyteArray jarrRet = jniEnv->NewByteArray( strlen(buf) );
strcpy( szByte, buf );
jniEnv->SetByteArrayRegion( jarrRet, 0, strlen(buf), (jbyte *)szByte );
jniEnv->ReleaseByteArrayElements(jarrRet, pJbyte, 0);
return jarrRet;
}
4、测试
private void testAES() {
SkeyDecrypt skeydecrypt=new SkeyDecrypt();//类似上面的TEST累,加载dll,声明native方法
String Key = "0123456789abcdef0123456789abcdee";
try{
byte[] plainSkeyBytes = Key.getBytes("utf-8");//到底用哪个字符集要看具体情况,没中文用iso-8859-1的单字节编码更好
int len = plainSkeyBytes.length;
System.out.println("plainTextBytes length is: " + len);
byte[] encryptedTextBytes = skeydecrypt.encrypt(len, plainSkeyBytes,skey_decrpyt);
String encryptedText = new String(encryptedTextBytes, "utf-8");
System.out.println("encryptedText is: " + encryptedText);
System.out.println("----------------------------");
byte[] decryptedSkeyBytes = skeydecrypt.decrypt(len,encryptedTextBytes,skey_decrpyt);
//将字符数组转换为String
System.out.println("decryptedTextBytes length is: " +decryptedSkeyBytes.length);
String decryptedText = new String(decryptedSkeyBytes, "utf-8");
System.out.println("decryptedText is: " + decryptedText);
System.out.println();
System.out.println("------------ END ----------------");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
四、客户端
android平台jni,前面有文章介绍过了,简单说下,要生成so库,windows用cygwin,linux写脚本
以Windows为例,进入项目jni目录,用ndk编译,生成so后,载入就可以使用了。
五、字符串编码和字符集
可以参考 http://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html