Checksum

Oi pessoal,

Estou desenvolvendo um sistema que faz download de pequenos arquivos de um Servidor FTP. Após o download, esse sistema apaga os arquivos do servidor.

Eu gostaria de verificar a integridade dos arquivos, antes de apagá-los.

Pensei em checar o tamanho do arquivo baixado e comparar com o arquivo existente no servidor, mas penso que isso não consiga pegar arquivos que por algum motivo forem alterados durante o download.

Então pensei em fazer um checksum. Já pesquisei algumas coisas no google.

Vale a pena mesmo usar um checksum? Isso afetará demais o desempenho do sistema?

Pra quem já utilizou isso, usou alguma biblioteca específica? Alguma sugestão?

Edit: Estou usando a lib da apache para fazer as transações ftp. Será que ela já tem algo referente a isso que ainda não encontrei?

Rod.

Você pode usar o MD5 mesmo.

A JRE já vem inclusive com uma implementação do mesmo.
Com relação à performance, o que posso dizer é o seguinte: varia de acordo com o tamanho dos arquivos.
Se forem grandes, com certeza você sentirá o impacto.
Mas nesta questão, o que cabe é você pensar o que pesa mais: a garantia de integridade dos seus dados ou a performance desejada?

[quote=kicolobo]Você pode usar o MD5 mesmo.

A JRE já vem inclusive com uma implementação do mesmo.
Com relação à performance, o que posso dizer é o seguinte: varia de acordo com o tamanho dos arquivos.
Se forem grandes, com certeza você sentirá o impacto.
Mas nesta questão, o que cabe é você pensar o que pesa mais: a garantia de integridade dos seus dados ou a performance desejada?[/quote]

Olhando a API do java SE, encontrei a interface CheckSum.

Você tem um exemplo de como utilizar?

Tenho um para MD5.

tenho, mas usando em uma String, neste link no meu blog: http://www.itexto.net/devkico/?p=5
O código está em Groovy mas é quase idêntico ao que você criaria em Java.

E, a outra adaptação que você tem de fazer é no MessageDigest, que ao invés de receber uma string, vai ter de receber um array de bytes.

A interface CheckSum é implementada pelas classes Adler e CRC32 mas eu usaria um MD5 - é mais seguro que um simples CRC32. Rode o programa abaixo e veja porque é que não se recomenda usar Adler32.

import java.security.*;
import java.util.zip.*;

class TesteChecksum {

    private static char[] hexDigits = "0123456789ABCDEF".toCharArray();
    private static String hex (byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append (hexDigits[(b & 0xF0) >>> 4]).append (hexDigits[b & 0x0F]);
        }
        return sb.toString();
    }


    public static void main (String[] args) throws Exception {
        Checksum cs1 = new CRC32(), cs2 = new Adler32();
        MessageDigest md5 = MessageDigest.getInstance ("MD5");
     
        System.out.println ("  CRC32  |  Adler32 | MD5");
        for (int i = 1; i < 20; ++i) {
            cs1.reset(); cs2.reset(); md5.reset();
            byte[] buf = new byte[i]; buf[0] = 1;
            cs1.update (buf, 0, buf.length);
            cs2.update (buf, 0, buf.length);
            byte[] digest = md5.digest (buf);
            System.out.printf ("%08X | %08X | %s%n", cs1.getValue (), cs2.getValue(), hex (digest));
        }
    }
}

[quote=thingol]A interface CheckSum é implementada pelas classes Adler e CRC32 mas eu usaria um MD5 - é mais seguro que um simples CRC32. Rode o programa abaixo e veja porque é que não se recomenda usar Adler32.

[code]
import java.security.;
import java.util.zip.
;

class TesteChecksum {

private static char[] hexDigits = "0123456789ABCDEF".toCharArray();
private static String hex (byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append (hexDigits[(b & 0xF0) >>> 4]).append (hexDigits[b & 0x0F]);
    }
    return sb.toString();
}


public static void main (String[] args) throws Exception {
    Checksum cs1 = new CRC32(), cs2 = new Adler32();
    MessageDigest md5 = MessageDigest.getInstance ("MD5");
 
    System.out.println ("  CRC32  |  Adler32 | MD5");
    for (int i = 1; i < 20; ++i) {
        cs1.reset(); cs2.reset(); md5.reset();
        byte[] buf = new byte[i]; buf[0] = 1;
        cs1.update (buf, 0, buf.length);
        cs2.update (buf, 0, buf.length);
        byte[] digest = md5.digest (buf);
        System.out.printf ("%08X | %08X | %s%n", cs1.getValue (), cs2.getValue(), hex (digest));
    }
}

}
[/code][/quote]

Algum exemplo usando MD5?

Ah ta…esse MessageDigest é o tal do MD5?

Esse método hex serve pra pegar a representação hexadecimal de cada byte no array passado? Ou entendi errado?
Como que usa tudo isso pra ver que um arquivo baixado é uma cópia identica de um arquivo num servidor?

Tá bom, tá bom, vamos escrever um método que lê um arquivo e calcula seu MD5.

import java.util.*;
import java.io.*;
import java.security.*;

class TesteMD5 {
    private static char[] hexDigits = "0123456789ABCDEF".toCharArray();
    private static String hex (byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append (hexDigits[(b & 0xF0) >>> 4]).append (hexDigits[b & 0x0F]);
        }
        return sb.toString();
    }

    public static String calcMD5 (File f) {
        try {
            MessageDigest md = MessageDigest.getInstance ("MD5");
            InputStream is = null;
            try {
                is = new BufferedInputStream (new FileInputStream (f));
                byte[] buf = new byte[8192];
                for (int nBytes = is.read (buf, 0, buf.length); nBytes > 0; nBytes = is.read (buf, 0, 

buf.length)) {
                    md.update (buf, 0, nBytes);
                }
            } catch (IOException ex) {
                if (is != null) try { is.close(); } catch (IOException ex2) { }
            }
            byte[] digest = md.digest();
            return hex (digest);
        } catch (NoSuchAlgorithmException ex) {
            // MD5 sempre está disponível
            throw new RuntimeException ("Can't happen", ex);
        }
    }
    public static void main (String[] args) {
        System.out.println (calcMD5 (new File ("teste.bin")));
    }
}

[quote=thingol]Tá bom, tá bom, vamos escrever um método que lê um arquivo e calcula seu MD5.

[code]
import java.util.;
import java.io.
;
import java.security.*;

class TesteMD5 {
private static char[] hexDigits = "0123456789ABCDEF".toCharArray();
private static String hex (byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append (hexDigits[(b & 0xF0) >>> 4]).append (hexDigits[b & 0x0F]);
}
return sb.toString();
}

public static String calcMD5 (File f) {
    try {
        MessageDigest md = MessageDigest.getInstance ("MD5");
        InputStream is = null;
        try {
            is = new BufferedInputStream (new FileInputStream (f));
            byte[] buf = new byte[8192];
            for (int nBytes = is.read (buf, 0, buf.length); nBytes > 0; nBytes = is.read (buf, 0, 

buf.length)) {
md.update (buf, 0, nBytes);
}
} catch (IOException ex) {
if (is != null) try { is.close(); } catch (IOException ex2) { }
}
byte[] digest = md.digest();
return hex (digest);
} catch (NoSuchAlgorithmException ex) {
// MD5 sempre está disponível
throw new RuntimeException (“Can’t happen”, ex);
}
}
public static void main (String[] args) {
System.out.println (calcMD5 (new File (“teste.bin”)));
}
}
[/code][/quote]

Obrigado Thingol.
Pelo que entendi, o que preciso fazer é o seguinte:

1- Calcular o md5 do arquivo que está no servidor FTP, antes de baixá-lo.
2- Baixar o arquivo.
3- Calcular o md5 do arquivo depois de baixado.
4- Comparar os dois md5’s gerados e considerar que o arquivo foi corretamente baixado somente se ambos forem exatamente iguais.

É isso mesmo?

Thingol, algum motivo particular pra escolher MD5 em vez de SHA1?

Nenhum - até porque o MD5 foi “quebrado” recentemente, e não deve ser usado para fins sérios (como assinatura digital).
A Microsoft, por exemplo, prefere que você use o SHA-1 para verificar os downloads dos arquivos do site deles.
É só uma questão de costume (por exemplo, muitos sites oferecem downloads e com o hash MD5 também); se quiser, pode usar o SHA-512 (por exemplo).
É que o MD5, desses hashes que já vêm com o JDK, é o mais rápido (se não me engano, não fiz nenhum teste de desempenho), e não gera um hash muito grande. O MD5 são 16 bytes, mas o SHA-512 são 64 bytes.