SDK C#
Biblioteca cliente C#/.NET (pacote NuGet HubSaude.Cliente).
Instalação
O nupkg (e o SBOM CycloneDX) saem da GitHub Release da tag; a publicação no nuget.org ainda não faz parte do repositório. Baixe o .nupkg da release e registre-o como fonte local antes de referenciar o pacote.
dotnet nuget add source ./caminho/para/pasta-com-o-nupkg --name hubsaude-local
dotnet add package HubSaude.Cliente --version 0.4.0 --source hubsaude-local
Isso adiciona ao seu projeto:
<PackageReference Include="HubSaude.Cliente" Version="0.4.0" />
Uso básico
SmartTokenClient.CreateBuilder() é a única forma de construção, já que a classe não expõe construtores públicos. Implementa IDisposable/IAsyncDisposable.
Informe a base do servidor: a biblioteca consulta /.well-known/smart-configuration e resolve o endpoint de token. Não combine .FhirBase com .TokenEndpoint.
await using var client = SmartTokenClient.CreateBuilder()
.FhirBase("https://hub.saude.go.gov.br")
.ClientId("meu-sistema")
.PrivateKeyPem("chave-privada.pem")
.CertificatePem("certificado.pem")
.Build();
string token = await client.ObtainTokenAsync("system/Patient.rs");
Endpoint de token explícito
Se preferir fixar o endereço de token, use .TokenEndpoint no lugar de .FhirBase:
await using var client = SmartTokenClient.CreateBuilder()
.TokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.ClientId("meu-sistema")
.PrivateKeyPem("chave-privada.pem")
.CertificatePem("certificado.pem")
.Build();
string token = await client.ObtainTokenAsync("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
await using var client = SmartTokenClient.CreateBuilder()
.FhirBase("https://localhost:8443")
.ClientId("meu-sistema")
.PrivateKeyPem("client-key.pem")
.CertificatePem("client-cert.pem")
.ServerTrustAnchor("simulador-server.pem")
.Build();
string token = await client.ObtainTokenAsync("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 ISigningStrategy (um único método que assina bytes), e SigningStrategyFactory já traz pronta a construção para as fontes mais comuns.
PEM com senha
var senha = "segredo".ToCharArray();
await using var client = SmartTokenClient.CreateBuilder()
.TokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.ClientId("meu-sistema")
.PrivateKeyPem("chave-privada.pem")
.PrivateKeyPassword(senha)
.Build();
PKCS#12 direto (recomendado para produção)
Um único arquivo (.pfx/.p12) já reúne a chave e o certificado, protegido por senha.
await using var client = SmartTokenClient.CreateBuilder()
.TokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.ClientId("meu-sistema")
.ClientPkcs12("certificado.pfx", "alias-da-chave", "senha-pfx".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.
await using var client = SmartTokenClient.CreateBuilder()
.TokenEndpoint("https://hub.saude.go.gov.br/auth/token")
.ClientId("meu-sistema")
.SigningStrategy(SigningStrategyFactory.FromPkcs11(new Pkcs11Options
{
Library = "/usr/lib/softhsm/libsofthsm2.so",
Pin = pinDoToken,
TokenLabel = "hubsaude",
KeyLabel = "jwt-key",
JwtAlgorithm = "RS384",
}))
.ClientPkcs12("cliente-mtls.pfx", "alias-do-cert", senhaPfx)
.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) |
.ClientPkcs12(path, alias, senha) | não | — | mTLS via bundle PKCS#12 |
.ServerTrustAnchor(path) | não | trust store do sistema | PEM ou certificado |
.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(TimeSpan) | não | 10s | Conexão TCP |
.RequestTimeout(TimeSpan) | 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 |