/*
* Demoiselle Framework
* Copyright (C) 2010 SERPRO
* ----------------------------------------------------------------------------
* This file is part of Demoiselle Framework.
*
* Demoiselle Framework is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License version 3
* along with this program; if not, see <http://www.gnu.org/licenses/>
* or write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301, USA.
* ----------------------------------------------------------------------------
* Este arquivo é parte do Framework Demoiselle.
*
* O Framework Demoiselle é um software livre; você pode redistribuí-lo e/ou
* modificá-lo dentro dos termos da GNU LGPL versão 3 como publicada pela Fundação
* do Software Livre (FSF).
*
* Este programa é distribuído na esperança que possa ser útil, mas SEM NENHUMA
* GARANTIA; sem uma garantia implícita de ADEQUAÇÃO a qualquer MERCADO ou
* APLICAÇÃO EM PARTICULAR. Veja a Licença Pública Geral GNU/LGPL em português
* para maiores detalhes.
*
* Você deve ter recebido uma cópia da GNU LGPL versão 3, sob o título
* "LICENCA.txt", junto com esse programa. Se não, acesse <http://www.gnu.org/licenses/>
* ou escreva para a Fundação do Software Livre (FSF) Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA.
*/
package br.gov.frameworkdemoiselle.certificate.ca.provider.impl;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownServiceException;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.xml.bind.DatatypeConverter;
import br.gov.frameworkdemoiselle.certificate.ca.provider.ProviderCA;
public class ICPBrasilOnLineSerproProviderCA implements ProviderCA {
private static final String STRING_URL_ZIP = "http://repositorio.serpro.gov.br/icp-brasil/ACcompactado.zip";
private static final String STRING_URL_HASH = "http://repositorio.serpro.gov.br/icp-brasil/hashsha512.txt";
private static final int TIMEOUT_CONNECTION = 3000;
private static final int TIMEOUT_READ = 5000;
private static final Logger LOGGER = Logger.getLogger(ICPBrasilOnLineSerproProviderCA.class.getName());
public String getURLZIP() {
return ICPBrasilOnLineSerproProviderCA.STRING_URL_ZIP;
}
public String getURLHash() {
return ICPBrasilOnLineSerproProviderCA.STRING_URL_HASH;
}
@Override
public Collection<X509Certificate> getCAs() {
Collection<X509Certificate> result = null;
boolean useCache = false;
try {
// Faz o hash do checksum do arquivo e não usa o local de propósito,
// pois o arquivo pode ter sido corrompido e neste caso o check vai
// dar errado e baixar novamente
String pathZip = ICPBrasilUserHomeProviderCA.FULL_PATH_ZIP;
File filePathZip = new File(pathZip);
if (filePathZip.exists()) {
// Baixa o hash do endereço online
InputStream inputStreamHash = getInputStreamFromURL(getURLHash());
// Convert o input stream em string
Scanner scannerOnlineHash = new Scanner(inputStreamHash);
scannerOnlineHash.useDelimiter("\\A");
String onlineHash = scannerOnlineHash.hasNext() ? scannerOnlineHash.next() : "";
scannerOnlineHash.close();
if (!onlineHash.equals("")) {
// Gera o hash do arquivo local
String localZipHash = DatatypeConverter.printHexBinary(checksum(filePathZip));
// Pega SOMENTE o hash sem o nome do arquivo
String onlineHashWithouFilename = onlineHash.replace(ICPBrasilUserHomeProviderCA.FILENAME_ZIP, "")
.replaceAll(" ", "").replaceAll("\n", "");
if (onlineHashWithouFilename.equalsIgnoreCase(localZipHash)) {
useCache = true;
} else {
useCache = false;
}
} else {
LOGGER.log(Level.WARNING, "Ocorreu um erro ao obter o hash online, pois está vazio.");
}
}
// Se não é para pegar do cache os certificados ele baixa o novo e
// salva localmente
if (!useCache) {
// Baixa um novo arquivo
LOGGER.log(Level.INFO, "Recuperando REMOTAMENTE as cadeias da ICP-Brasil [" + getURLZIP() + "].");
InputStream inputStreamZip = getInputStreamFromURL(getURLZIP());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStreamZip.read(data, 0, data.length)) != -1)
buffer.write(data, 0, nRead);
buffer.flush();
byte[] content = buffer.toByteArray();
FileOutputStream out = new FileOutputStream(filePathZip);
out.write(content);
out.close();
inputStreamZip.close();
LOGGER.log(Level.INFO, "Cadeias da ICP-Brasil recupedadas com sucesso.");
}
// Pega os certificados locais
InputStream inputStreamZipReturn = new FileInputStream(pathZip.toString());
result = getFromZip(inputStreamZipReturn);
inputStreamZipReturn.close();
LOGGER.log(Level.INFO, "Recuperou [" + result.size() + "] certificados do arquivo que foi baixado.");
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Erro ao tentar recuperar a cadeia.", e);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Erro inesperado ao tentar recuperar a cadeia.", e);
}
if (result != null) {
LOGGER.log(Level.INFO, "O Provider " + getName() + " possui [" + result.size() + "] certificados.");
} else {
LOGGER.log(Level.INFO, "O Provider " + getName() + " NÃO possui certificados.");
}
return result;
}
public byte[] checksum(File input) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(input);
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] block = new byte[4096];
int length;
while ((length = in.read(block)) > 0) {
digest.update(block, 0, length);
}
return digest.digest();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null)
in.close();
}
return null;
}
public Collection<X509Certificate> getOnline(InputStream zip) {
Collection<X509Certificate> result = new HashSet<X509Certificate>();
long timeBefore = 0;
long timeAfter = 0;
try {
timeBefore = System.currentTimeMillis();
result = this.getFromZip(zip);
timeAfter = System.currentTimeMillis();
} catch (Throwable error) {
timeAfter = System.currentTimeMillis();
LOGGER.log(Level.WARNING, "ERRO. [" + error.getMessage() + "].");
} finally {
LOGGER.log(Level.INFO, "Levamos " + (timeAfter - timeBefore) + "ms para recuperar as cadeias.");
}
return result;
}
public Collection<X509Certificate> getFromZip(InputStream zip) throws RuntimeException {
Collection<X509Certificate> result = new HashSet<X509Certificate>();
InputStream in = new BufferedInputStream(zip);
ZipInputStream zin = new ZipInputStream(in);
ZipEntry arquivoInterno = null;
try {
while ((arquivoInterno = zin.getNextEntry()) != null) {
if (!arquivoInterno.isDirectory()) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] b = new byte[512];
int len = 0;
while ((len = zin.read(b)) != -1)
out.write(b, 0, len);
ByteArrayInputStream is = new ByteArrayInputStream(out.toByteArray());
out.close();
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X509")
.generateCertificate(is);
is.close();
result.add(certificate);
}
}
} catch (CertificateException error) {
throw new RuntimeException("Certificado inválido", error);
} catch (IOException error) {
throw new RuntimeException("Erro ao tentar abrir o stream", error);
}
return result;
}
public InputStream getInputStreamFromURL(String stringURL) throws RuntimeException {
try {
URL url = new URL(stringURL);
URLConnection connection = url.openConnection();
connection.setConnectTimeout(TIMEOUT_CONNECTION);
connection.setReadTimeout(TIMEOUT_READ);
return connection.getInputStream();
} catch (MalformedURLException error) {
throw new RuntimeException("URL mal formada", error);
} catch (UnknownServiceException error) {
throw new RuntimeException("Serviço da URL desconhecido", error);
} catch (IOException error) {
throw new RuntimeException("Algum erro de I/O ocorreu", error);
}
}
@Override
public String getName() {
return "ICP Brasil ONLINE SERPRO Provider (" + getURLZIP() + ")";
}
}