Skip to content

Commit 7091315

Browse files
Merge branch 'release/3.8'
2 parents 5d79258 + 4c742c9 commit 7091315

File tree

6 files changed

+122
-34
lines changed

6 files changed

+122
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# Notas de versão
22
- Atualizado CACERT
3+
- Adicionado modo Multithreading

cacert

3.55 KB
Binary file not shown.

src/main/java/br/com/swconsultoria/certificado/Certificado.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ public class Certificado {
3232
private String sslProtocol;
3333
private BigInteger numeroSerie;
3434
private Provider provider;
35+
private boolean isModoMultithreading;
3536

3637
public Certificado() {
3738
this.setSslProtocol(TLSV_1_2);
39+
this.setModoMultithreading(false);
3840
}
3941

4042
@Override

src/main/java/br/com/swconsultoria/certificado/CertificadoService.java

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import br.com.swconsultoria.certificado.exception.CertificadoException;
44
import br.com.swconsultoria.certificado.util.DocumentoUtil;
55
import lombok.extern.java.Log;
6+
import org.apache.commons.httpclient.HttpClient;
67
import org.apache.commons.httpclient.protocol.Protocol;
78

89
import java.io.FileNotFoundException;
@@ -35,35 +36,42 @@ public static void inicializaCertificado(Certificado certificado) throws Certifi
3536
inicializaCertificado(certificado, CertificadoService.class.getResourceAsStream("/cacert"));
3637
}
3738

39+
/**
40+
*
41+
* <p>Inicializa o certificado para a conexão SSL/TLS.</p>
42+
* <p><b>Importante: </b>Quando NÃO estiver com o modo multithreading ativado (certificado.isModoMultithreading())
43+
* será registrado e utilizado um único certificado em todas as conexões até a próxima chamada à
44+
* {@link #inicializaCertificado(Certificado, InputStream)}. É o modo antigo e padrão da biblioteca.</p>
45+
* <p>Quando estiver com o modo multithreading ativo o consumidor deverá obter um
46+
* {@link org.apache.commons.httpclient.HttpClient} com protocolo e certificado exclusivos para ele usando o
47+
* método {@link #getHttpsClient(Certificado, String, InputStream)}</p>
48+
*
49+
* @param certificado {@link Certificado} a ser utilizado na conexão.
50+
* @param cacert {@link java.io.InputStream} contendo o cacert
51+
* @throws CertificadoException
52+
*/
53+
3854
public static void inicializaCertificado(Certificado certificado, InputStream cacert) throws CertificadoException {
55+
if (certificado == null) {
56+
throw new IllegalArgumentException(CERTIFICADO_NAO_PODE_SER_NULO);
57+
}
3958

40-
try {
41-
42-
KeyStore keyStore = getKeyStore(
43-
Optional.ofNullable(certificado).orElseThrow(() -> new IllegalArgumentException(CERTIFICADO_NAO_PODE_SER_NULO)));
44-
SocketFactoryDinamico socketFactory = new SocketFactoryDinamico(keyStore, certificado.getNome(), certificado.getSenha(),
45-
Optional.ofNullable(cacert).orElseThrow(() -> new IllegalArgumentException("Cacert não pode ser nulo.")),
46-
certificado.getSslProtocol());
47-
Protocol protocol = new Protocol("https", socketFactory, 443);
48-
Protocol.registerProtocol("https", protocol);
49-
50-
log.info(String.format("JAVA-CERTIFICADO | Samuel Oliveira | [email protected] " +
51-
"| VERSAO=%s | DATA_VERSAO=%s | CNPJ/CPF=%s | VENCIMENTO=%s | ALIAS=%s | TIPO=%s | CAMINHO=%s | CACERT=%s | SSL=%s",
52-
"3.7",
53-
"11/07/2024",
54-
certificado.getCnpjCpf(),
55-
certificado.getDataHoraVencimento(),
56-
certificado.getNome().toUpperCase(),
57-
certificado.getTipoCertificado().toString(),
58-
certificado.getArquivo(),
59-
cacertProprio ? "Default" : "Customizado",
60-
certificado.getSslProtocol()));
61-
62-
} catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | CertificateException |
63-
IOException e) {
64-
throw new CertificadoException(e.getMessage(), e);
59+
if (!certificado.isModoMultithreading()) {
60+
Protocol.registerProtocol("https", getProtocoloCertificado(certificado, cacert));
6561
}
6662

63+
log.info(String.format("JAVA-CERTIFICADO | Samuel Oliveira | [email protected] " +
64+
"| VERSAO=%s | DATA_VERSAO=%s | CNPJ/CPF=%s | VENCIMENTO=%s | ALIAS=%s | TIPO=%s | CAMINHO=%s | CACERT=%s | SSL=%s | Multithreading=%s",
65+
"3.8",
66+
"14/10/2024",
67+
certificado.getCnpjCpf(),
68+
certificado.getDataHoraVencimento(),
69+
certificado.getNome().toUpperCase(),
70+
certificado.getTipoCertificado().toString(),
71+
certificado.getArquivo(),
72+
cacertProprio ? "Default" : "Customizado",
73+
certificado.getSslProtocol(),
74+
certificado.isModoMultithreading()));
6775
}
6876

6977
public static Certificado certificadoPfxBytes(byte[] certificadoBytes, String senha) throws CertificadoException {
@@ -278,4 +286,51 @@ public static Certificado getCertificadoByCnpjCpf(String cnpjCpf) throws Certifi
278286
cnpjCpf));
279287
}
280288

289+
private static Protocol getProtocoloCertificado(final Certificado certificado, InputStream cacert) throws CertificadoException {
290+
try {
291+
KeyStore keyStore = getKeyStore(
292+
Optional.ofNullable(certificado).orElseThrow(() -> new IllegalArgumentException(CERTIFICADO_NAO_PODE_SER_NULO)));
293+
SocketFactoryDinamico socketFactory = new SocketFactoryDinamico(keyStore, certificado.getNome(), certificado.getSenha(),
294+
Optional.ofNullable(cacert).orElseThrow(() -> new IllegalArgumentException("Cacert não pode ser nulo.")),
295+
certificado.getSslProtocol());
296+
297+
return new Protocol("https", socketFactory, 443);
298+
299+
} catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | CertificateException |
300+
IOException e) {
301+
throw new CertificadoException(e.getMessage(), e);
302+
}
303+
}
304+
305+
/**
306+
* Utiliza cacert default da biblioteca.
307+
* @see #getHttpsClient(Certificado, String, InputStream)
308+
*/
309+
public static HttpClient getHttpsClient(Certificado certificado, String url) throws CertificadoException {
310+
return getHttpsClient(certificado, url, CertificadoService.class.getResourceAsStream("/cacert"));
311+
}
312+
313+
/**
314+
* <p>Utilizar o {@link org.apache.commons.httpclient.HttpClient} gerado nesse método para evitar conflitos de
315+
* certificados nas conexões HTTPS/TLS/SSL, especialmente em ambientes multithreading.</p>
316+
*
317+
* <p>O consumidor desse método deverá usar esse cliente no stub desejado</p>
318+
* exemplo:
319+
* <pre>
320+
* HttpClient client = getHttpsClient(certificado, url);
321+
* NFeStatusServico4Stub stub = new NFeStatusServico4Stub(url);
322+
* stub._getServiceClient().getOptions().setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpclient);
323+
* </pre>
324+
* @param certificado {@link Certificado} a ser utilizado na conexão.
325+
* @param url {@link String} a ser executada na conexão https
326+
* @param cacert {@link java.io.InputStream} contendo o cacert
327+
* @return {@link org.apache.commons.httpclient.HttpClient} configurado para a URL e Certificados informados.
328+
* @throws CertificadoException
329+
*/
330+
public static HttpClient getHttpsClient(Certificado certificado, String url, final InputStream cacert) throws CertificadoException {
331+
Protocol protocol = getProtocoloCertificado(certificado, cacert);
332+
HttpClient httpclient = new HttpClient();
333+
httpclient.getHostConfiguration().setHost(url, 443, protocol);
334+
return httpclient;
335+
}
281336
}

src/main/resources/cacert

3.55 KB
Binary file not shown.

src/test/java/br/com/swconsultoria/certificado/CertificadoServiceTest.java

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import br.com.swconsultoria.certificado.util.DocumentoUtil;
55
import mockit.Mock;
66
import mockit.MockUp;
7+
import org.apache.commons.httpclient.protocol.Protocol;
8+
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
79
import org.junit.jupiter.api.Assertions;
810
import org.junit.jupiter.api.Test;
911

1012
import java.io.FileNotFoundException;
1113
import java.io.IOException;
1214
import java.io.InputStream;
15+
import java.lang.reflect.Field;
1316
import java.math.BigInteger;
1417
import java.nio.file.Files;
1518
import java.nio.file.Paths;
@@ -27,11 +30,11 @@
2730

2831
class CertificadoServiceTest {
2932

30-
private final String CERTIFICADO_CPF = "NaoUsar_CPF.pfx";
31-
private final String CERTIFICADO_CNPJ = "NaoUsar_CNPJ.pfx";
32-
private final String CPF = "99999999999";
33-
private final String CNPJ = "99999999999999";
34-
private final String SENHA = "123456";
33+
private static final String CERTIFICADO_CPF = "NaoUsar_CPF.pfx";
34+
private static final String CERTIFICADO_CNPJ = "NaoUsar_CNPJ.pfx";
35+
private static final String CPF = "99999999999";
36+
private static final String CNPJ = "99999999999999";
37+
private static final String SENHA = "123456";
3538

3639
@Test
3740
void certificadoPfxParametroNull() {
@@ -172,10 +175,7 @@ void inicaConfiguracoesCorretamente() {
172175
}
173176

174177
@Test
175-
void inicaConfiguracoesParametrosNull() throws IOException, CertificadoException {
176-
177-
InputStream cacert = CertificadoServiceTest.class.getResourceAsStream("cacert");
178-
Certificado certificado = CertificadoService.certificadoPfx(CERTIFICADO_CNPJ, SENHA);
178+
void inicaConfiguracoesParametrosNull() {
179179

180180
//Certificado Null
181181
Assertions.assertThrows(IllegalArgumentException.class, () ->
@@ -207,4 +207,34 @@ void extraiCpfCnpjCorretamente() {
207207

208208
}
209209

210+
/**
211+
* <p>Testa a compatibilidade com consumidores "antigos", que ainda não estão no "novo modelo" de controle do
212+
* certificado nas conexões TLS/SSL. Isso permitirá uma "migração gradual" dos consumidores.</p>
213+
* </p>Por padrão será utilizado o modo antigo, cada consumidor irá precisar explicitamente escolher o
214+
* "modo multithreading", caso deseje.<p>
215+
*/
216+
@Test
217+
void compatibilidadeModoMultithreadingDesativado() throws FileNotFoundException, CertificadoException {
218+
Certificado certificado = CertificadoService.certificadoPfx(CERTIFICADO_CPF, SENHA);
219+
certificado.setModoMultithreading(false);
220+
CertificadoService.inicializaCertificado(certificado);
221+
222+
String alias = getHttpsProtocoloAlias("https");
223+
224+
assertEquals("certificado cpf teste", alias);
225+
}
226+
227+
private String getHttpsProtocoloAlias(String protocolId) {
228+
try {
229+
Protocol registeredProtocol = Protocol.getProtocol(protocolId);
230+
ProtocolSocketFactory factory = registeredProtocol.getSocketFactory();
231+
232+
Class<?> clazz = factory.getClass();
233+
Field getAliasField = clazz.getDeclaredField("alias");
234+
getAliasField.setAccessible(true);
235+
return (String) getAliasField.get(factory);
236+
} catch (Exception e) {
237+
return null;
238+
}
239+
}
210240
}

0 commit comments

Comments
 (0)