Fala ae galera, blz?
Putz, muito tempo não passo por aqui…
Seguinte, tive um problema para geração da assinatura digita dos arquivos do PAF-ECF
encontrei umas dica em um forum de automação e consegui solucionar o problema, porém
achei importante compartilhar a solução com a comunidade Java para se alguém um dia precisar…
Post Envia no Forum de Autmoção Comercial
http://www.forumweb.com.br/foruns/index.php?/topic/76285-homologacao-paf-eecfcexe-cuidado-com-o-ead/page__view__findpost__p__323254
segue abaixo as dicas que segui para geração da assinatura:
Para a geração da chave utilizei o OpenSSL:
openssl dgst -md5 -sign private.key -out rsa.sign -hex arquivo-ex.txt
Pra a geração do modulo o seguinte comando:
openssl rsa -in chave.pem -modulus -out modulo.txt
Copiei o conteudo da primeira linha do arquivo modulo.txt, para o arquivo de configurações do validador eECFc.exe
Bem, como prometi, criei um programa em Java que cria uma assinatura válida, deve ser executado no console, necessário o instalar o Java (jvm).
[code]Como executar o programa:
java -jar mgsys-openpaf.jar
Funciona tanto no windows quanto no Linux[/code]
Segue link para download:
http://www.managersys.com.br/download/openpaf/
Ao fim da mensagem vou deixar o fonte do programa.
Ele pode ser usado tanto para assinar um arquivo existente, como para gerar um arquivo de exemplo e assiná-lo.
Também pode usar o programa para gerar o arquivo Software House.xml, depois basta alterar para no mome da sua empresa.
Observações importantes sobre o arquivo:
Com eu trabalho no Linux, estava gerando o arquivo usando apenas a quebra de linha #13, e esse foi o problema que tive quando mandei o post com o código.
Sobre o arquivo:
- Utilizar para quebra de linha (#10 + #13)
- Ao gerar a assinatura o final do arquivo deve ser uma linha em branco
[code]ERRADO: Nao ha linha em branco no fim do arquivo
// ----- INICIO DO ARQUIVO (BOF)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
// ----- FIM DO ARQUIVO (EOF)
CERTO: Ultima linha em branco
// ----- INICIO DO ARQUIVO (BOF)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO
// ----- FIM DO ARQUIVO (EOF)
ERRADO: Usou apenas o caracter 13 para pular linha
// ----- INICIO DO ARQUIVO (BOF)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 13)
// ----- FIM DO ARQUIVO (EOF)
CERTO: Usou caracter 10 e 13 para quebra de linha
// ----- INICIO DO ARQUIVO (BOF)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
// ----- FIM DO ARQUIVO (EOF)
// DETALHE IMPORTANTE
// APOS A ASSINATURA SEMPRE DEVE HAVER UMA LINHA EM BRANCO
ERRADO: Não há uma linha em branco após a assinatura
// ----- INICIO DO ARQUIVO (BOF)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
EADCDAB52C7E53E47D9B2A0325…A458B54EB2125039F8D76F4E270F
// ----- FIM DO ARQUIVO (EOF)
CERTO: Há uma linha em branco após a assinatura
// ----- INICIO DO ARQUIVO (BOF)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO TEXTO + (CHAR 10) + (CHAR 13)
EADCDAB52C7E53E47D9B2A0325…A458B54EB2125039F8D76F4E270F + (CHAR 10) + (CHAR 13)
// ----- FIM DO ARQUIVO (EOF)
[/code]
Muito obrigado pela dicas enviada… me ajudou pra caramba a gerar essa %%$$#@#@#$%!!@# de assinatura… ehehhe
Abraço,
Rodrigo G. Tavares de Souza
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.text.MessageFormat;
/**
* Geração da Assinatura EAD para os arquivos arquivos
*
* @author Rodrigo G. Tavares de Souza
* @author Managersys - Soluções em Sistemas
*/
public class AssinaturaDigitalPAF {
/**
* Buffer para leitura do console
*/
private static BufferedReader reader;
/**
* Caminho para a geração da chave privada
*/
private static final String PATH_PRIV_KEY = "private.key";
/**
* Caminho para geração do módulo
*/
private static final String PATH_MOD_KEY = "modulo.txt";
/**
* Caminho para geração do arquivo de configuração do validor eECFc
*/
private static final String PATH_SHOUSE = "Software House.xml";
/**
* Caminho padrão do OpenSSL no Linux
*/
private static final String LNX_CAMINNHO_SSL = "/usr/bin/";
/**
* Caminho para geração do arquivo de teste
*/
private static final String PATH_ARQUIVO_TESTE = "arquivo-ex.txt";
/**
* Caminho padrão do OpenSSL no Windows
*/
private static final String WIN_CAMINNHO_SSL = "/Arquivos de Programas/GnuWin32/bin/";
/**
* Comando que será utilizado para a execução do OpenSSL
*/
private static String openssl;
/**
* Contem o modelo para geracao do XML de configuracao
*/
private static StringBuffer modeloXMLConfig;
static {
// Arquivo Padrão Para Validação da Assinatura
modeloXMLConfig = new StringBuffer();
modeloXMLConfig.append("<?xml version=\"1.0\"?>\r\n");
modeloXMLConfig.append("<empresa_desenvolvedora>\r\n");
modeloXMLConfig.append(" <nome>Software House</nome>\r\n");
modeloXMLConfig.append(" <chave>\r\n");
modeloXMLConfig.append(" <modulo>{0}</modulo>\r\n");
modeloXMLConfig.append(" <expoente_publico>10001</expoente_publico>\r\n");
modeloXMLConfig.append(" </chave>\r\n");
modeloXMLConfig.append("</empresa_desenvolvedora>\r\n");
}
public static void main(String[] args) throws Exception {
System.out.println("*************************************");
System.out.println("* Managersys - Solucoes em Sistemas *");
System.out.println("* contato@managersys.com.br *");
System.out.println("* Fone: (19) 3388-1212 *");
System.out.println("*************************************");
System.out.println("* GERADOR ASSINATURA RSA - PAF-ECF *");
System.out.println("* >> UTILIZACAO E COPIA LIVRE << *");
System.out.println("*************************************");
// Verifica o sistema operacional
String os = System.getProperty("os.name");
String path = null;
String cmd = "";
if (os.toUpperCase().equals("LINUX")) {
path = LNX_CAMINNHO_SSL;
cmd = "openssl";
} else {
path = WIN_CAMINNHO_SSL;
cmd = "openssl.exe";
}
// Solicita informação do caminho do OpenSSL, vazio assume
// o valor padrão
File fOpenssl = new File(path);
String vlr = lerConsole("Caminho OpenSSL ["
+ fOpenssl.getAbsolutePath() + "] : ");
// Atribuindo o caminho para o OpenSSL
if (vlr.isEmpty())
openssl = path + File.separator + cmd;
else
openssl = vlr + File.separator + cmd;
// Verifica se o OpenSSL está encontra-se na máquina
fOpenssl = new File(openssl);
if (!fOpenssl.exists()) {
System.out.println();
System.err.println("OpenSSL nao encontrado em [" + fOpenssl.getAbsolutePath() + "]");
System.err.println(" * Necessario instalar o OpenSSL para prosseguir");
System.err.println(" * Linux: http://www.openssl.org/");
System.err.println(" * Windows: http://gnuwin32.sourceforge.net/packages/openssl.htm");
System.exit(-1);
}
openssl = fOpenssl.getAbsolutePath();
// A primeira opçao efetua a geracao da assinatura
// para o arquivo informado
// A segunda opção efetua a geração dos arquivo
// de para a assinatura.
System.out.println("\nSelecione uma Opcao");
System.out.println("1. Gerar Assinatura");
System.out.println("2. Gerar Configuracoes");
vlr = lerConsole("Opcao: ");
// Verifica a seleção das Opçoes
if ("1".equals(vlr))
gerarAssinatura();
else if ("2".equals(vlr))
gerarConfiguracoes();
else {
System.out.println();
System.err.println("Opcao [" + vlr + "] invalida");
System.exit(-1);
}
}
private static void gerarAssinatura() throws Exception {
File fPriv = new File(PATH_PRIV_KEY);
// Verfica se a chave privada existe
if (!fPriv.exists()) {
System.out.println();
System.err.println("Chave privada nao encontrada");
System.exit(-1);
}
System.out.println("\nSelecione uma Opcao");
System.out.println("1. Gerar Arquivo Exemplo");
System.out.println("2. Informar Arquivo");
String vlr = lerConsole("Opcao: ");
File fArquivo = null;
// Verifica se deve gerar um arquivo de exemplo
// Ou o usuário irá gerar o arquivo
// Verifica a seleção das Opçoes
if ("1".equals(vlr)) {
System.out.println();
// Efetua a geração do arquivo de exemplo
fArquivo = new File(PATH_ARQUIVO_TESTE);
System.out.println("Gerando Arquivo de Exemplo ["
+ fArquivo.getAbsolutePath() + "]...");
criarArquivoExemplo(fArquivo);
System.out.println("Criado Arquivo de Exemplo ["
+ fArquivo.getName() + "]...");
}else if ("2".equals(vlr)) {
// Usuário vai informar o caminho do arquivo
vlr = lerConsole("Caminho do Arquivo: ");
if (vlr.isEmpty()) {
System.out.println();
System.out.println("Caminho do arquivo nao informado");
System.exit(0);
}
// validação do arquivo informado
fArquivo = new File(vlr);
// Verifica se existe
if (!fArquivo.exists()) {
System.out.println();
System.out.println("Arquivo informado nao existe ["
+ fArquivo.getAbsolutePath() + "] ");
System.exit(0);
}
// Verifica se é um arquivo ou diretório
if (fArquivo.isDirectory()) {
System.out.println();
System.out.println("Impossivel assinar um diretorio ["
+ fArquivo.getAbsolutePath() + "] ");
System.exit(0);
}
// Verifica se tem permissao de escriuta
if (!fArquivo.canWrite()) {
System.out.println();
System.out.println("Sem permissao de escrita no arquivo ["
+ fArquivo.getAbsolutePath() + "] ");
System.exit(0);
}
} else {
System.out.println();
System.err.println("Opcao [" + vlr + "] invalida");
System.exit(-1);
}
System.out.println();
// Iniciando assinatura do arquivo
System.out.println("Iniciando assinatura do arquivo [" + fArquivo.getName() + "]");
// Caminho do arquivo para geração do arquivo
// com a assinatura
final String PATH_TMP_SIGN = "sign.tmp";
File fSign = new File(PATH_TMP_SIGN);
Runtime rt = Runtime.getRuntime();
// Executando comando OpenSSL
Process pr = rt.exec(openssl + " dgst -md5 -sign "
+ fPriv.getAbsolutePath() + " -out "
+ fSign.getAbsolutePath() + " -hex "
+ fArquivo.getAbsolutePath());
// Aguarda até que o processo tenha terminado
int status = pr.waitFor();
// Valor diferente de Zero, indica
// que houve falha ao executar o comando
if (status != 0) {
System.out.println();
System.err.println("Falha ao executar OpenSSL");
System.exit(status);
}
// Verifica se o arquivo foi gerado
if (!fSign.exists()) {
System.out.println();
System.err.println("Comando OpenSSL executado, "
+ "porem arquivo temporario com a " + "assinatura ["
+ fSign.getAbsolutePath() + "] não foi gerado");
System.exit(status);
}
System.out.println("Verificando dados da assinatura gerada...");
// Leitura do conteúdo da assintatura
StringBuffer sbSign = new StringBuffer();
// Abrindo arquivo com a assinatura
FileInputStream inSign = new FileInputStream(fSign);
byte[] b = new byte[inSign.available()];
inSign.read(B);
sbSign.append(new String(B));
System.out.println("Icluindo linha EAD no " + "arquivo ["
+ fArquivo.getName() + "] com assinatura gerada");
// Arquivo gerado com
String assinatura = "EAD"
+ sbSign.substring(sbSign.indexOf("=")+1).trim() + "\r\n";
// Abre arquivo para inclusão da assinatura
System.out.println("Assinatura: " + assinatura);
FileOutputStream outArquivo = new FileOutputStream(fArquivo, true);
outArquivo.write(assinatura.getBytes());
outArquivo.flush();
outArquivo.close();
// Concluído o processo de geração da assinatura
System.out.println();
System.out.println("Assinatura digital gerada com sucesso");
System.out.println(" + Arquivo Assinado: " + fArquivo.getAbsolutePath());
}
/**
* Geração dos arquivos necessários para a configuração
*/
private static void gerarConfiguracoes() throws Exception {
File fPriv = new File(PATH_PRIV_KEY);
// Verfica se a chave privada já foi gerada
if (fPriv.exists()) {
System.out.println();
System.out.println("Chave privada ja existe, apagar para continuar");
System.exit(0);
}
// Se encontrou o modulo deleta
File fMod = new File(PATH_MOD_KEY);
if (fMod.exists())
fMod.delete();
// Se encontrou configuracao deleta
File fXml = new File(PATH_SHOUSE);
if (fXml.exists())
fXml.delete();
// Executa comando para geração do arquivo
System.out.println("Gerando chave privada [" + fPriv.getAbsolutePath()
+ "]...");
Runtime rt = Runtime.getRuntime();
// Executando comando OpenSSL
Process pr = rt.exec(openssl + " genrsa -out " + fPriv.getAbsolutePath() + " 1024 ");
int status = pr.waitFor();
// Valor diferente de Zero, indica
// que houve falha ao executar o comando
if (status != 0) {
System.out.println();
System.err.println("Falha ao executar OpenSSL");
System.exit(status);
}
System.out.println("Chave privada gerada [" + fPriv.getName() + "]");
// Iniciando a geração do módulo
System.out.println("Gerando módulo [" + fMod.getAbsolutePath() + "]...");
pr = rt.exec(openssl + " rsa -in " + fPriv.getAbsolutePath()
+ " -modulus -out " + fMod.getAbsolutePath() + " ");
status = pr.waitFor();
// Valor diferente de Zero, indica
// que houve falha ao executar o comando
if (status != 0) {
System.out.println();
System.err.println("Falha ao executar OpenSSL");
System.exit(status);
}
System.out.println("Modulo gerado [" + fMod.getName() + "]");
// Criando arquivo de configurações
System.out.println("Criando arquivo de configuracao ["
+ fXml.getAbsolutePath() + "]...");
// Lendo o arquivo com o módulo
// Primeira linha do arquivo do módulo, contem o módulo
// Modulus=B4FCFDB3E...F1B85675F42646EDC5
BufferedReader modIn = new BufferedReader(new FileReader(fMod));
String modulo = modIn.readLine();
System.out.println(modulo);
// Fechando stream de leitura
modIn.close();
// Leitura do módulo após o sinal de igual
modulo = modulo.substring(modulo.indexOf('=')+1);
// Inserindo módulo no Parâmetro
String xml = MessageFormat.format(modeloXMLConfig.toString(), new Object[]{modulo});
// Criando arquivo de configuracoes
FileOutputStream modOut = new FileOutputStream(fXml);
modOut.write(xml.getBytes());
modOut.flush();
modOut.close();
// Arquivo de cofingurações criado
System.out.println("Arquivo de configuracao criado ["
+ fXml.getName() + "]");
System.out.println();
System.out.println("Geracao das configuracoes concluido...");
System.out.println(" + Chave : " + fPriv.getAbsolutePath() );
System.out.println(" + Modulo: " + fMod.getAbsolutePath() );
System.out.println(" + Config: " + fXml.getAbsolutePath() );
}
/**
* Efetua a leitura de dados do console
*
* @param msg
* Uma {@link String} com a mensagem para exibição
* @return Uma {@link String} com a entrada do usuário
*/
private static String lerConsole(String msg) throws Exception {
System.out.print(msg);
if (reader == null)
reader = new BufferedReader(new InputStreamReader(System.in));
String entrada = reader.readLine();
if (entrada == null)
entrada = "";
return entrada;
}
/**
* Efetua a criação de um arquivo de exemplo para teste de geração da assinatura digital
*/
private static void criarArquivoExemplo(File f) throws Exception {
StringBuffer sb = new StringBuffer();
sb.append("E00BE0104SC56001203764 01ECF-IF BEMATECH MP-25 FI 000000 000000000000000000000000000000000000000000\r\n");
sb.append("E01BE0104SC56001203764 ECF-IF BEMATECH MP-25 FI 010102 00105831026000103RFD001326001326200902112009021101.00.00PC5207 01.00.00\r\n");
sb.append("E02BE0104SC56001203764 MP-25 FI 05831026000103387155811110 ELISABETH STRAZZIERI DE CARVALHO - \"V E R O R I S T O R A N T E\" ROD. CASTELO BRANCO KM 72,5 S/N LJ 56/57 ITU-SP 00000000000000007093532601\r\n");
sb.append("E14BE0104SC56001203764 MP-25 FI 0100000101967120081222000000000019000000000000000V0000000000190V00000000002090N0000000000000A 00000553960890\r\n");
sb.append("E14BE0104SC56001203764 MP-25 FI 0100000201967220081222000000000019000000000000000V0000000000190V00000000002090N0000000000000A 00004032410810\r\n");
sb.append("E14BE0104SC56001203764 MP-25 FI 0100000301967320081222000000000089800000000000000V0000000000898V00000000009878N0000000000000A 64614449000122\r\n");
sb.append("E15BE0104SC56001203764 MP-25 FI 010196710000010010000000000156 #AGUA MIN 310 C/ GAS 0001000UN 00000220000000000000000000000000000220I1 N000000000000000000000000000000000T32\r\n");
sb.append("E15BE0104SC56001203764 MP-25 FI 010196710000010020000000000754 #VIRADO A PAULISTA 0001000UN 0000168000000000000000000000000000168001T0700N000000000000000000000000000000000T32\r\n");
sb.append("E15BE0104SC56001203764 MP-25 FI 010196720000020010000000000155 #AGUA MIN 310 ML S/G 0001000UN 00000220000000000000000000000000000220I1 N000000000000000000000000000000000T32\r\n");
sb.append("E21BE0104SC56001203764 MP-25 FI 01019671000001000000Dinheiro 0000000002090N0000000000000\r\n");
sb.append("E21BE0104SC56001203764 MP-25 FI 01019672000002000000Dinheiro 0000000002090N0000000000000\r\n");
if (f.exists())
f.delete();
FileOutputStream out = new FileOutputStream(f);
out.write(sb.toString().getBytes());
out.flush();
out.close();
}
}