[RESOLVIDO] NFe-PR Instalação certificados - erro bad_certificate

Bom Dia,

Estou desenvolvendo um cliente web service para se comunicar com os serviços da receita para tratar de NFe.
Mas estou tendo um problema na configuração do ambiente para conseguir efetivar a conexão através de HTTPS.
Para atender o critério de comunicação segura, adicionei os certificados no meu keystore, usando o keytool:

keytool -import -file icpbrasilv2.cer -alias icpbrasilv2 -keystore homologacao_pr.jks
keytool -import -file acserprov3.cer -alias acserprov3 -keystore homologacao_pr.jks
keytool -import -file serproacfv3.cer -alias serproacfv3 -keystore homologacao_pr.jks
keytool -import -file homologacao_nfe2_fazenda_pr_gov_br_2012_13.cer -alias homologacao -keystore homologacao_pr.jks

Com isso, teoricamente já seria possível realizar a comunicação com os serviços.
Configurando o certificado (.pfx) que foi disponibilizado por meu cliente.

System.setProperty("javax.net.ssl.keyStoreType", "PKCS12"); System.setProperty("javax.net.ssl.keyStore", "C:\\caminho\\certificadoCliente.pfx"); System.setProperty("javax.net.ssl.keyStorePassword", "senha");
Configurando o trustStore para meu keystore que importei a cadeia de certificados.

System.setProperty("javax.net.ssl.trustStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStore", "C:\\caminho\\homologacao_pr.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "senhaDoKeyStore");

Ao executar o cliente do web service, recebo esse erro:

... 2 more Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174) at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1822)
Não sei o motivo por esse erro estar acontecendo, pois os certificados estão adicionados no keyStore.
Eu preciso fazer algo com o certificado (*.pfx) disponibilizado por meu cliente ?

Engraçado que usando a classe SSLPoke para testar a conexão segura, configurando o keyStore e o trustStore, a conexão é feita com sucesso, exibindo o log:

Successfully connected

=/

Estive fazendo alguns testes com a classe InstallCert (https://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/util/InstallCert.java) no ambiente de homologação dos estados do Paraná e São Paulo.

Em São Paulo, funcionou sem problemas. Instalando os certificados, conforme o log:

[code]
Loading KeyStore jssecacerts…
Opening connection to homologacao.nfe.fazenda.sp.gov.br:443…
Starting SSL handshake…

No errors, certificate is already trusted

Server sent 3 certificate(s):
…[/code]
Mas aqui no Paraná, executando o mesmo passo, não tem o mesmo comportamento, gerando o seguinte erro no console:

Loading KeyStore jssecacerts...
Opening connection to homologacao.nfe2.fazenda.pr.gov.br:443...
Starting SSL handshake...

javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1837)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1019)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1203)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1230)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1214)
	at br.com.devlogic.apinfe.util.InstallCert.main(InstallCert.java:60)

Server sent 4 certificate(s):

Me fazendo a pensar que existe algo de errado com os certificados disponibilizados pelo Paraná =/.

Pessoal que tem experiência com NFe aqui no PR, vocês tiveram esse problema ?

Podem ocorrer problemas na primeira conexão, pois o certificado da Webserver ainda não está no truststore.
Repare na sua mensagem, que houve erro no handshake, mas a sefaz retornou 4 certificados. Você deve armazená-los em seu truststore e então realizar a conexão com o webservice utilizando os stubs q vc gerou.

[quote=EduFrazao]Podem ocorrer problemas na primeira conexão, pois o certificado da Webserver ainda não está no truststore.
Repare na sua mensagem, que houve erro no handshake, mas a sefaz retornou 4 certificados. Você deve armazená-los em seu truststore e então realizar a conexão com o webservice utilizando os stubs q vc gerou.

[/quote]
Boa Tarde EduFrazao, justamente, eu baixei esses quatro certificados do site da receita do PR e adicionei no truststore que estou utilizando, conforme os comandos:

keytool -import -file icpbrasilv2.cer -alias icpbrasilv2 -keystore homologacao_pr.jks
keytool -import -file acserprov3.cer -alias acserprov3 -keystore homologacao_pr.jks
keytool -import -file serproacfv3.cer -alias serproacfv3 -keystore homologacao_pr.jks
keytool -import -file homologacao_nfe2_fazenda_pr_gov_br_2012_13.cer -alias homologacao -keystore homologacao_pr.jks

Em relação ao certificado disponibilizado por meu cliente, preciso fazer algo ? Alem de usa-lo no momento da comunicação, pelas linhas:

System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");  
System.setProperty("javax.net.ssl.keyStore", "C:\\caminho\\certificadoCliente.pfx");  
System.setProperty("javax.net.ssl.keyStorePassword", "senha");  

O Procedimento está correto.

Eu evito tratar desta forma, pois você precisa ficar mantendo os truststores, etc. Também acho ruim lidar com com system properties para parametrizar a conexão. Dependendo do ambiente isso é suficiente, mas você pode ter problemas de concorrência tendo operações simultâneas. Desenvolvi um comunicador (emissão, cancelamento, eventos), que lida com várias operações paralelas, inclusive, possívelmente com certificados de cliente diferentes, etc.

Para isto, resolvi deixar o sistema lidar com o trustsore, e criei uma classe para lidar com ele em memória, injetando o truststore diretamente no SSLSocketFactory utilizado pelo cliente Stub do Webservice para eliminar problemas de concorrência. Este manager insere automaticamente certificados ainda nao existentes. Abaixo segue o source.

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.output.ByteArrayOutputStream;


/**
 * <b>Gerenciador de TrustStore</b><br>
 * Esta classe utilitaria fornece acesso sincronizado e seguro ao repositorio de certificados
 * confiaveis.
 * <b>Projeto:</b>**<br>
 * <b>Criado em:</b> May 15, 2013 - 4:19:18 PM<br>
 * @author Eduardo B. Frazao
 */
public class TrustStoreManager {

	private Logger logger = Logger.getLogger(TrustStoreManager.class.getName());
	
	private static TrustStoreManager instance;
	
	private byte[] trustStoreInMemory;
	private boolean trustStoreInitialized = false;
	
	// Mutex para acesso ao singleton
	private static final Object singletonMutex = new Object();
	
	private TrustStoreManager() {
		super();
		try {
			loadTrustStoreFromFile();
			trustStoreInitialized = true;
		} catch (IOException e) {
			trustStoreInitialized = false;
		}
	}
	
	/**
	 * Acesso sincronizado ao Singleton
	 * @return
	 */
	public static TrustStoreManager instance() {
		synchronized (singletonMutex) {
			if(instance == null) {
				instance = new TrustStoreManager();
			}
			return instance;
		}
	}
	
	// Obtem acesso sincronizado ao truststore em memoria
	private byte[] getLocalTrustStore() {
		if(!trustStoreInitialized) {
			throw new IllegalStateException("O Truststore ainda nao foi inicializado");
		}
		return trustStoreInMemory;
	}
	
	private void loadTrustStoreFromFile() throws IOException {
		logger.log(Level.INFO, "Carregando truststore em memoria");
		
		char SEP = File.separatorChar;
		File fileTrustStore;
		InputStream trustStoreInputStream;
		
		// Tentando carregar certificado da JVM
		fileTrustStore = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security" + SEP + "cacerts");
        if(fileTrustStore.exists() && fileTrustStore.isFile() && fileTrustStore.canRead()) {
        	trustStoreInputStream = new FileInputStream(fileTrustStore);
        }
        // Se nao, sera utilizada uma truststore distribuida com a aplicacao
        else {
        	trustStoreInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/sunTrustStore/cacerts");
        }
        
        // Carregando em memoria
        trustStoreInMemory = new byte[trustStoreInputStream.available()];
        DataInputStream readStream = null;
        try {
        	readStream = new DataInputStream(trustStoreInputStream);
            readStream.readFully(trustStoreInMemory);
        } finally {
        	readStream.close();
        }
	}
	
	/**
	 * Obtem uma copia imutavel do truststore atual
	 * @return
	 */
	public byte[] processHostAndGetTrustStore(final String host, int timeout) throws TrustStoreException, ComunicacaoException {
		addChainOnTrustStore(host, timeout);
		synchronized (this) {
			return Arrays.copyOf(getLocalTrustStore(), getLocalTrustStore().length);
		}
	}
	
	private void addChainOnTrustStore(final String hostUrl, int timeout) throws TrustStoreException, ComunicacaoException {
		String[] url = hostUrl.split("/");
        String host = url[2];
        
        try {
        	KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            char[] passphrase = "changeit".toCharArray();
            synchronized (this) {
            	InputStream in = new ByteArrayInputStream(getLocalTrustStore());
                ks.load(in, passphrase);
                in.close();
			}
            if (!ks.containsAlias(host + "-1")) {
                addHostToTrustStore(ks, hostUrl, timeout);
            } else {
                Logger.getLogger(ConfiguracaoTrustKey.class.getName()) .log(Level.INFO, "Certificado do host '" + host + "' ja existe no Truststore. Nao foi necessario obte-lo novamente");
            }
        } catch (Exception e) {
			throw new ComunicacaoException("Impossivel ler ou adicionar host no truststore", e);
		}
		
	}
	
	private synchronized void addHostToTrustStore(KeyStore ks, final String hostUrl, int timeout) throws Exception {
		 	String[] url = hostUrl.split("/");
	        String host = url[2];

	        int port;
	        String[] c = host.split(":");
	        port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
	        
	        char[] passphrase = "changeit".toCharArray();
	        
	        SSLContext context = SSLContext.getInstance("TLS");
	        TrustManagerFactory tmf = TrustManagerFactory
	                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
	        tmf.init(ks);

	        X509TrustManager defaultTrustManager = (X509TrustManager) tmf
	                .getTrustManagers()[0];
	        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
	        context.init(null, new TrustManager[] { tm }, null);
	        SSLSocketFactory factory = context.getSocketFactory();
	        logInfo("Conectando em " + host + ":" + port + "...");
	        SSLSocket socket = (SSLSocket) factory.createSocket();
	        socket.setSoTimeout(timeout);
	        socket.setKeepAlive(false);
	        socket.setReuseAddress(true);
	        socket.setSoLinger(true, (timeout - 1000));
	        try {
	            socket.connect(new InetSocketAddress(host, port), timeout);
	        }
	        catch (IOException e) {
	        	try {
	        		socket.close();
	        	} catch (Exception e1) {}
	            throw new ComunicacaoException("Erro ao tentar se conectar Host para obtencao de certificado publico.", e);
	        }
	        // Utilizando outro bloco de tratamento, pois se e normal obter excessoes durante o handshake
	        // que podem ser ignoradas, diferente de uma excessao na conexao do socket
	        try {
	        	logInfo("Iniciando handshake SSL (Negociando obtencao de certificado publico...)");
	            socket.startHandshake();
	        } catch (Exception e) {} finally {socket.close();}

	        X509Certificate[] chain = tm.chain;
	        if (chain == null) {
	            logger.log(Level.SEVERE, ConfiguracaoTrustKey.class.getName(), "Nao foi possivel obter os certificados publicos no host: " + host + ". A conexao sera abortada.");
	            throw new TrustStoreException(
	                    "Nao foi possivel obter os certificados publicos no host: " + host + ". A conexao sera abortada.");
	        }

	        // Salvando cadeia de certificados no truststore.
	        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
	        MessageDigest md5 = MessageDigest.getInstance("MD5");
	        for (int i = 0; i < chain.length; i++) {
	            X509Certificate cert = chain[i];
	            sha1.update(cert.getEncoded());
	            md5.update(cert.getEncoded());
	        }

	        int k = 0;

	        X509Certificate cert = chain[k];
	        String alias = host + "-" + (k + 1);
	        ks.setCertificateEntry(alias, cert);

	        logInfo("Certificado do host '"+alias+"' adicionado no Truststore ");
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        ks.store(out, passphrase);
	        out.flush();
	        trustStoreInMemory = out.toByteArray();
	        out.close();
	}
	
	private static class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;

        private X509Certificate[] chain;

        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }

        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType) {
            throw new UnsupportedOperationException();
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) {
            this.chain = chain;
            try {
                tm.checkServerTrusted(chain, authType);
            } catch (CertificateException e) {
                e.printStackTrace();
            }
        }
    }
	
	private void logInfo(String info) {
		logger.log(Level.INFO, info);
	}
	
}

Interessante EduFrazao,

Também terei essa necessidade futuramente em meu sistema, que será usado por diferentes clientes, e existe o risco sobre concorrência conforme voce comentou. Fiquei curioso sobre como desenvolveu isso, ficaria feliz se pudesse compartilhar isso conosco, pois pelos posts que tenho lido aqui no forum, parece que todas as pessoas deixam fixo o caminho do keystore e truststore.

Você utiliza esse seu sistema aqui no PR ?

Fiz o uso dessa classe para gerar um keystore para teste, pelo que entendi, o retorno do método é um byte[] que representa o keystore criado dinamicamente.

	byte[] bytes = TrustStoreManager.instance().processHostAndGetTrustStore("https://homologacao.nfe2.fazenda.pr.gov.br", 10000);
	ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        OutputStream out = new FileOutputStream("novoKeyStoreGeradoDinamicamente.jks");
        IOUtils.copy(in, out);
        out.close();
        in.close();

Após a execução desse fonte, alterei meu cliente para usar o trustStore criado anteriomente novoKeyStoreGeradoDinamicamente.jks.
Infelizmente recebi a mesma mensagem de erro. Analisando o arquivo gerado pelo keytool, ele parece estar correto, contendo os 4 certificados necessários para o PR.

Received fatal alert: bad_certificate 

Fala Renato.

Então… É realmente comum receber um bad_certificate em algumas UF’s. Minas Gerais faz muito isso também.
No primeiro handshake o sistema alerta sobre a falha, pois o sistema ainda não confia nos certificados sem unidade certificadora reconhecida da sefaz. Por esta razão precisamos adicioná-las no truststore antes de efetuar a conexão para consumo final do serviço.

Este sistema já foi utilizado por clientes no paraná, e faz tempo que nao vejo reclamações. Tive que gerar stubs personalizados no Paraná devido a diferenças no WSDL e também quanto a algumas tratativas de namespace’s equivocadas pelo pessoal de lá.

Você pode postar a stack trace completa da tentativa de comunicação do stub?

Ah, esqueci de responder sua pergunta.

Quando desenvolvi este produto, a idéia inicial era de que ele fosse uma unidade de emissão para todos os clientes da empresa (na época, uns 350), gerenciando a emissão, controle, e backup das NF’s. Infelizmente, devido a mudanças no escopo do projeto, ele acabou trabalhando IN HOUSE, porém, segui a mesma orientação na codificação da biblioteca de comunicação. A idéia, é justamente evitar o uso de propriedades globais, por DIVERSAS razões, mas principalmente pela concorrência. Seria muito complexo sincronizar o acesso às system properties. Usar um mutex num objeto de acesso global seria pedir para sofrer deadlocks :).

Então, criei um mecanismo para lidar com os certificados 100% em memória. Dessa forma, eu manipulo o SecureProtocol utilizado pelos Stubs do Axis2 (na verdade pelo Apache Commons HTTP Client), e injeto o certificado do cliente e o truststore para cada conexão. Assim posso monitorar Sockets presos pela Sefaz, e destruir a conexão forçadamente depois de algum tempo. Tive que fazer isso em Minas.
Também sincronizo o acesso aos documentos pendentes de envio, possibilitando o envio multithread de nfes. Aliados com o envio via lote, posso emitir até uns 8 lotes simultaneamente com 50 nfes cada, ou se você não quiser lidar com várias NFes em um unico lote, pode emitir várias por vez em conexões paralelas. A performance fica bem agressiva. Paralelo a isso, permito que as outras operações rodem livres também (cancelamento, consultas etc).

Entendi a necessidade que teve, interessante e parabéns pela solução adotada. Futuramente, terei que implementar nesse sistema que estou desenvolvendo algo parecido.

Para realização do teste, gerei um novo keystore usando a classe que me disponibilizou:

byte[] bytes = TrustStoreManager.instance().processHostAndGetTrustStore("https://homologacao.nfe2.fazenda.pr.gov.br", 10000);
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
OutputStream out = new FileOutputStream("./target/jssecacerts-novo");
IOUtils.copy(in, out);
out.close();
n.close();

Rodei o cliente novamente usando esse truststore gerado e ativei o debug do ssl, devido ao tamanho do conteudo, anexei nesse post o arquivo.
O cliente foi gerado com CXF e o objetivo agora é conseguir efetivar a comunicação e a configuração do ambiente.
Achei um pouco estranho, no meio do log tem uma ou outra exceção.

Unparseable certificate extensions: 1
[1]: ObjectId: 2.5.29.32 Criticality=false
Unparseable CertificatePolicies extension due to
java.io.IOException: No data available in policyQualifiers

Não sei se isso pode ser relevante, mas saiu no log.

Outro detalhe que percebi, que todos os certificados da cadeia do PR parecem estar no log, pois os encontrei pelo nome, entre eles:

OU=Autoridade Certificadora Raiz Brasileira v2
CN=Autoridade Certificadora SERPRO v3
CN=Autoridade Certificadora do SERPRO Final v3
CN=homologacao.nfe2.fazenda.pr.gov.br

Pode ser um pouco de paranóia minha, mas instalar o certificado do servidor automaticamente no trustStore não é uma possível falha de segurança? Não te deixaria vulnerável a ataques “man in the middle”?

Depois de muitos testes com a classe SSLPoke, acabei sacando o problema que vinha enfrentando. O mau "bad_certificate"

public class SSLPoke {

	public static void main(String[] args) {
  //    	System.setProperty(&quot;javax.net.ssl.keyStoreType&quot;, &quot;PKCS12&quot;);
  //    	System.setProperty(&quot;javax.net.ssl.keyStore&quot;, &quot;caminhoCertificadoCliente&quot;);
   //    	System.setProperty(&quot;javax.net.ssl.keyStorePassword&quot;, &quot;senha&quot;);

	    	System.setProperty(&quot;javax.net.ssl.trustStoreType&quot;, &quot;JKS&quot;);
	    	System.setProperty(&quot;javax.net.ssl.trustStore&quot;, &quot;certificadosTrustStore&quot;);
	    	System.setProperty(&quot;javax.net.ssl.trustStorePassword&quot;, &quot;changeit&quot;);

	        String host = &quot;homologacao.nfe2.fazenda.pr.gov.br&quot;;
	        host = &quot;nfe2.fazenda.pr.gov.br&quot;;
	        int port = 443;
	         try {
	                 SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
	                 SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(host, port);
	                 sslsocket.startHandshake();
	                 InputStream in = sslsocket.getInputStream();
	                 OutputStream out = sslsocket.getOutputStream();
	                 // Write a test byte to get a reaction :)
	                 out.write(1);
	                 while (in.available() &gt; 0) {
	                         System.out.print(in.read());
	                 }
	                 System.out.println(&quot;Successfully connected&quot;);
	         } catch (Exception exception) {
	                 exception.printStackTrace();
	         }
 }
}

Percebi que comentando as linhas que adicionam o keyStore nas propriedades do sistema, acontece a exceção:

javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1822)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1004)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1571)

Descomentando, a conexão é realizada com sucesso.

Chegando a concluir que o cliente gerado está considerando apenas o trustStore configurado. Por esse motivo o certificado do cliente não é enviado ao servidor da receita, causando a exceção no momento do "aperto de mãos" entre o cliente e o servidor.

Novamente, comecei a googlar sobre como configurar o keystore e trustStore no proxy criado.
Acabei encontrando um post no StackOverflow (http://stackoverflow.com/questions/8403949/apache-cxf-client-issue-with-calling-ssl-service) que me clareou a ideia e levantou outra duvida, tenho que setar isso no proxy mesmo ? E a definição das propriedades, foram ignoradas ? Tanto faz agora =).
Onde quero destacar o comentário retirado do post:

You develop a simple test CXF client which does mutual HTTPS connection, there is a very nice sample project available from cxf-samples that does exactly what you need: http://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.4/distribution/src/main/release/samples/wsdl_first_https/

Através desse projeto exemplo, conseguir encontrar um fonte que configura o ketStore e o trustStore no proxy de seu cliente.

    public static void setupTLS(Object port)
            throws FileNotFoundException, IOException, GeneralSecurityException {

  	    HTTPConduit httpConduit = (HTTPConduit) ClientProxy.getClient(port).getConduit();

            TLSClientParameters tlsCP = new TLSClientParameters();
            String keyPassword = &quot;changeit&quot;;
            KeyStore keyStore = KeyStore.getInstance(&quot;PKCS12&quot;);
            String keyStoreLoc = &quot;caminhoCertificadoCliente.pfx&quot;;
            keyStore.load(new FileInputStream(keyStoreLoc), &quot;senhaCertificadoCliente&quot;.toCharArray());
            KeyManager[] myKeyManagers = getKeyManagers(keyStore, keyPassword);
            tlsCP.setKeyManagers(myKeyManagers);

            KeyStore trustStore = KeyStore.getInstance(&quot;JKS&quot;);
            String trustStoreLoc = &quot;caminhoDoTruststore&quot;;
            trustStore.load(new FileInputStream(trustStoreLoc), keyPassword);
            TrustManager[] myTrustStoreKeyManagers = getTrustManagers(trustStore);
            tlsCP.setTrustManagers(myTrustStoreKeyManagers);

            //The following is not recommended and would not be done in a prodcution environment,
            //this is just for illustrative purpose
            tlsCP.setDisableCNCheck(true);

            httpConduit.setTlsClientParameters(tlsCP);

        }

        private static TrustManager[] getTrustManagers(KeyStore trustStore)
            throws NoSuchAlgorithmException, KeyStoreException {
            String alg = KeyManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
            fac.init(trustStore);
            return fac.getTrustManagers();
        }

        private static KeyManager[] getKeyManagers(KeyStore keyStore, String keyPassword)
            throws GeneralSecurityException, IOException {
            String alg = KeyManagerFactory.getDefaultAlgorithm();
            char[] keyPass = keyPassword != null
                         ? keyPassword.toCharArray()
                         : null;
            KeyManagerFactory fac = KeyManagerFactory.getInstance(alg);
            fac.init(keyStore, keyPass);
            return fac.getKeyManagers();
        }

Com o fonte acima, após criar o proxy, utilizo o metodo setupTLS() para configurar essa questão dos certificados.

setupTLS(port);

Sobre a gestão do trustStore, para o funcionamento é indiferente se foi criado dinamicamente ou importado na mão via keytool.

Outro ponto que quero comentar, criei o cliente usando JAX-WS, devido a facilidade para passar os headers da mensagem SOAP e não ter que adicionar bibliotecas adicionais no projeto.
Encontrei a dica para isso aqui no GUJ mesmo, através do post http://www.guj.com.br/java/206765-webservices-soap-12-headerresolvido/2

Obrigado a todos que leram ou responderam o post.
Concerveja, terei mais duvidas sobre isso =D.

Abraço

1 curtida

Que bacana que resolveu Renato. Observando a forma que você configura o cliente, você consegue facilmente montar um acesso sincronizado multithread à seu keystore.
Veja se você consegue acessar o Socket SSL Gerado para monitorar o tempo de comunicação e principalmente, configurar o timeout e o So Linger. Tem um bug na JVM 1.7.09 onde ocorrem deadlocks no startup TLS. Repassaram o S.O Linger do Socket como um workaround, que para mim funcionou muito bem. Meu sistema roda como um Daemon de emissão de nfe, e está ha 4 semanas online, rodou milhares de operações e não observei mais deadlocks locando threads, ou sockets zumbis.

Eu percebia esses problemas na Sefaz de Minas Gerais. Acredito que eles tenham um Proxy HTTP antes do serviço final. Quando o serviço está instável, o proxy http retém a conexão enviando keep alives para o socket, que nunca morre. (mesmo que você desabilite o keep alive nativo);

O que você falou faz sentido. Acredito que seja possível um ataque deste tipo, mas acredito que ele seja bem complexo de ser forjado, pois haveria a necessidade de hackear dns das telcos (não que já nao tenha sido feito antes), etc.

Acredito que pelo benefício da gestão automática em emissores que rodam em todas as UF’s valha a pena.