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á:
- o Simulador em execução, iniciado com
hubsaude sim start; - um certificado de teste (
client-cert.pem) e a respectiva chave (client-key.pem), gerados com o OpenSSL; - o sistema cadastrado no Simulador com o identificador
meu-sistemae a permissãosystem/Patient.rs; - o certificado do próprio Simulador (
simulador-server.pem), baixado do endereçohttps://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:
.fhirBaseaponta 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;
.serverTrustAnchorfaz 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_identregue 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ção | Causa provável | O que fazer |
|---|---|---|
| Erro de certificado ao conectar, como falha na verificação da cadeia | O certificado do Simulador não foi informado | Baixe o simulador-server.pem e configure a âncora de confiança |
401 invalid_client informando que o certificado não corresponde ao cadastrado | O certificado usado é diferente do enviado no cadastro | Use o mesmo par de chave e certificado do cadastro |
401 invalid_client informando que o certificado de cliente é obrigatório | O certificado de cliente não foi configurado | Informe o certificado de teste na configuração da biblioteca |
| Erro de permissão ao pedir o token | A permissão solicitada não foi liberada no cadastro | Cadastre o sistema novamente incluindo a permissão desejada |
| O sistema deixa de ser reconhecido | O Simulador foi reiniciado e apagou os cadastros | Cadastre 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 builder | Obrigatório | Padrão | Observaçõ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ão | trust store da plataforma | PEM ou objeto X.509 |
.tlsProtocol(protocolo) | não | TLSv1.3 | Ex.: TLSv1.2 |
.jwtAlgorithm(alg) | não | RS384 | HubSaú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ão | 10s | Conexão TCP |
.requestTimeout(Duration) | não | 30s | Requisição HTTP completa |
.assertionTtlSeconds(n) | não | 60 | Deveria ser ≤300 |
.maxRetries(n) | não | 3 | Tentativas totais |
.enableTokenCache(bool) | não | true | Ativa ou desativa o cache de tokens |
.tokenCacheMarginSeconds(n) | não | 30 | Margem de renovação antes da expiração |
.tokenCacheMaxEntries(n) | não | 1000 | Descarte 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
| Tipo | Uso |
|---|---|
SmartTokenException | Configuração criptográfica inválida, resposta HTTP/JSON inválida ou algoritmo não suportado |
SigningException | Falha da estratégia criptográfica ao assinar o client_assertion |