Pular para o conteúdo principal

SDK Java

Biblioteca cliente Java (hubsaude-cliente-java), a implementação de referência do contrato normativo compartilhado pelos 4 SDKs.

Instalação​

A release 0.4.0 está disponível, somente leitura, no GitHub Packages legado. Novas versões serão promovidas pelo repositório atual, até a publicação no Maven Central. O GitHub Packages exige repositório e credenciais configurados mesmo para leitura pública — a dependência sozinha não resolve.

<dependency>
<groupId>br.gov.go.saude.hubsaude</groupId>
<artifactId>hubsaude-cliente-java</artifactId>
<version>0.4.0</version>
</dependency>

Adicione o repositório no pom.xml:

<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/sesgo-ti/hubsaude-cliente-java</url>
</repository>
</repositories>

E as credenciais no ~/.m2/settings.xml (um token pessoal com escopo read:packages):

<servers>
<server>
<id>github</id>
<username>SEU_USUARIO_GITHUB</username>
<password>SEU_TOKEN_COM_READ_PACKAGES</password>
</server>
</servers>

Uso básico​

SmartTokenClient não expõe construtores públicos, então builder() é a única entrada. A instância é thread-safe; reutilize-a pelo ciclo de vida da aplicação e invoque close() uma única vez no encerramento.

Informe a base do servidor: a biblioteca consulta /.well-known/smart-configuration e resolve o endpoint de token. Não combine .fhirBase com .tokenEndpoint.

var client = SmartTokenClient.builder()
.fhirBase("https://hub.saude.go.gov.br")
.clientId("meu-sistema")
.privateKeyPem(Path.of("chave-privada.pem"))
.certificatePem(Path.of("certificado.pem"))
.build();

String token = client.obtainToken("system/Patient.rs");

Endpoint de token explícito​

Se preferir fixar o endereço de token, use .tokenEndpoint no lugar de .fhirBase:

var client = SmartTokenClient.builder()
.tokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.clientId("meu-sistema")
.privateKeyPem(Path.of("chave-privada.pem"))
.certificatePem(Path.of("certificado.pem"))
.build();

String token = client.obtainToken("system/Patient.rs");

Testar com o Simulador​

O Simulador reproduz o HubSaúde no seu computador e permite testar a biblioteca antes de receber a credencial oficial. Ele exige os mesmos cuidados de segurança da produção, então a configuração é praticamente a mesma: muda apenas o endereço, a origem do certificado e a confiança no servidor.

Antes de executar o código​

Siga os passos de Preparar seu sistema para o Simulador. Ao final deles, você terá:

  1. o Simulador em execução, iniciado com hubsaude sim start;
  2. um certificado de teste (client-cert.pem) e a respectiva chave (client-key.pem), gerados com o OpenSSL;
  3. o sistema cadastrado no Simulador com o identificador meu-sistema e a permissão system/Patient.rs;
  4. o certificado do próprio Simulador (simulador-server.pem), baixado do endereço https://localhost:8443/certs/simulador-server.pem.

Configurar a biblioteca​

var client = SmartTokenClient.builder()
.fhirBase("https://localhost:8443")
.clientId("meu-sistema")
.privateKeyPem(Path.of("client-key.pem"))
.certificatePem(Path.of("client-cert.pem"))
.serverTrustAnchor(Path.of("simulador-server.pem"))
.build();

String token = client.obtainToken("system/Patient.rs");

Em relação ao uso com a produção, há três diferenças:

  • .fhirBase aponta para o Simulador, e a biblioteca descobre sozinha os demais endereços;
  • a chave e o certificado são os de teste, cadastrados no Simulador, e o identificador é o informado no cadastro;
  • .serverTrustAnchor faz a biblioteca confiar no certificado do Simulador, que não é emitido por uma autoridade reconhecida.

Se você gerou o certificado de teste com o Java keytool, a chave fica dentro do arquivo client-keystore.p12. Nesse caso, use o PKCS#12, descrito em Fontes de chave suportadas.

Passar para a produção​

Depois que o credenciamento for aprovado, ajuste a mesma configuração:

  • troque o endereço do Simulador por https://hub.saude.go.gov.br;
  • use o client_id entregue no credenciamento;
  • substitua a chave e o certificado de teste pelos da instituição;
  • remova a linha da âncora de confiança: o certificado do HubSaúde já é reconhecido automaticamente.

Problemas comuns no Simulador​

SituaçãoCausa provávelO que fazer
Erro de certificado ao conectar, como falha na verificação da cadeiaO certificado do Simulador não foi informadoBaixe o simulador-server.pem e configure a âncora de confiança
401 invalid_client informando que o certificado não corresponde ao cadastradoO certificado usado é diferente do enviado no cadastroUse o mesmo par de chave e certificado do cadastro
401 invalid_client informando que o certificado de cliente é obrigatórioO certificado de cliente não foi configuradoInforme o certificado de teste na configuração da biblioteca
Erro de permissão ao pedir o tokenA permissão solicitada não foi liberada no cadastroCadastre o sistema novamente incluindo a permissão desejada
O sistema deixa de ser reconhecidoO Simulador foi reiniciado e apagou os cadastrosCadastre o sistema novamente

Fontes de chave suportadas​

Sua chave privada pode estar em diferentes lugares: um arquivo, um cofre de segredos ou um dispositivo de hardware. O SDK abstrai isso com a interface funcional SigningStrategy (um único método, sign(bytes) -> bytes), e SigningStrategyFactory já traz pronta a construção para as fontes mais comuns.

PEM com senha​

var client = SmartTokenClient.builder()
.tokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.clientId("meu-sistema")
.privateKeyPem(Path.of("chave-encrypted.pem"))
.privateKeyPassword("minha-senha".toCharArray())
.build();

PKCS#12 direto (recomendado para produção)​

Um único arquivo (.pfx/.p12) já reúne a chave e o certificado, protegido por senha.

KeyStore ks = KeyStore.getInstance("PKCS12");
try (var fis = new FileInputStream("certificado.pfx")) {
ks.load(fis, "senha-pfx".toCharArray());
}

SigningStrategy strategy = SigningStrategyFactory.fromKeyStore(
ks, "alias-da-chave", "senha-chave".toCharArray());

var client = SmartTokenClient.builder()
.tokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.clientId("meu-sistema")
.signingStrategy(strategy)
.clientKeyStore(ks, "alias-da-chave", "senha-chave".toCharArray())
.build();

HSM via PKCS#11​

Um HSM (Hardware Security Module) é um dispositivo dedicado que guarda a chave e nunca a expõe: toda assinatura acontece dentro dele, acessado aqui pelo padrão PKCS#11.

Provider pkcs11 = SigningStrategyFactory.configurePkcs11Provider("/etc/pkcs11/hsm.cfg");
SigningStrategy strategy = SigningStrategyFactory.fromPkcs11(
pkcs11, "alias-chave-hsm", "123456".toCharArray());

var client = SmartTokenClient.builder()
.tokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.clientId("meu-sistema")
.signingStrategy(strategy)
.build();

Configuração completa​

Método do builderObrigatórioPadrãoObservações
.tokenEndpoint(url)sim¹—URL completa do endpoint de token
.fhirBase(url)sim¹—Base FHIR para descoberta automática
.clientId(id)sim—Emitido pelo Ganesha
.privateKeyPem(path)sim²—Caminho do PEM da chave privada
.privateKeyPassword(char[])não—Senha do PEM criptografado
.signingStrategy(strategy)sim²—Estratégia pronta (HSM, cofre etc.)
.certificatePem(path)não³—Certificado do cliente (PEM)
.clientKeyStore(ks, alias, senha)não—mTLS via KeyStore (PKCS#11/12, JKS)
.serverTrustAnchor(path)nãotrust store da plataformaPEM ou objeto X.509
.tlsProtocol(protocolo)nãoTLSv1.3Ex.: TLSv1.2
.jwtAlgorithm(alg)nãoRS384HubSaúde aceita RS384/ES384
.keyId(kid)não—Inclui kid no header do JWT
.hubContext(ig, versao)não—Inclui o claim hub_ctx
.connectTimeout(Duration)não10sConexão TCP
.requestTimeout(Duration)não30sRequisição HTTP completa
.assertionTtlSeconds(n)não60Deveria ser ≤300
.maxRetries(n)não3Tentativas totais
.enableTokenCache(bool)nãotrueAtiva ou desativa o cache de tokens
.tokenCacheMarginSeconds(n)não30Margem de renovação antes da expiração
.tokenCacheMaxEntries(n)não1000Descarte LRU por scope

¹ Exatamente um entre tokenEndpoint e fhirBase. ² Exatamente um entre privateKeyPem e signingStrategy. ³ Obrigatório apenas para mTLS via chave em memória.

Exceções​

TipoUso
SmartTokenExceptionConfiguração criptográfica inválida, resposta HTTP/JSON inválida ou algoritmo não suportado
SigningExceptionFalha da estratégia criptográfica ao assinar o client_assertion

Repositório e especificação​