我有一个使用 tomcat 7 并调用 JNI dll 使用 Openssl EVP 解密一些数据的 Java Web 应用程序。
在 Windows XP 上运行时一切正常,但当我尝试在 Windows 7 中运行时,它崩溃了。
Tomcat stdout.log :
2013-07-07 14:23:33 Commons Daemon procrun stdout initialized
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x77b42b79, pid=3576, tid=1528
#
# JRE version: 6.0_18-b07
# Java VM: Java HotSpot(TM) Client VM (16.0-b13 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [ntdll.dll+0x52b79]
#
# An error report file with more information is saved as:
# C:\Windows\system32\hs_err_pid3576.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
applicationContext.xml]
[2013-07-07 14:23:52,025] INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from ServletContext resource [/WEB-INF/conf/spring/applicationContext-dataSource.xml]
[2013-07-07 14:23:52,119] INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from ServletContext resource [/WEB-INF/conf/spring/applicationContext-manageable.xml]
[2013-07-07 14:23:52,228] INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from ServletContext resource [/WEB-INF/conf/spring/applicationContext-security.xml]
[2013-07-07 14:23:52,930] INFO [org.springframework.security.core.SpringSecurityCoreVersion] - You are running with Spring Security Core 3.0.5.RELEASE
[2013-07-07 14:23:52,930] INFO [org.springframework.security.config.SecurityNamespaceHandler] - Spring Security 'config' module version is 3.0.5.RELEASE
[2013-07-07 14:23:53,148] INFO [org.springframework.security.config.http.HttpSecurityBeanDefinitionParser] - ncyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 1800, <org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0>, order = 1900]
文件 C:\Windows\system32\hs_err_pid3576.log 表明从 jni dll 调用的函数是错误的来源:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x77b42b79, pid=3576, tid=1528
#
# JRE version: 6.0_18-b07
# Java VM: Java HotSpot(TM) Client VM (16.0-b13 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [ntdll.dll+0x52b79]
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
--------------- T H R E A D ---------------
Current thread (0x039ab000): JavaThread "Thread-2" daemon [_thread_in_vm, id=1528, stack(0x03bf0000,0x03c60000)]
....
Stack: [0x03bf0000,0x03c60000], sp=0x03c5e74c, free space=1b903c5e268k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [ntdll.dll+0x52b79]
C [ntdll.dll+0x52e7d]
C [ntdll.dll+0x52d68]
C [kernel32.dll+0x4f1ac]
C [MSVCR71.dll+0x218a]
V [jvm.dll+0x105036]
C [myDLL.dll+0x1910]
j myApp.d([B)[B+0
j myApp.dec([B)[B+2
j org.apache.catalina.h.WebappsClassLoader.findClassInternal(Ljava/lang/String;)Ljava/lang/Class;+425
j org.apache.catalina.loader.WebappClassLoader.findClass(Ljava/lang/String;)Ljava/lang/Class;+301
j org.apache.catalina.h.WebappsClassLoader.loadClass(Ljava/lang/String;Z)Ljava/lang/Class;+451
j org.apache.catalina.h.WebappsClassLoader.loadClass(Ljava/lang/String;)Ljava/lang/Class;+3
j org.springframework.util.ClassUtils.forName(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class;+177
j org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(Ljava/lang/ClassLoader;)Ljava/lang/Class;+13
j org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(Lorg/springframework/beans/factory/support/RootBeanDefinition;[Ljava/lang/Class;)Ljava/lang/Class;+96
j org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(Lorg/springframework/beans/factory/support/RootBeanDefinition;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/Class;+42
j org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(Ljava/lang/String;Lorg/springframework/beans/factory/support/RootBeanDefinition;[Ljava/lang/Class;)Ljava/lang/Class;+23
j org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(Ljava/lang/String;Lorg/springframework/beans/factory/support/RootBeanDefinition;)Z+13
j org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(Ljava/lang/Class;ZZ)[Ljava/lang/String;+105
j org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(Ljava/lang/Class;ZZ)Ljava/util/Map;+4
j org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;)V+126
j org.springframework.context.support.AbstractApplicationContext.refresh()V+28
j org.springframework.web.context.ContextLoader.createWebApplicationContext(Ljavax/servlet/ServletContext;Lorg/springframework/context/ApplicationContext;)Lorg/springframework/web/context/WebApplicationContext;+245
j org.springframework.web.context.ContextLoader.initWebApplicationContext(Ljavax/servlet/ServletContext;)Lorg/springframework/web/context/WebApplicationContext;+69
j org.springframework.web.context.ContextLoaderListener.contextInitialized(Ljavax/servlet/ServletContextEvent;)V+28
j org.apache.catalina.core.StandardContext.listenerStart()Z+523
j org.apache.catalina.core.StandardContext$1.call()Ljava/lang/Boolean;+12
j org.apache.catalina.core.StandardContext$1.call()Ljava/lang/Object;+1
j java.util.concurrent.FutureTask$Sync.innerRun()V+30
j java.util.concurrent.FutureTask.run()V+4
j java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Ljava/lang/Runnable;)V+59
j java.util.concurrent.ThreadPoolExecutor$Worker.run()V+28
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
V [jvm.dll+0xf072c]
V [jvm.dll+0x17fd51]
V [jvm.dll+0xf08f7]
V [jvm.dll+0xf096d]
V [jvm.dll+0x11a4c0]
V [jvm.dll+0x1dd924]
V [jvm.dll+0x17f9cc]
C [MSVCR71.dll+0x9565]
C [kernel32.dll+0x51174]
C [ntdll.dll+0x5b3f5]
C [ntdll.dll+0x5b3c8]
....
--------------- S Y S T E M ---------------
OS: Windows 7 Build 7600
CPU:total 2 (2 cores per cpu, 1 threads per core) family 6 model 23 stepping 10, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1
Memory: 4k page, physical 2056808k(1150444k free), swap 4113616k(2684008k free)
vm_info: Java HotSpot(TM) Client VM (16.0-b13) for windows-x86 JRE (1.6.0_18-b07), built on Dec 17 2009 13:35:55 by "java_re" with MS VC++ 7.1 (VS2003)
time: Sun Jul 07 14:23:53 2013
elapsed time: 19 seconds
jni dll 名称为:myDLL.dll ,函数为:myApp.d()
这是生成 dll 的 c 程序中的 myApp.d() 函数:
JNIEXPORT jbyteArray JNICALL Java_myApp_d(JNIEnv *env, jobject obj, jbyteArray b)
{
EVP_CIPHER_CTX en, de;
// get length of bytes
int srcLen=(*env)->GetArrayLength(env, b);
//convert jbyteArray to byte []
jbyte data[srcLen];
(*env)->GetByteArrayRegion(env, b, 0, srcLen, data);
(*env)->ReleaseByteArrayElements(env, b, data , 0);
unsigned int salt[] = {12345, 54321};
unsigned char key_data[16]={ 'A','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p'};
int key_data_len = 16;
if (aes_init(key_data, key_data_len, (unsigned char *)&salt, &en, &de))
{
printf("Couldn't initialize AES cipher\n");
return -1;
}
unsigned char* indata=(unsigned char*)data;
char *plaintext;
int olen, len;
int j;
char tampon[16];
for(j=0; j<16; j++)
{
tampon[j]=indata[j];
}
int desLen;
sscanf(tampon,"%d",&desLen);
unsigned char indataB[srcLen-16];
for(j=0; j<srcLen-16; j++)
{
indataB[j]=indata[16+j];
}
olen = len = srcLen-16+1;
plaintext = (char *)aes_decrypt(&de, indataB, &len);
jbyteArray bArray = (*env)->NewByteArray(env, desLen);
jboolean isCopy = JNI_TRUE;
jbyte* decrypteddata = (jbyte *)(*env)->GetByteArrayElements(env, bArray, &isCopy);
memcpy(decrypteddata, plaintext, desLen);
(*env)->ReleaseByteArrayElements(env, bArray, decrypteddata, 0);
free(plaintext);
free(decrypteddata);
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return bArray;
(*env)->DeleteLocalRef(env, bArray );
}
此函数使用以下两个函数:
int aes_init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx,
EVP_CIPHER_CTX *d_ctx)
{
int i, nrounds = 5;
unsigned char key[16], iv[16];
/*
* Gen key & IV for AES 128 CBC mode. A SHA1 digest is used to hash the supplied key material.
* nrounds is the number of times the we hash the material. More rounds are more secure but
* slower.
*/
i = EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
if (i != 16)
{
printf("Key size is %d bits - should be 128 bits\n", i);
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_128_cbc(), NULL, key, iv);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx, EVP_aes_128_cbc(), NULL, key, iv);
return 0;
}
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
{
/* because we have padding ON, we must allocate an extra cipher block size of memory */
int p_len = *len, f_len = 0;
unsigned char *plaintext = malloc(p_len + AES_BLOCK_SIZE+1);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
*len = p_len + f_len;
return plaintext;
}
我怀疑这部分允许将数据从 char* 复制到 jbytearray :
jbyte* decrypteddata = (jbyte *)(*env)->GetByteArrayElements(env, bArray, &isCopy);
memcpy(decrypteddata, plaintext, desLen);
(*env)->ReleaseByteArrayElements(env, bArray, decrypteddata, 0);
我试图用GetPrimitiveArrayCritical替换GetByteArrayElements,用ReleasePrimitiveArrayCritical替换ReleaseByteArrayElements,但没有成功。