Android中SHA1哈希实现的问题

|| 我有两个用于计算SHA1的小片段。 一个非常快,但似乎不正确,另一个非常慢,但正确。 我认为将“ 0”转换为“ 1”是个问题。 快速版本:
MessageDigest md = MessageDigest.getInstance(\"SHA1\");
FileInputStream fis = new FileInputStream(\"path/to/file.exe\");
ByteArrayInputStream byteArrayInputStream =
    new ByteArrayInputStream(fis.toString().getBytes());
DigestInputStream dis = new DigestInputStream(byteArrayInputStream, md);
BufferedInputStream bis = new BufferedInputStream(fis);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

int ch;
while ((ch = dis.read()) != -1) {
    byteArrayOutputStream.write(ch);
}

byte[] newInput = byteArrayOutputStream.toByteArray();
System.out.println(\"in digest : \" +
    byteArray2Hex(dis.getMessageDigest().digest()));

byteArrayOutputStream = new ByteArrayOutputStream();
DigestOutputStream digestOutputStream =
    new DigestOutputStream(byteArrayOutputStream, md);
digestOutputStream.write(newInput);

System.out.println(\"out digest: \" +
    byteArray2Hex(digestOutputStream.getMessageDigest().digest()));
System.out.println(\"length: \" + 
    new String(
        byteArray2Hex(digestOutputStream.getMessageDigest().digest())).length());

digestOutputStream.close();
byteArrayOutputStream.close();
dis.close();
慢版本:
MessageDigest algorithm = MessageDigest.getInstance(\"SHA1\");
FileInputStream fis = new FileInputStream(\"path/to/file.exe\");
BufferedInputStream bis = new BufferedInputStream(fis);
DigestInputStream   dis = new DigestInputStream(bis, algorithm);

// read the file and update the hash calculation
while (dis.read() != -1);

 // get the hash value as byte array
byte[] hash = algorithm.digest();
转换方式:
private static String byteArray2Hex(byte[] hash) {
    Formatter formatter = new Formatter();
    for (byte b : hash) {
        formatter.format(\"%02x\", b);
    }
    return formatter.toString();
}
我希望有另一种可能使其运行,因为我需要性能。     
已邀请:
我使用了JNI加载的高性能c ++实现。 有关更多详细信息,请发表评论。 编辑: JNI的要求是Android NDK。对于Windows,还需要cygwin或类似的东西。 如果您决定使用cygwin,我会给您一些指导,说明如何使其与NDK一起使用: 从cygwin下载setup.exe并执行。 单击下一步,然后选择从Internet安装,然后单击下一步进行确认。 接下来的两个步骤将根据需要调整设置,并一如既往地单击“下一步”。 选择您的互联网连接,并按照与最后阶段相同的步骤进行操作。 选择一个下载页面会吸引您的注意,或者仅下载您所在国家/地区的下载页面。没什么可说的了。 我们需要make和gcc-g ++软件包。您可以使用左上角的搜索找到它们,单击“跳过直到显示版本”,然后选择第一个字段。完成选择后我们一直做的事情。 您将获得必须解决的依赖关系信息。通常无需自己进行确认。 开始下载和安装。 如果需要,您可以创建快捷方式,否则单击特殊的“完成”。 下载该zip文件,然后将NDK解压缩到不包含路径的路径中。 您可以立即开始cygwin。 导航到NDK。路径/ cydrive为您提供所有可用的驱动器
cd /cygdrive/d
导航到带有字母D的驱动器。 在NDK的根文件夹中,您可以使用
./ndk-build
执行ndk-build文件。应该出现类似“ 7”的错误。 您必须在Android项目中导航才能执行命令。因此,让我们从一个项目开始。 在开始项目之前,搜索哈希算法的C / C ++实现。我从此站点CSHA1中获取了代码。 您应该根据需要编辑源代码。 现在我们可以从JNI开始。 您在Android项目中创建一个名为jni的文件夹。它还包含所有本机源文件和Android.mk(稍后将进一步介绍该文件)。 将下载的(和编辑的)源文件复制到该文件夹​​中。 我的Java包称为de.dhbw.file.sha1,因此我将源文件命名为类似文件,以便轻松找到它们。 Android.mk:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog

# How the lib is called?
LOCAL_MODULE    := SHA1Calc
# Which is your main SOURCE(!) file?
LOCAL_SRC_FILES := de_dhbw_file_sha1_SHA1Calc.cpp

include $(BUILD_SHARED_LIBRARY)
Java代码: 我将AsyncTask与ProgressDialog一起使用,以向用户提供有关该操作的一些反馈。
package de.dhbw.file.sha1;

// TODO: Add imports

public class SHA1HashFileAsyncTask extends AsyncTask<String, Integer, String> {
    // [...]

    static {
        // loads a native library
        System.loadLibrary(\"SHA1Calc\");
    }

    // [...]

    // native is the indicator for native written methods
    protected native void calcFileSha1(String filePath);

    protected native int getProgress();

    protected native void unlockMutex();

    protected native String getHash();

    // [...]
}
本机代码(C ++): 请记住,访问本机代码内部的变量或使用线程的其他方式需要同步,否则您很快会遇到分段错误! 要使用JNI,您必须添加ѭ10。 对于日志记录,请插入以下内容ѭ11。 现在您可以使用
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, \"Version [%s]\", \"19\");
登录。 第一个参数是消息的类型,第二个参数是原因库。 您可以看到我的代码中有一个版本号。这非常有帮助,因为有时apk生成器不使用新的本机库。如果在线版本错误,则可以极大地缩短故障排除时间。 本机代码中的命名约定有点麻烦:
Java_[package name]_[class name]_[method name]
。 始终提供first到arguments,但是应根据应用程序进行区分:
func(JNIEnv * env, jobject jobj)
-> JNI调用是一个实例方法
func(JNIEnv * env, jclass jclazz)
-> JNI调用是静态方法 方法“ 16”的标头:
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_calcFileSha1(JNIEnv * env, jobject jobj, jstring file)
JDK提供了二进制javah.exe,该文件生成了本机代码的头文件。用法非常简单,只需使用完全合格的类即可调用它:
javah de.dhbw.file.sha1.SHA1HashFileAsyncTask
在我的情况下,我必须额外提供bootclasspath,因为我使用的是Android类:
javah -bootclasspath <path_to_the_used_android_api> de.dhbw.file.sha1.SHA1HashFileAsyncTask
那将是生成的文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class de_dhbw_file_sha1_SHA1HashFileAsyncTask */

#ifndef _Included_de_dhbw_file_sha1_SHA1HashFileAsyncTask
#define _Included_de_dhbw_file_sha1_SHA1HashFileAsyncTask
#ifdef __cplusplus
extern \"C\" {
#endif
#undef de_dhbw_file_sha1_SHA1HashFileAsyncTask_ERROR_CODE
#define de_dhbw_file_sha1_SHA1HashFileAsyncTask_ERROR_CODE -1L
#undef de_dhbw_file_sha1_SHA1HashFileAsyncTask_PROGRESS_CODE
#define de_dhbw_file_sha1_SHA1HashFileAsyncTask_PROGRESS_CODE 1L
/*
 * Class:     de_dhbw_file_sha1_SHA1HashFileAsyncTask
 * Method:    calcFileSha1
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_calcFileSha1
  (JNIEnv *, jobject, jstring);

/*
 * Class:     de_dhbw_file_sha1_SHA1HashFileAsyncTask
 * Method:    getProgress
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_getProgress
  (JNIEnv *, jobject);

/*
 * Class:     de_dhbw_file_sha1_SHA1HashFileAsyncTask
 * Method:    unlockMutex
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_unlockMutex
  (JNIEnv *, jobject);

/*
 * Class:     de_dhbw_file_sha1_SHA1HashFileAsyncTask
 * Method:    getHash
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_getHash
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
您可以更改文件而无需另行通知。但是不要再使用use21ѭ! 类和方法 要获得一个类实例,可以使用
jclass clz = callEnv->FindClass(CALL_CLASS);
。在这种情况下,“ 23”是类de / dhbw / file / sha1 / SHA1HashFileAsyncTask的完整限定路径。 要找到一个方法,您需要JNIEnv和该类的实例:
jmethodID midSet = callEnv->GetMethodID(callClass, \"setFileSize\", \"(J)V\");
第一个参数是类的实例,第二个参数是方法的名称,第三个是方法的签名。 您可以使用JDK给定的二进制javap.exe从获得的签名。只需使用类f的完整限定路径调用它即可
javap -s de.dhbw.file.sha1.SHA1HashFileAsyncTask
。 您将得到如下结果:
Compiled from \"SHA1HashFileAsyncTask.java\"
public class de.dhbw.file.sha1.SHA1HashFileAsyncTask extends android.os.AsyncTas
k<java.lang.String, java.lang.Integer, java.lang.String> {
  [...]
  static {};
    Signature: ()V

  public de.dhbw.file.sha1.SHA1HashFileAsyncTask(android.content.Context, de.dhb
w.file.sha1.SHA1HashFileAsyncTask$SHA1AsyncTaskListener);
    Signature: (Landroid/content/Context;Lde/dhbw/file/sha1/SHA1HashFileAsyncTas
k$SHA1AsyncTaskListener;)V

  protected native void calcFileSha1(java.lang.String);
    Signature: (Ljava/lang/String;)V

  protected native int getProgress();
    Signature: ()I

  protected native void unlockMutex();
    Signature: ()V

  protected native java.lang.String getHash();
    Signature: ()Ljava/lang/String;

  [...]

  public void setFileSize(long);
    Signature: (J)V

  [...]
}
如果找到该方法,则变量不等于0。 调用方法非常简单:
callEnv->CallVoidMethod(callObj, midSet, size);
第一个参数是来自“ main”方法的给定jobject,我认为其他方法很清楚。 请记住,尽管本类是私有方法,但您可以从本机代码调用,因为本机代码是其中的一部分! 弦乐 给定的字符串将使用以下代码转换:
jboolean jbol;
const char *fileName = env->GetStringUTFChars(file, &jbol);
另一种方式:
TCHAR* szReport = new TCHAR;
jstring result = callEnv->NewStringUTF(szReport);
它可以是每个“ 30”变量。 例外情况 可以与JNIEnv一起抛出:
callEnv->ThrowNew(callEnv->FindClass(\"java/lang/Exception\"), 
    \"Hash generation failed\");
您还可以检查JNIEnv是否也发生了异常:
if (callEnv->ExceptionOccurred()) {
    callEnv->ExceptionDescribe();
    callEnv->ExceptionClear();
}
技术指标 Java本机接口规范 建立/清洁 建立 创建所有文件并将其填充内容后,便可以对其进行构建。 打开cygwin,导航到项目根目录,然后从该目录执行NDK根目录中的ndk-build。 这开始编译,如果成功,您将得到如下输出:
$ /cygdrive/d/android-ndk-r5c/ndk-build
Compile++ thumb  : SHA1Calc <= SHA1Calc.cpp
SharedLibrary  : libSHA1Calc.so
Install        : libSHA1Calc.so => libs/armeabi/libSHA1Calc.so
如果有任何错误,您将从编译器获取典型输出。 清洁 打开cygwin,打开您的Android项目并执行命令
/cygdrive/d/android-ndk-r5c/ndk-build clean
。 建立APK 构建本机库后,您可以构建项目。我发现它很干净,使用eclipse功能清理项目是有利的。 调试 Java代码的调试与以前没有什么不同。 C ++代码的调试将在下一次进行。     
做这个:
MessageDigest md = MessageDigest.getInstance(\"SHA1\");
InputStream in = new FileInputStream(\"hereyourinputfilename\");
byte[] buf = new byte[8192];
for (;;) {
    int len = in.read(buf);
    if (len < 0)
        break;
    md.update(buf, 0, len);
}
in.close();
byte[] hash = md.digest();
性能来自按块处理数据。此处的8 kB缓冲区应该足够块状。由于8 kB缓冲区也可用作I / O缓冲区,因此您不必使用ѭ36。     
        快速的一个快速和错误的原因是(我认为)它没有对文件内容进行哈希处理!
FileInputStream fis = new FileInputStream(\"C:/Users/Ich/Downloads/srware_iron.exe\");
ByteArrayInputStream byteArrayInputStream = 
        new ByteArrayInputStream(fis.toString().getBytes());
fis.toString()
调用不读取文件的内容。相反,它为您提供了一个(我怀疑)如下所示的字符串:
\"java.io.FileInputStream@xxxxxxxx\"
然后您要继续计算其SHA1哈希值。
FileInputStream
及其超类不会覆盖
Object::toString
... 将InputStream的全部内容读取到ѭ42的简单方法是使用Apache Commons I / O帮助器方法ѭ43。     
        
    public void computeSHAHash(String path)// path to your file
    {
            String SHAHash = null;
    try 
    {
        MessageDigest md = MessageDigest.getInstance(\"SHA1\");
        InputStream in = new FileInputStream(path);
        byte[] buf = new byte[8192];
        int len = -1;
        while((len = in.read(buf)) > 0) 
        {
            md.update(buf, 0, len);
        }
        in.close();
        byte[] data = md.digest();
        try 
        {
           SHAHash = convertToHex(data);
        } 
        catch (IOException e) 
        {
           // TODO Auto-generated catch block
           e.printStackTrace();
        }
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
      Toast.makeToast(getApplicationContext(),\"Generated Hash =\"+SHAHash,Toast.LENGTH_SHORT).show();  

   }
 private static String convertToHex(byte[] data) throws java.io.IOException
{
    StringBuffer sb = new StringBuffer();
    String hex = null;

    hex = Base64.encodeToString(data, 0, data.length, NO_OPTIONS);

    sb.append(hex);

    return sb.toString();
}
    

要回复问题请先登录注册