Android 中 SHA1 哈希实现的问题

发布于 2024-11-15 14:11:16 字数 2078 浏览 3 评论 0原文

我有两个用于计算 SHA1 的小片段。

一个非常快但似乎不正确,另一个非常慢但正确。
我认为 FileInputStream 转换为 ByteArrayInputStream 是问题所在。

快速版本:

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();
}

我希望有另一种可能让它运行,因为我需要性能。

I have two small snippets for calculating SHA1.

One is very fast but it seems that it isn't correct and the other is very slow but correct.
I think the FileInputStream conversion to ByteArrayInputStream is the problem.

Fast version:

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();

Slow version:

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();

Conversion method:

private static String byteArray2Hex(byte[] hash) {
    Formatter formatter = new Formatter();
    for (byte b : hash) {
        formatter.format("%02x", b);
    }
    return formatter.toString();
}

I hope there is another possibility to get it running because I need the performance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

贪恋 2024-11-22 14:11:16

我使用了高性能 C++ 实现,并通过 JNI 加载它。
欲了解更多详情,请写评论。

编辑:
JNI 的要求是 Android NDK。对于 Windows,还需要 cygwin 或类似的东西。
如果您决定使用 cygwin,我会给您一些如何让它与 NDK 一起工作的小说明:

  1. 从 cygwin 下载 setup.exe 并执行它。
  2. 单击下一步并选择从互联网安装并单击下一步进行确认。
  3. 接下来的两个步骤根据需要调整设置,并一如既往地单击下一步
  4. 选择您的互联网连接以及与最后阶段相同的过程。
  5. 下载页面会吸引眼球,选择它或仅选择您所在国家/地区的下载页面。没什么可说的了。
  6. 我们需要makegcc-g++ 包。您可以使用左上角的搜索找到它们,单击跳过直到显示版本并选择第一个字段。做我们在选择后一直做的事情。
  7. 您将得到信息,表明存在必须解决的依赖关系。通常不需要自己做并确认。
  8. 下载和安装开始。
  9. 如果需要,您可以创建快捷方式,否则单击特殊的完成
  10. 下载 zip 文件并将 NDK 解压到不包含空格的路径。
  11. 你现在就可以开始cygwin了。
  12. 导航到 NDK。路径/cydrive为您提供了cd /cygdrive/d导航到带有字母D的驱动器的所有可用驱动器。
  13. 在 NDK 的根文件夹中,您可以使用 ./ndk-build 执行文件 ndk-build。应该会出现类似 Android NDK: Could not find application project directory! 的错误。
    您必须在 Android 项目中导航才能执行该命令。那么让我们从一个项目开始吧。

在我们开始项目之前,请先搜索哈希算法的 C/C++ 实现。我从这个网站 CSHA1 获取了代码。
您应该根据您的要求编辑源代码。

现在我们可以从 JNI 开始。
您在 Android 项目中创建一个名为 jni 的文件夹。它也包含所有本机源文件和 Android.mk(稍后会详细介绍该文件)。
将下载的(和编辑的)源文件复制到该文件夹​​中。

我的 java 包名为 de.ddbw.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 代码:
我将 AsyncTaskProgressDialog 一起使用,为用户提供有关操作的一些反馈。

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 使用,您必须添加#include

对于日志记录,插入以下内容:#include
现在您可以使用 __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "Version [%s]", "19"); 进行日志记录。
第一个参数是消息类型,第二个参数是引发库。
您可以看到我的代码中有一个版本号。这非常有帮助,因为有时 apk 构建器不使用新的本机库。如果在线版本错误,则可以大大缩短故障排除时间。

本机代码中的命名约定有点疯狂:Java_[包名称]_[类名称]_[方法名称]

第一个参数始终给出,但根据应用程序,您应该区分:

  • func(JNIEnv * env, jobject jobj) -> JNI调用是一个实例方法
  • func(JNIEnv * env, jclass jclazz) -> JNI 调用是静态方法

方法的标头 calcFileSha1(...):
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 -bootclasspathde.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

您可以更改该文件,恕不另行通知。但不要再使用 javah

类和方法
要获取类实例,您可以使用jclass clz = callEnv->FindClass(CALL_CLASS);。在本例中,CALL_CLASS 是类 de/dhbw/file/sha1/SHA1HashFileAsyncTask 的完全限定路径。

要查找方法,您需要 JNIEnv 和该类的实例:
jmethodID midSet = callEnv->GetMethodID(callClass, "setFileSize", "(J)V");
第一个参数是类的实例,第二个参数是方法的名称,第三个参数是方法的签名。
您可以通过 JDK 给定的二进制文件 javap.exe 获得签名。只需使用类 fe 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”方法中给定的作业,我认为其他参数很清楚。

请记住,您可以从本机代码调用类的私有方法,因为本机代码是其中的一部分!

字符串
给定的字符串将使用以下代码进行转换:

jboolean jbol;
const char *fileName = env->GetStringUTFChars(file, &jbol);

另一种方式:

TCHAR* szReport = new TCHAR;
jstring result = callEnv->NewStringUTF(szReport);

它可以是每个 char* 变量。

例外
可以用JNIEnv抛出:

callEnv->ThrowNew(callEnv->FindClass("java/lang/Exception"), 
    "Hash generation failed");

您还可以检查JNIEnv是否也发生异常:

if (callEnv->ExceptionOccurred()) {
    callEnv->ExceptionDescribe();
    callEnv->ExceptionClear();
}

规范

构建/清理

构建
创建所有文件并填充内容后,我们就可以构建它了。
打开 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++代码的调试将在下次进行。

I used a high performance c++ implementation which I load with JNI.
For more details write a comment, please.

EDIT:
Requirements for JNI is the Android NDK. For Windows is needed in addition cygwin or something similar.
If you decided for cygwin, I give you some little instructions how to get it working with the NDK:

  1. Download the setup.exe from cygwin and execute it.
  2. Click on Next and choice Install from Internet confirm with Next.
  3. The next two steps adjust the settings as desired and as always click Next.
  4. Select your internet connection and the same procedure as in the final stages.
  5. A download page will catch the eye select it or take just a download page, which is in your country. There is nothing more to say.
  6. We need the packages make and gcc-g++. You can find them using the search in the left upper corner, click on the Skip til a version is displayed and the first field is selected. Do that what we have always done after a selection.
  7. You will get the information, that there are dependencies, which must be resolved. It is usually not necessary to do it yourself and confirm it.
  8. The download and installation started.
  9. If you need you can create shortcuts otherwise click on exceptional Finish.
  10. Download the zip file and extract the NDK to a non space containing path.
  11. You can start now cygwin.
  12. Navigate to the NDK. The path /cydrive gives you all available drives f.e. cd /cygdrive/d navigates to the drive with the letter D.
  13. In the root folder of the NDK you can execute the file ndk-build with ./ndk-build. There should be an error occurs like Android NDK: Could not find application project directory !.
    You have to navigate in an Android project to execute the command. So let's start with a project.

Before we can start with the project search for a C/C++ implementation of the hash algorithm. I took the code from this site CSHA1.
You should edit the source code for your requirements.

Now we can start with JNI.
You create a folder called jni in your Android project. It contains all native source files and the Android.mk (more about that file later), too.
Copy your downloaded (and edited) source files in that folder.

My java package is called de.dhbw.file.sha1, so I named my source files similar to find them easily.

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 code:
I used the AsyncTask with a ProgressDialog to give the user some feedback about the action.

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();

    // [...]
}

Native code (C++):

Remember accessing variables inside native code or other way around using threads needs synchronizing or you will get a segmentation fault soon!

For JNI usage you have to add #include <jni.h>.

For logging insert following include #include <android/log.h>.
Now you can log with __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "Version [%s]", "19");.
The first argument is the type of message and the second the causing library.
You can see I had a version number in my code. It is very helpful because sometimes the apk builder doesn't use the new native libraries. Troubleshooting can be extremely shortened, if the wrong version is online.

The naming conventions in the native code are a little bit crasier: Java_[package name]_[class name]_[method name].

The first to arguments are always given, but depending on the application you should distinguish:

  • func(JNIEnv * env, jobject jobj) -> JNI call is an instance method
  • func(JNIEnv * env, jclass jclazz) -> JNI call is a static method

The header for the method calcFileSha1(...):
JNIEXPORT void JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_calcFileSha1(JNIEnv * env, jobject jobj, jstring file)

The JDK delivers the binary javah.exe, which generates the header file for the native code. The usage is very simple, simply call it with the full qualified class:
javah de.dhbw.file.sha1.SHA1HashFileAsyncTask

In my case I have to give the bootclasspath additionally, because I use Android classes:
javah -bootclasspath <path_to_the_used_android_api> de.dhbw.file.sha1.SHA1HashFileAsyncTask

That would be the generated file:

/* 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

You can change the file without further notice. But do not use javah again!

Class and methods
To get a class instance you can use jclass clz = callEnv->FindClass(CALL_CLASS);. In this case is CALL_CLASS the full qualified path to the class de/dhbw/file/sha1/SHA1HashFileAsyncTask.

To find a method you need the JNIEnv and an instance of the class:
jmethodID midSet = callEnv->GetMethodID(callClass, "setFileSize", "(J)V");
The first argument is the instance of the class, the second the name of the method and the third is the signature of the method.
The signature you can get with the from JDK given binary javap.exe. Simply call it with the full qualified path of the class f.e. javap -s de.dhbw.file.sha1.SHA1HashFileAsyncTask.
You will get an result like:

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

  [...]
}

If the method is found the variable is not equal 0.
Calling the method is very easy:

callEnv->CallVoidMethod(callObj, midSet, size);

The first argument is the given jobject from the "main" method and I think the others are clear.

Remember that you can call from native code although private methods of the class, because the native code is part of it!

Strings
The given string would be converted with following code:

jboolean jbol;
const char *fileName = env->GetStringUTFChars(file, &jbol);

And the other way:

TCHAR* szReport = new TCHAR;
jstring result = callEnv->NewStringUTF(szReport);

It can be every char* variable.

Exceptions
Can be thrown with the JNIEnv:

callEnv->ThrowNew(callEnv->FindClass("java/lang/Exception"), 
    "Hash generation failed");

You can also check if there is an exception occurred also with JNIEnv:

if (callEnv->ExceptionOccurred()) {
    callEnv->ExceptionDescribe();
    callEnv->ExceptionClear();
}

Specifications

Build/Clean

Build
After we have created all files and filled them with content, we can build it.
Open cygwin, navigate to the project root and execute from there the ndk-build, which is in the NDK root.
This start the compile, if it is success you will get an output like that:

$ /cygdrive/d/android-ndk-r5c/ndk-build
Compile++ thumb  : SHA1Calc <= SHA1Calc.cpp
SharedLibrary  : libSHA1Calc.so
Install        : libSHA1Calc.so => libs/armeabi/libSHA1Calc.so

If there is any error, you will get the typical output from the compiler.

Clean
Open cygwin, switch in your Android project and execute the command /cygdrive/d/android-ndk-r5c/ndk-build clean.

Build apk
After you have build the native libraries you can build your project. I've found clean, it is advantageous to use the eclipse feature clean project.

Debugging
Debugging of java code isn't different as before.
The debugging of c++ code will follow in the next time.

美男兮 2024-11-22 14:11:16

这样做:

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 缓冲区应该足够块化。您不必使用 BufferedInputStream ,因为 8 kB 缓冲区也可用作 I/O 缓冲区。

Do this:

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();

Performance comes from handling data by blocks. An 8 kB buffer, as here, ought to be blocky enough. You do not have to use a BufferedInputStream since the 8 kB buffer also serves as I/O buffer.

探春 2024-11-22 14:11:16

快速的快速且不正确的原因是(我认为)它没有对文件内容进行哈希处理!

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 的全部内容读取到 byte[] 的简单方法> 是使用 Apache Commons I/O 帮助器方法 - IOUtils .toByteArray(InputStream)

The reason the fast one is fast and incorrect is (I think) that it is not hashing the file contents!

FileInputStream fis = new FileInputStream("C:/Users/Ich/Downloads/srware_iron.exe");
ByteArrayInputStream byteArrayInputStream = 
        new ByteArrayInputStream(fis.toString().getBytes());

The fis.toString() call does not read the contents of the file. Rather it gives you a string that (I suspect) looks something like this:

"java.io.FileInputStream@xxxxxxxx"

which you are then proceeding to calculate the SHA1 hash for. FileInputStream and its superclasses do not override Object::toString ...


The simple way to read the entire contents of an InputStream to a byte[] is to use an Apache Commons I/O helper method - IOUtils.toByteArray(InputStream).

阳光下慵懒的猫 2024-11-22 14:11:16
    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();
}
    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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文