Skip to content

Commit

Permalink
Merge branch 'release/3.8'
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel-Oliveira committed Oct 15, 2024
2 parents 5d79258 + 4c742c9 commit 7091315
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Notas de versão
- Atualizado CACERT
- Adicionado modo Multithreading
Binary file modified cacert
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ public class Certificado {
private String sslProtocol;
private BigInteger numeroSerie;
private Provider provider;
private boolean isModoMultithreading;

public Certificado() {
this.setSslProtocol(TLSV_1_2);
this.setModoMultithreading(false);
}

@Override
Expand Down
105 changes: 80 additions & 25 deletions src/main/java/br/com/swconsultoria/certificado/CertificadoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import br.com.swconsultoria.certificado.exception.CertificadoException;
import br.com.swconsultoria.certificado.util.DocumentoUtil;
import lombok.extern.java.Log;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.protocol.Protocol;

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

/**
*
* <p>Inicializa o certificado para a conexão SSL/TLS.</p>
* <p><b>Importante: </b>Quando NÃO estiver com o modo multithreading ativado (certificado.isModoMultithreading())
* será registrado e utilizado um único certificado em todas as conexões até a próxima chamada à
* {@link #inicializaCertificado(Certificado, InputStream)}. É o modo antigo e padrão da biblioteca.</p>
* <p>Quando estiver com o modo multithreading ativo o consumidor deverá obter um
* {@link org.apache.commons.httpclient.HttpClient} com protocolo e certificado exclusivos para ele usando o
* método {@link #getHttpsClient(Certificado, String, InputStream)}</p>
*
* @param certificado {@link Certificado} a ser utilizado na conexão.
* @param cacert {@link java.io.InputStream} contendo o cacert
* @throws CertificadoException
*/

public static void inicializaCertificado(Certificado certificado, InputStream cacert) throws CertificadoException {
if (certificado == null) {
throw new IllegalArgumentException(CERTIFICADO_NAO_PODE_SER_NULO);
}

try {

KeyStore keyStore = getKeyStore(
Optional.ofNullable(certificado).orElseThrow(() -> new IllegalArgumentException(CERTIFICADO_NAO_PODE_SER_NULO)));
SocketFactoryDinamico socketFactory = new SocketFactoryDinamico(keyStore, certificado.getNome(), certificado.getSenha(),
Optional.ofNullable(cacert).orElseThrow(() -> new IllegalArgumentException("Cacert não pode ser nulo.")),
certificado.getSslProtocol());
Protocol protocol = new Protocol("https", socketFactory, 443);
Protocol.registerProtocol("https", protocol);

log.info(String.format("JAVA-CERTIFICADO | Samuel Oliveira | [email protected] " +
"| VERSAO=%s | DATA_VERSAO=%s | CNPJ/CPF=%s | VENCIMENTO=%s | ALIAS=%s | TIPO=%s | CAMINHO=%s | CACERT=%s | SSL=%s",
"3.7",
"11/07/2024",
certificado.getCnpjCpf(),
certificado.getDataHoraVencimento(),
certificado.getNome().toUpperCase(),
certificado.getTipoCertificado().toString(),
certificado.getArquivo(),
cacertProprio ? "Default" : "Customizado",
certificado.getSslProtocol()));

} catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | CertificateException |
IOException e) {
throw new CertificadoException(e.getMessage(), e);
if (!certificado.isModoMultithreading()) {
Protocol.registerProtocol("https", getProtocoloCertificado(certificado, cacert));
}

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

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

private static Protocol getProtocoloCertificado(final Certificado certificado, InputStream cacert) throws CertificadoException {
try {
KeyStore keyStore = getKeyStore(
Optional.ofNullable(certificado).orElseThrow(() -> new IllegalArgumentException(CERTIFICADO_NAO_PODE_SER_NULO)));
SocketFactoryDinamico socketFactory = new SocketFactoryDinamico(keyStore, certificado.getNome(), certificado.getSenha(),
Optional.ofNullable(cacert).orElseThrow(() -> new IllegalArgumentException("Cacert não pode ser nulo.")),
certificado.getSslProtocol());

return new Protocol("https", socketFactory, 443);

} catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | CertificateException |
IOException e) {
throw new CertificadoException(e.getMessage(), e);
}
}

/**
* Utiliza cacert default da biblioteca.
* @see #getHttpsClient(Certificado, String, InputStream)
*/
public static HttpClient getHttpsClient(Certificado certificado, String url) throws CertificadoException {
return getHttpsClient(certificado, url, CertificadoService.class.getResourceAsStream("/cacert"));
}

/**
* <p>Utilizar o {@link org.apache.commons.httpclient.HttpClient} gerado nesse método para evitar conflitos de
* certificados nas conexões HTTPS/TLS/SSL, especialmente em ambientes multithreading.</p>
*
* <p>O consumidor desse método deverá usar esse cliente no stub desejado</p>
* exemplo:
* <pre>
* HttpClient client = getHttpsClient(certificado, url);
* NFeStatusServico4Stub stub = new NFeStatusServico4Stub(url);
* stub._getServiceClient().getOptions().setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpclient);
* </pre>
* @param certificado {@link Certificado} a ser utilizado na conexão.
* @param url {@link String} a ser executada na conexão https
* @param cacert {@link java.io.InputStream} contendo o cacert
* @return {@link org.apache.commons.httpclient.HttpClient} configurado para a URL e Certificados informados.
* @throws CertificadoException
*/
public static HttpClient getHttpsClient(Certificado certificado, String url, final InputStream cacert) throws CertificadoException {
Protocol protocol = getProtocoloCertificado(certificado, cacert);
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setHost(url, 443, protocol);
return httpclient;
}
}
Binary file modified src/main/resources/cacert
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
import br.com.swconsultoria.certificado.util.DocumentoUtil;
import mockit.Mock;
import mockit.MockUp;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
Expand All @@ -27,11 +30,11 @@

class CertificadoServiceTest {

private final String CERTIFICADO_CPF = "NaoUsar_CPF.pfx";
private final String CERTIFICADO_CNPJ = "NaoUsar_CNPJ.pfx";
private final String CPF = "99999999999";
private final String CNPJ = "99999999999999";
private final String SENHA = "123456";
private static final String CERTIFICADO_CPF = "NaoUsar_CPF.pfx";
private static final String CERTIFICADO_CNPJ = "NaoUsar_CNPJ.pfx";
private static final String CPF = "99999999999";
private static final String CNPJ = "99999999999999";
private static final String SENHA = "123456";

@Test
void certificadoPfxParametroNull() {
Expand Down Expand Up @@ -172,10 +175,7 @@ void inicaConfiguracoesCorretamente() {
}

@Test
void inicaConfiguracoesParametrosNull() throws IOException, CertificadoException {

InputStream cacert = CertificadoServiceTest.class.getResourceAsStream("cacert");
Certificado certificado = CertificadoService.certificadoPfx(CERTIFICADO_CNPJ, SENHA);
void inicaConfiguracoesParametrosNull() {

//Certificado Null
Assertions.assertThrows(IllegalArgumentException.class, () ->
Expand Down Expand Up @@ -207,4 +207,34 @@ void extraiCpfCnpjCorretamente() {

}

/**
* <p>Testa a compatibilidade com consumidores "antigos", que ainda não estão no "novo modelo" de controle do
* certificado nas conexões TLS/SSL. Isso permitirá uma "migração gradual" dos consumidores.</p>
* </p>Por padrão será utilizado o modo antigo, cada consumidor irá precisar explicitamente escolher o
* "modo multithreading", caso deseje.<p>
*/
@Test
void compatibilidadeModoMultithreadingDesativado() throws FileNotFoundException, CertificadoException {
Certificado certificado = CertificadoService.certificadoPfx(CERTIFICADO_CPF, SENHA);
certificado.setModoMultithreading(false);
CertificadoService.inicializaCertificado(certificado);

String alias = getHttpsProtocoloAlias("https");

assertEquals("certificado cpf teste", alias);
}

private String getHttpsProtocoloAlias(String protocolId) {
try {
Protocol registeredProtocol = Protocol.getProtocol(protocolId);
ProtocolSocketFactory factory = registeredProtocol.getSocketFactory();

Class<?> clazz = factory.getClass();
Field getAliasField = clazz.getDeclaredField("alias");
getAliasField.setAccessible(true);
return (String) getAliasField.get(factory);
} catch (Exception e) {
return null;
}
}
}

0 comments on commit 7091315

Please sign in to comment.