0

我有一个使用 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,但没有成功。

4

1 回答 1

0

[解决了]

正如我在问题中所想的那样,问题是由对那些 JNI 函数的调用引起的:

    (*env)->GetByteArrayRegion(env, b, 0, srcLen, data);
    (*env)->ReleaseByteArrayElements(env, b, data , 0);

我将它们替换为:

jbyte* d = (*env)->GetByteArrayElements(env, b, &isCopyS);

int i;

for(i = 0; i < srcLen; i++)
{
    data[i] = d[i];
}

(*env)->ReleaseByteArrayElements(env, b, d, JNI_ABORT);

现在它适用于 Windows XP 和 Windows 7。

于 2013-07-09T10:31:57.880 回答