-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDigestCalculator.java
425 lines (347 loc) · 14 KB
/
DigestCalculator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
// -------------------------
// Jam Ajna Soares - 2211689
// Olavo Lucas - 1811181
// -------------------------
import java.util.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import javax.crypto.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import javax.xml.xpath.*;
import java.io.*;
public class DigestCalculator
{
public enum Estados
{
TOBECHECKED,
OK,
NOTOK,
NOTFOUND,
COLISION
}
public static void main(String[] args) throws IOException
{
String[] argumentos = args;
HashSet<String> tiposSuportadosDigest = new HashSet<String>(Arrays.asList("MD5","SHA1","SHA256","SHA512"));
if (argumentos.length != 3)
{
System.err.println("Usage: java DigestCalculator DigestType FileAddress DigestListFileAddress");
System.exit(1);
}
String tipoDigest = argumentos[0].toUpperCase();
String arquivosCalcular = argumentos[1];
String arqDigest = argumentos[2];
if (!tiposSuportadosDigest.contains(tipoDigest))
{
System.err.println("Error: " + argumentos[0]+ " não é suportado");
System.exit(1);
}
if (tipoDigest.equals("SHA1") || tipoDigest.equals("SHA256") || tipoDigest.equals("SHA512"))
{
tipoDigest = tipoDigest.substring(0, 3) + "-" + tipoDigest.substring(3);
}
File arquivoCal = new File(arquivosCalcular);
if (!arquivoCal.exists())
{
System.err.println("Error: " + argumentos[1]+ " não foi encontrado");
System.exit(1);
}
else if (!arquivoCal.isDirectory())
{
System.err.println("Error: " + argumentos[2] + " não é uma pasta");
System.exit(1);
}
File arquivoDi = new File(arqDigest);
if (!arquivoDi.exists())
{
System.err.println("Error: " + argumentos[2]+ " não foi encontrado");
System.exit(1);
}
else if(!arquivoDi.isFile())
{
System.err.println("Error: " + argumentos[2]+ " não é um arquivo");
System.exit(1);
}
DocumentBuilderFactory factory;
DocumentBuilder builder;
Document listXML = null;
try
{
factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
listXML = builder.parse(arquivoDi);
listXML.getDocumentElement().normalize();
}
catch (ParserConfigurationException parserProblem)
{
System.err.println("error na configuração do parser de XML");
System.exit(1);
}
catch(Exception SAXProblem)
{
System.err.println("error na leitura do arquivo XML");
System.exit(1);
}
//verificar se arquivo DI é um arquivo XML no formato esperado pelo programa
/* cria lista de arquivos a serem calculados e dados como digest, tipo de digest
e estado */
List<Object[]> listaArquivos = new ArrayList<Object[]>();
for (File file : arquivoCal.listFiles())
{
String arquivoNomei = file.getName();
String digestTipoi = tipoDigest;
String digesti = digestString(digestCalculate(digestTipoi, file));
Estados estado = Estados.TOBECHECKED;
Object[] linha = { (Object) arquivoNomei, (Object) digestTipoi, (Object) digesti, (Object) estado };
listaArquivos.add(linha);
}
listaArquivos = digestDetectColision(listaArquivos);
listaArquivos = digestCompare(listaArquivos, listXML);
updateXML(listXML, listaArquivos);
printList(listaArquivos);
}
private static String digestString(byte[] digest)
{
StringBuffer buf = new StringBuffer();
for(int i = 0; i < digest.length; i++)
{
String hex = Integer.toHexString(0x0100 + (digest[i] & 0x00FF)).substring(1);
buf.append((hex.length() < 2 ? "0" : "") + hex);
}
return buf.toString();
}
//TODO implementar digestCalculate, recebe o arquivo e o tipo de digest, retorna o digest calculado e uma array de byte, procure o metodo update adequado
protected static byte[] digestCalculate(String tipoDigest, File conteudo)
{
// retorna o digest calculado e uma array de byte, procure o metodo update
// adequado: Update(Byte[], Int32, Int32)
int bufferSize = 1024;
byte[] result = {};
try
{
MessageDigest calculadora = MessageDigest.getInstance(tipoDigest);
byte[] bytebuffer = new byte[bufferSize];
FileInputStream leitor = new FileInputStream(conteudo);
int check = leitor.read(bytebuffer);
while(check != -1)
{
calculadora.update(bytebuffer, 0, check);
check = leitor.read(bytebuffer);
}
leitor.close();
result = calculadora.digest();
}
catch(IOException e)
{
System.err.println("Erro na leitura do arquivo durante o calculo do digest");
System.exit(1);
}
catch (NoSuchAlgorithmException e)
{
System.err.println("Error: " + tipoDigest + " não é suportado por essa aplicação");
System.exit(1);
}
return result;
}
//TODO implementar disgestDetecColision, use algo como dicionario de python com os disgest como chave e os valores como listas e para cada no digest calculado, adicionar a lista adequada, no final, procure por listas com tamanho maior que 1 e marque os elementos com COLISION
protected static List<Object[]> digestDetectColision(List<Object[]> list)
{
// use algo como dicionario de python com
// os digests como chave e os valores como listas e para cada no digest
// calculado, adicionar a lista adequada, no final, procure por listas com
// tamanho maior que 1 e marque os elementos com COLISION
HashMap <String, List<Integer>> mapeamento = new HashMap<String, List<Integer>>();
for (int i = 0; i< list.size(); i++)
{
String digest = (String) list.get(i)[2];
if(!mapeamento.containsKey(digest))
{
mapeamento.put(digest,new ArrayList<Integer>());
}
mapeamento.get(digest).add(i);
}
for (List<Integer> i : mapeamento.values())
{
if (i.size() > 1)
{
for (int j:i)
{
list.get(j)[3] = Estados.COLISION;
}
}
}
return list;
}
//TODO implementar digestCompare, recebe a lista de digest calculado e compara com os digests salvos no arquivo XML, atualiza o campo de status de cada item com os valores adequados e chame o updateXML caso um ou mais elementos deram como NOTFOUND
protected static List<Object[]> digestCompare(List<Object[]> list, Document XMLroot)
{
//crie um hashset com pares (tipo_digest, digest) e veja se algum item de list está incluso e marque no encontradoSet
//organize queries no XML
//primeiro nome do arquivo, segundo o tipo de digest, terceiro o digest
//no caso de falhas e encontros, registre no encontradoQuerie
//Compare os booleanos para os casos:
//OK: true em ambos
//NOTFOUND: false em ambos
//COISION: true no encontradoSet e false no encontradoQuery
//NOTOK: false no encontradoSet e true no encontradoQuery
Element root = XMLroot.getDocumentElement();
HashMap<String,HashSet<String>> XMLdigests = new HashMap<String,HashSet<String>>();
List<Boolean> encontradoSet = new ArrayList<Boolean>();
List<Boolean> encontradoQuerie = new ArrayList<Boolean>();
if(!root.hasChildNodes())
{
for (Object[] line : list)
{
Estados check = (Estados) line[3];
if(check == Estados.COLISION){continue;}
line[3] = Estados.NOTFOUND;
//XMLUpdate ou acumula para fazer update com todos os novos digest
}
return list;
}
for(Node childNode = root.getFirstChild(); childNode != null; childNode = childNode.getNextSibling())
{
NodeList Arqinfo = childNode.getChildNodes();
for(int i = 0; i < Arqinfo.getLength(); i++)
{
if(Arqinfo.item(i).getNodeName().equals("FILE_NAME"))
{
continue;
}
NodeList DigestInfo = Arqinfo.item(i).getChildNodes();
String DigestType = DigestInfo.item(0).getNodeValue();
String DigestHex = DigestInfo.item(1).getNodeValue();
if(!XMLdigests.containsKey(DigestType))
{
XMLdigests.put(DigestType, new HashSet<String>());
}
XMLdigests.get(DigestType).add(DigestHex);
}
}
for (Object[] line : list)
{
Estados check = (Estados) line[3];
if(check == Estados.COLISION){continue;}
String NomeArquivo = (String) line[0];
String digestTipo = (String) line[1];
String digest = (String) line[2];
try
{
XPath xPath = XPathFactory.newInstance().newXPath();
encontradoSet.add(XMLdigests.get(digestTipo).contains(digest));
String expressaoArquivo = "/FILE_ENTRY[FILE_NAME = '"+NomeArquivo+"']/DIGEST_ENTRY[DIGEST_TYPE = '"+digestTipo+"']/DIGEST_HEX";
XPathExpression expr = xPath.compile(expressaoArquivo);
NodeList ProcuraDigest = (NodeList) expr.evaluate(XMLroot, XPathConstants.NODESET);
int tam = ProcuraDigest.getLength();
if (tam == 0)
{
encontradoQuerie.add(false);continue;
}
else if (tam > 1)
{
List<Integer> indices = new ArrayList<Integer>();
for (int i = 0; i < tam; i++)
{
if(ProcuraDigest.item(i).getNodeValue().equals(digest)){indices.add(i);}
}
int numPositivos = indices.size();
if(numPositivos == 1)
{
encontradoQuerie.add(true);
}
else
{
encontradoQuerie.add(false);
}
continue;
}
encontradoQuerie.add(digest.equals(ProcuraDigest.item(0).getNodeValue()));
} catch(XPathException x)
{
System.err.println("Erro no Xpath durante a procura dos arquivos");
System.exit(1);
}
for (int i = 0; i < list.size(); i++)
{
boolean resultadoQuerie = encontradoQuerie.get(i);
boolean resultadoSet = encontradoSet.get(i);
if(resultadoQuerie && resultadoSet){
list.get(i)[3] = Estados.OK;
}
else if(resultadoQuerie && !resultadoSet){
list.get(i)[3] = Estados.NOTOK;
}
else if(!resultadoQuerie && resultadoSet){
list.get(i)[3] = Estados.COLISION;
}
else if(!resultadoQuerie && !resultadoSet){
list.get(i)[3] = Estados.NOTFOUND;
//XML Update ou acumula para um unico update
}
}
}
return list;
}
//TODO implementar updateXML, atualiza o arquivo XML recebido com as informações, prioridade: Digest entry> File entry> Catalog
protected static void updateXML(Document XMLroot, List<Object[]> list){
Element root = XMLroot.getDocumentElement();
for(Object[] line: list){
Estados status = (Estados) line[3];
if(status!=Estados.NOTFOUND){continue;}
String nome = (String) line[0];
String tipo = (String) line[1];
if(tipo.equals("SHA-1")||tipo.equals("SHA-256")||tipo.equals("SHA-512"))
{tipo = tipo.substring(0, 3) + tipo.substring(4);}
String digest = (String) line[2];
String expressaoArquivo = "/FILE_ENTRY[FILE_NAME = '"+nome+"']";
try{
Node digestHex = XMLroot.createElement("DIGEST_HEX");
Node digestHexText = XMLroot.createTextNode(digest);
digestHex.appendChild(digestHexText);
Node digestType = XMLroot.createElement("DIGEST_TYPE");
Node digestTypeText = XMLroot.createTextNode(tipo);
digestType.appendChild(digestTypeText);
Node digestEntry = XMLroot.createElement("DIGEST_ENTRY");
digestEntry.appendChild(digestType);
digestEntry.appendChild(digestHex);
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xPath.compile(expressaoArquivo);
NodeList ProcuraArquivo = (NodeList) expr.evaluate(XMLroot, XPathConstants.NODESET);
int tam = ProcuraArquivo.getLength();
if (tam == 0){
Node fileName = XMLroot.createElement("FILE_NAME");
Node fileNameText = XMLroot.createTextNode(nome);
fileName.appendChild(fileNameText);
Node fileEntry = XMLroot.createElement("FILE_ENTRY");
fileEntry.appendChild(fileName);
fileEntry.appendChild(digestEntry);
root.appendChild(fileEntry);
}
else{
ProcuraArquivo.item(0).appendChild(digestEntry);
}
} catch(Exception e) {
System.err.println("Erro na escrita do arquivo XML");
System.exit(1);
}
}
}
//TODO implementar printList, imprime a lista recebida no formato especificado no enunciado:
//nome_arquivo <espaço> tipo_digest <espaço> digest_Hex_calculado <espaço> Status
//IMPORTANTE: presta atenção como é salvo o hexadecimal comparado a string
protected static void printList(List<Object[]> list)
{
for (Object[] line: list)
{
String nomeArquivo = (String) line[0];
String tipoDigest = (String) line[1];
String digest = (String) line[2];
Estados estado = (Estados) line[3];
System.out.println(nomeArquivo + " " + tipoDigest + " " + digest + " " + estado);
}
}
}