Utilitário para geração de TrustStore (JKS) – Java

Desenvolvi um utilitário que transforma um arquivo zip, contendo “n” certificados de Autoridades Certificadoras para um arquivo JKS.

Para referência segue um link que contém todas as CA’s brasileiras:

http://acraiz.icpbrasil.gov.br/credenciadas/CertificadosAC-ICP-Brasil/ACcompactado.zip

Como seria o processo normal ?

1 – Java Keytool – http://download.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html
É um software que acompanha a máquina virtual Java (jdk), utilizado para diversas finalidades com relação à segurança de aplicações e pode ser usado para gerar o pacote JKS.
Seguem dois comandos exemplo para se trabalhar com arquivos JKS de truststore:

1. Listando todos os certificados contidos em um arquivo JKS:
[sourcecode language=”java”]keytool –list –v –keystore C:\meukeystore.jks[/sourcecode]

2. Importando um certificado de uma AC para dentro de um JKS pré-existente
[sourcecode language=”java”]keytool –import –trustcacerts –file C:\certificadoAC.cer –alias apelidoentrada –keystore C:\meutruststore.jks[/sourcecode]

Observação: Caso não exista um jks no diretório especificado em -keystore, será criado um automaticamente.
O arquivo ACcompactado.zip especificado na URL anteriormente, possui atualmente 84 certificados, portanto, deve-se executar o comando (2), 84 vezes, alterando o -alias e o –file. Por ser esta uma forma muito trabalhosa de se gerar um arquivo Trusted JKS, foi criado um utilitário que auxiliará nesta etapa, apresentado no próximo capítulo.

2 – Usando o utilitário – utilitarioTrustJKS.jar
O utilitário desenvolvido basicamente recebe como entrada um arquivo zip com todos os certificados desejados e gera um arquivo trust.jks.
Parâmetros de Entrada:
1 – caminhoZip – Diretório do arquivo zip que contém todos os certificados das AC’s
2 – caminhoSaida – Diretório de saída, onde será gerado o truststore.jks
3 – senhaKestore – Senha do arquivo TrustStore JKS
4 – incluirExpirados (opcional) – valor default => false) – Possui os valores true ou false. Informa se certificados expirados ou não válidos ainda deverão ser incluídos no arquivo JKS gerado.

Exemplo de utilização:

java -jar utilitarioTrustJKS.jar C:\ACcompactado.zip C:\truststore.jks 123456789 true

Link para Download do arquivo Jar:
http://arquivos.victorjabur.com/java/seguranca/utilitarioTrustJKS.jar

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;

public class Main {

private String caminhoZIP = "C:\\ACcompactado.zip";
private String caminhoSaida = "C:\\truststore.jks";
private String senhaKeystore = "123456789";
private Boolean incluirExpirados = false;

public static void main(String[] args) throws Exception {
Main main = new Main();
main.validarParametrosEntrada(args);
List<X509Certificate> listaCertificadosValidos = main.getListaCertificadosValidos();
main.gerarJKS(listaCertificadosValidos);
System.out.println("Arquivo JKS gerado com sucesso - " + listaCertificadosValidos.size() + " certificados incluidos");
}

public void validarParametrosEntrada(String[] args){
if(args.length < 3){
throw new RuntimeException("O numero minimo de parametros = 3. caminhoZip caminhoSaida senhaKeystore e incluirExpirados (Opcional) ");
}else{
String caminhoZip = args[0];
validacaoLeituraArquivo(new File(caminhoZip));
this.caminhoZIP = caminhoZip;
String caminhoSaida = args[1];
this.caminhoSaida = caminhoSaida;
String senhaKeystore = args[2];
this.senhaKeystore = senhaKeystore;
if(args.length >= 4){
String incluirExpirados = args[3];
if(incluirExpirados.equals("true") || incluirExpirados.equals("false")){
this.incluirExpirados = Boolean.valueOf(incluirExpirados);
}else{
throw new RuntimeException("O parametro incluirExpirados (4) deve ser true ou false");
}
}
}
}

public List<X509Certificate> getListaCertificadosValidos() {
File arquivoZipEntrada = new File(this.caminhoZIP);
validacaoLeituraArquivo(arquivoZipEntrada);
List<X509Certificate> x509CertificateList = new LinkedList<X509Certificate>();
try {
InputStream in = new FileInputStream(arquivoZipEntrada);
ZipInputStream zipInputStream = new ZipInputStream(in);

ZipEntry zipentry = zipInputStream.getNextEntry();
while (zipentry != null) {
byte[] buffer = new byte[(int) zipentry.getSize()];
int offset = 0;
int numRead = 0;
while (offset < buffer.length && (numRead = zipInputStream.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += numRead;
}
InputStream bis = new ByteInputStream(buffer, 0, buffer.length);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
try {
cert.checkValidity();
x509CertificateList.add(cert);
} catch (CertificateExpiredException e) {
if(this.incluirExpirados){
x509CertificateList.add(cert);
System.out.println("Certificado Expirado - " + zipentry.getName());
}else{
System.out.println("Certificado Expirado - " + zipentry.getName() + " - nao sera adicionado no JKS");
}
} catch (CertificateNotYetValidException e) {
if(this.incluirExpirados){
x509CertificateList.add(cert);
System.out.println("Certificado não válido ainda - " + zipentry.getName());
}else{
System.out.println("Certificado não válido ainda - " + zipentry.getName() + " - nao sera adicionado no JKS");
}
}
zipInputStream.closeEntry();
zipentry = zipInputStream.getNextEntry();
}
zipInputStream.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return x509CertificateList;
}

public OutputStream gerarJKS(List<X509Certificate> listaCertificados){
OutputStream out = null;
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(null, null);
int indice = 0;
for (X509Certificate cert : listaCertificados) {
keystore.setCertificateEntry("ac_" + indice, cert);
indice++;
}
File jks = new File(this.caminhoSaida);
String diretorioDestino = jks.getParent();
new File(diretorioDestino).mkdirs();
out = new FileOutputStream(this.caminhoSaida);
keystore.store(out, this.senhaKeystore.toCharArray());
out.close();
out.flush();
} catch (Exception e) {
throw new RuntimeException("Erro ao gerar o arquivo de Keystore - " + e.getCause() + " - " + e.getMessage());
}
return out;
}

public void validacaoLeituraArquivo(File arquivo) {
if (!arquivo.exists()) {
throw new RuntimeException("Arquivo Inexistente - " + arquivo.getAbsolutePath());
}
if (!arquivo.canRead()) {
throw new RuntimeException("Sem permissão de Leitura do Arquivo - " + arquivo.getAbsolutePath());
}
}
}

Abraços,
Victor Jabur