在Android的SHA1哈希执行有关的问题 [英] Issues with SHA1 hash implementation in Android

查看:293
本文介绍了在Android的SHA1哈希执行有关的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个小片段计算SHA1。

I have two small snippets for calculating SHA1.

一个是非常快的,但它似乎是不正确的,另一种是很慢的,但正确的。
我觉得的FileInputStream 转化为 ByteArrayInputStream的的问题。

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.

快速的版本:

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 hope there is another possibility to get it running because I need the performance.

推荐答案

我用了一个高性能的C ++实现,我用JNI加载。
欲了解更多详细信息,写评论,请。

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

编辑:
对于JNI的要求是的Andr​​oid NDK 。对于Windows需要另外 cygwin的或类似的东西。
如果你决定了Cygwin的,我给你一些小小的说明如何让它与NDK工作:


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. 下载的的setup.exe cygwin的版本的并执行它。
  2. 点击上的下一步的和选择的从网络安装的确认的下一步
  3. 在接下来的两个步骤,根据需要和往常一样点击的下一步的调整设置。
  4. 选择您的Internet连接,并在相同的过程的最后阶段。
  5. 一个下载页面会吸引眼球中选择,或采取只是一个下载页面,这是在你的国家。没有什么好说的了。
  6. 我们需要的软件包的 GCC-G ++ 的。你可以找到他们利用左上角的搜索,点击跳过直到一个版本显示的第一个字段被选中。难道这就是我们选择后一直做。
  7. 您将获得的信息,有依赖关系,必须解决。它通常是没有必要做自己并确认。
  8. 在下载和安装开始。
  9. 如果你需要,你可以创建快捷方式,否则点击例外完成
  10. 下载压缩文件,解压NDK到非含路径的空间。
  11. 您现在可以开始Cygwin的。
  12. 导航到NDK。路径的 / cydrive 的给你所有可用驱动器FE CD / cygdrive / D 导航到驱动器字母ð
  13. 在NDK的根文件夹,您可以执行该文件的 NDK建造的与 ./ NDK建造。应该有发生的错误,如的Andr​​oid NDK:找不到应用程序项目目录
    ! 你有一个Android项目要执行的命令进行导航。因此,让我们开始一个项目。
  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.

在我们的项目中寻找一个C / C ++实现的哈希算法的开始。我拿着code从本网站 CSHA1
您应该编辑源$ C ​​$下您的要求。

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.

现在我们可以用JNI启动。
创建您的Andr​​oid项目叫做文件夹的 JNI 的。它包含了所有的本地源文件和 Android.mk 的(更多关于该文件后),太。照片 复制您下载的(或修改)的源文件的文件夹中。

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.

我的Java包被称为的 de.dhbw.file.sha1 的,所以我叫我的源文件类似,很容易地找到他们。

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:
我用的的AsyncTask 的用的 ProgressDialog 的向用户提供有关操作的一些反馈。

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

    // [...]
}

本机code(C ++):

记住访问各地使用线程内本地code或其他方式的变量需要同步或者你会得到一个分段错误很快!

有关JNI使用,您必须添加的#include&LT; jni.h&GT;

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

有关记录插入下面包括的#include&LT;安卓/ log.h&GT;
现在,您可以用 __ android_log_print日志(ANDROID_LOG_DEBUG,DEBUG_TAG,版本[%S],19);
第一个参数是消息的类型和第二造成库。
你可以看到我在code有一个版本号。这是非常有用的,因为有时APK商不使用新的机库。故障排除,可极大地缩短,如果错误的版本是在网上。

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.

在本机code的命名约定有点crasier: JAVA_ [包名] _ [类名称] _ [方法名称]

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调用是一个实例方法
  • FUNC(JNIEnv的* ENV,JCLASS jclazz) - > JNI调用是一个静态方法
  • func(JNIEnv * env, jobject jobj) -> JNI call is an instance method
  • func(JNIEnv * env, jclass jclazz) -> JNI call is a static method

该方法的标题 calcFileSha1(...)
JNIEXPORT无效JNICALL Java_de_dhbw_file_sha1_SHA1HashFileAsyncTask_calcFileSha1(JNIEnv的* ENV,jobject jobj,的jstring文件)

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

在JDK提供的二进制文件的 javah.exe 的,其产生的本地code头文件。用法很简单,只需把它与全限定类:
javah的de.dhbw.file.sha1.SHA1HashFileAsyncTask

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

在我来说,我必须给的 BOOTCLASSPATH 的补充,因为我用的Andr​​oid类: javah的-bootclasspath&LT; path_to_the_used_android_api&GT; 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

这将是生成的文件:

/* 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的又来了!

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

类和方法
为了得到一个类实例,你可以使用 JCLASS CLZ = callEnv-&GT;的findClass(CALL_CLASS); 。在这种情况下是 CALL_CLASS 的完全限定路径类的 DE / dhbw /文件/ SHA1 / SHA1HashFileAsyncTask 的。

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.

要找到你所需要的的的JNIEnv 的和类实例的方法:
jmethodID midSet = callEnv-&GT;的GetMethodID(callClass,setFileSize,(J)V); 第一个参数是类的实例,方法和第三的第二个名字是方法的签名。
签名您可以用从JDK给定二进制的 javap.exe 的获取。简单地用类FE的完全限定路径叫它的javap -s 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

  [...]
}

如果找到了该变量中的方法是不等于0。
调用方法很简单:

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

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

第一个参数是给定的 jobject从主的方法的,我想其他人是清楚的。

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

记住,你可以从本地code虽然类的私有方法调用,因为本地code是它的一部分!

字符串
在给定的字符串将与以下code转换:

Strings
The given string would be converted with following code:

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

和其他方式:

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

这可能是每一个的char * 变量。

例外
可与抛出的的的JNIEnv 的:

Exceptions
Can be thrown with the JNIEnv:

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

您还可以检查是否存在同样发生在例外的的JNIEnv 的:

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

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

规格

Specifications

建立/清除

建立
之后,我们创建的所有文件,填满了内容,我们可以建造它。
打开cygwin的,浏览到项目的根目录,并从那里的 NDK建造的,这是在NDK根。
执行 这开始编译,如果是成功的,你会得到这样的输出:

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.

清理
打开Cygwin的,开关在你的Andr​​oid项目,并执行命令 / cygdrive /天/ Android的NDK-R5C / NDK-打造干净

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

生成APK
之后,你有构建本机库,你可以建立你的项目。我发现的清洁,最好使用Eclipse功能的清洁工程

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.

调试
java的code调试是不是不同的如前。
C ++的code调试将遵循在接下来的时间。

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

这篇关于在Android的SHA1哈希执行有关的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆