Java Android中SHA1哈希实现的问题

Java Android中SHA1哈希实现的问题,java,android,hash,stream,sha1,Java,Android,Hash,Stream,Sha1,我有两个用于计算SHA1的小片段 一个非常快,但似乎不正确;另一个非常慢,但正确。 我认为FileInputStream到ByteArrayInputStream的转换就是问题所在 快速版本: MessageDigest md = MessageDigest.getInstance("SHA1"); FileInputStream fis = new FileInputStream("path/to/file.exe"); ByteArrayInputStream byteArrayInputS

我有两个用于计算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();
}
慢速版本:

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();
}
转换方法:

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();
}
我希望有另一种可能让它运行,因为我需要性能。

执行以下操作:

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();
MessageDigest md=MessageDigest.getInstance(“SHA1”);
InputStream in=新文件InputStream(“此处为您的InputFileName”);
字节[]buf=新字节[8192];
对于(;;){
int len=in.read(buf);
if(len<0)
打破
md.update(buf,0,len);
}
in.close();
字节[]散列=md.digest();

性能来自按块处理数据。一个8KB的缓冲区,就像这里一样,应该足够大。您不必使用
BufferedInputStream
,因为8KB的缓冲区也可用作I/O缓冲区。

快速缓冲区之所以快速且不正确(我认为)是因为它没有散列文件内容

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



一个简单的读取输入流的内容到<代码>字节[]/Cord>的方法是使用Apache CAMSONS I/O帮助器方法.< /P> < P>我使用了一个高性能的C++实现,我用JNI加载。 欲了解更多详情,请写评论

编辑:
JNI的需求是最基本的。对于Windows,还需要添加或类似的内容。
如果您决定使用cygwin,我将为您提供一些关于如何使用NDK的小说明:

  • 从cygwin下载setup.exe并执行它
  • 单击“下一步”,选择“从Internet安装”,然后用“下一步”确认
  • 接下来的两个步骤根据需要调整设置,并一如既往地单击“下一步”
  • 选择您的internet连接和与最后阶段相同的步骤
  • 一个下载页面会吸引眼球,选择它或者只选择一个下载页面,这是在你的国家。没什么可说的了
  • 我们需要make和gcc-g++包。您可以使用左上角的搜索找到它们,单击跳过,直到显示版本并选择第一个字段。在选择之后,我们总是这样做
  • 您将得到相关信息,即存在必须解决的依赖项。通常不需要自己做并确认
  • 下载和安装已开始
  • 如果需要,您可以创建快捷方式,否则单击ExceptionFinish
  • 下载zip文件并将NDK解压缩到一个不包含空格的路径
  • 你现在可以开始了,cygwin
  • 导航到NDK。路径/cydrive为您提供所有可用的驱动器,例如,cd/cydrive/d导航到带有字母d的驱动器
  • 在NDK的根文件夹中,您可以使用
    /NDK build
    执行文件NDK build。应该会出现一个错误,如Android NDK:找不到应用程序项目目录
    您必须在Android项目中导航才能执行该命令。让我们从一个项目开始
  • 在开始项目之前,我们可以搜索一个C/C++实现的哈希算法。我从这个站点获取了代码。
    您应该根据自己的需求编辑源代码

    现在我们可以从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代码:
    我使用带有ProgressDialog的AsyncTask向用户提供有关操作的一些反馈

    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();
    
        // [...]
    }
    
    您可以更改该文件,无需另行通知。但是不要再使用
    javah

    类和方法
    要获取类实例,可以使用
    jclass clz=callEnv->FindClass(CALL_class)。在本例中,
    CALL\u CLASS
    类de/dhbw/file/sha1/SHA1HashFileAsyncTask的完整限定路径

    要找到一个方法,需要JNIEnv和类的实例:
    jmethodID midSet=callEnv->GetMethodID(callClass,“setFileSize”,“J)V”)
    第一个参数是类的实例,第二个参数是方法的名称,第三个参数是方法的签名。
    您可以从给定二进制javap.exe的JDK获得的签名。只需使用类f.e.
    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
    
      [...]
    }
    
    第一个论点是来自“main”方法的给定jobject,我认为其他论点都很清楚

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

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

    jboolean jbol;
    const char *fileName = env->GetStringUTFChars(file, &jbol);
    
    另一方面:

    TCHAR* szReport = new TCHAR;
    jstring result = callEnv->NewStringUTF(szReport);
    
    它可以是每个
    char*
    变量

    例外情况
    可以与JNIEnv一起抛出:

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

    规格

    构建/清理

    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)
    
    构建
    在我们创建了所有文件并用内容填充它们之后,我们就可以构建它了