Referência rápida do CloudHub
O essencial do CloudHub em uma página, com links para os artigos completos.
Para que serve
O CloudHub conecta a sua aplicação aos provedores de certificado em nuvem da ICP-Brasil (os Prestadores de Serviço de Confiança, ou PSCs, como BirdID, SafeID e VIDaaS) por uma única API REST e uma única chave de API. Ele descobre quais provedores têm um certificado para um CPF ou CNPJ, intermedeia a autorização do usuário no provedor escolhido e, depois, lê o certificado e assina hashes com ele. O CloudHub não monta documentos assinados nem valida certificados: uma biblioteca de assinatura (PKI SDK, Rest PKI, Rest PKI Core ou PKI Express) calcula o hash a assinar, monta o PDF, CMS ou XML assinado e valida o certificado. O CloudHub recebe só o hash, e a chave privada nunca sai do provedor.
| Alternativa | Quando é a melhor escolha |
|---|---|
PKI SDK com o BrazilTrustServices | Seu backend é .NET e você mesmo vai cadastrar a sua aplicação em cada provedor: o pacote fala diretamente com os provedores, com um ClientID e um ClientSecret por provedor. |
| Rest PKI Core | Você quer uma página de assinatura hospedada: uma sessão de assinatura do Rest PKI Core permite assinar com os certificados do computador do usuário, pelo Web PKI, ou com certificados em nuvem, e pode aceitar só certificados em nuvem. |
| Web PKI | Os certificados estão no computador, no token ou no smart card do usuário, e não em um provedor de nuvem. |
Instalação
| Linguagem | Pacote | Versão principal atual | Registro |
|---|---|---|---|
| C# / .NET | Lacuna.Cloudhub.Client | 1 (última 1.0.0) | https://www.nuget.org/packages/Lacuna.Cloudhub.Client |
| C# / .NET, injeção de dependência do ASP.NET Core (opcional) | Lacuna.Cloudhub.ClientCore | 1 (última 1.1.0) | https://www.nuget.org/packages/Lacuna.Cloudhub.ClientCore |
| PHP | lacuna/cloudhub-client | 2 (última 2.0.2) | https://packagist.org/packages/lacuna/cloudhub-client |
| Node.js | cloudhub-client | 1 (última 1.0.1) | https://www.npmjs.com/package/cloudhub-client |
| Java | com.lacunasoftware.cloudhub:cloudhub-client | 1 (última 1.0.5) | https://central.sonatype.com/artifact/com.lacunasoftware.cloudhub/cloudhub-client |
| Python | nenhum: chame a API REST com requests | n/a | n/a |
dotnet add package Lacuna.Cloudhub.Client
O cliente PHP exige PHP 7.4 ou posterior e usa o Guzzle; os métodos dele terminam em Async, mas são síncronos. A API tem oito operações e um header, então qualquer cliente HTTP serve tão bem quanto uma biblioteca cliente. Veja Primeiros passos.
Configuração e autenticação
Toda chamada precisa da sua chave de API no header X-Api-Key; o endereço é https://cloudhub.lacunasoftware.com. Uma requisição sem uma chave válida recebe HTTP 401 com corpo vazio.
using Lacuna.Cloudhub.Api;
using Lacuna.Cloudhub.Client;
var cloudhub = new CloudhubClient(
"https://cloudhub.lacunasoftware.com/",
Environment.GetEnvironmentVariable("CLOUDHUB_API_KEY"));
- Peça a chave ao suporte da Lacuna Software (suporte@lacunasoftware.com), listando as URIs de redirecionamento que a sua aplicação vai usar em produção e em desenvolvimento.
- Mantenha a chave no seu servidor. Ela autoriza todas as chamadas, inclusive a assinatura, então nunca pode chegar a um navegador ou a um aplicativo móvel; se ela vazar, peça uma nova ao suporte.
- As URIs de redirecionamento precisam estar cadastradas para a sua chave: uma URI exata (com a query string), um prefixo de esquema, host e porta como
https://app.example.comou um curinga de subdomínio comohttps://*.example.com. Se você acrescenta seus próprios parâmetros, cadastre uma entrada de host ou de curinga. Veja URIs de redirecionamento. - Com o cliente Java, deixe o endereço sem a barra final (o cliente acrescenta
/api/...); o cliente PHP precisa dela. Não compartilhe umCloudhubClientJava entre threads que o chamem ao mesmo tempo.
Operações principais
Uma assinatura leva três chamadas: criar uma sessão, ler o certificado quando o usuário voltar e assinar o hash que a sua biblioteca de assinatura calculou. A página API REST documenta todas as operações; Primeiros passos traz cada etapa em C#, PHP, Python, Node.js e Java.
Criar uma sessão
POST /api/sessions com o CPF ou CNPJ do signatário (só dígitos), a URI de redirecionamento e o tipo de sessão. O CloudHub consulta todos os provedores em paralelo e devolve um authUrl por provedor que tem um certificado para esse identificador; uma lista services vazia significa que nenhum tem, ou que nenhum respondeu a tempo. Sem identificador, ele devolve todos os provedores e o usuário se identifica no provedor.
var session = await cloudhub.CreateSessionAsync(new SessionCreateRequest {
Identifier = "12345678909",
RedirectUri = "https://app.example.com/sign/complete?fileId=42",
Type = TrustServiceSessionTypes.SingleSignature,
LifetimeInSeconds = 300,
});
foreach (var service in session.Services) {
Console.WriteLine($"{service.ServiceInfo.Provider}: {service.AuthUrl}");
}
| Tipo de sessão | O usuário autoriza |
|---|---|
SingleSignature (padrão) | Uma assinatura: um documento, ou um desafio para autenticar o usuário. |
SignatureSession | Qualquer número de assinaturas até a sessão expirar: vários documentos com uma única autorização. |
MultiSignature | Uma operação de assinatura que cobre um ou mais hashes; depende do provedor. O CloudHub assina um hash por chamada, então prefira SignatureSession. |
AuthenticationSession | Só a leitura do certificado. Não recomendado: a maioria dos provedores não oferece esse tipo. |
lifetimeInSeconds é um pedido: o provedor pode conceder menos e aplica o próprio padrão quando o valor é omitido. Para levar seus próprios dados na ida e volta, acrescente parâmetros à URI de redirecionamento (eles voltam ao lado de session) ou envie customState, que o usuário não consegue ler nem alterar e que GET /api/sessions/custom-state devolve; os clientes .NET e Java publicados não têm esse campo.
Enviar o usuário a um provedor
Mostre uma opção por entrada de services, com badgeUrl como logotipo (pode ser nulo) e provider como legenda, e leve o navegador até o authUrl escolhido. O usuário autoriza no provedor, em geral no aplicativo dele, com PIN ou QR code; o provedor volta ao CloudHub, que redireciona para a sua URI de redirecionamento com o parâmetro session acrescentado à query string.
https://app.example.com/sign/complete?fileId=42&session=9f2c…e81a
Quando session não vem, o usuário cancelou, o provedor recusou ou a troca falhou; o CloudHub não diz qual, então deixe o usuário tentar de novo. Trate o valor de session (64 caracteres hexadecimais) como uma credencial: use-o logo, mantenha-o fora dos seus logs e só o aceite na URI de redirecionamento com a qual você criou a sessão.
Ler o certificado
GET /api/sessions/certificate?session=… devolve o certificado codificado em DER e depois em base64, como uma string JSON; GET /api/v2/sessions/certificate acrescenta o alias dele e o serviceName do provedor. Quando o provedor tem vários certificados do usuário, o CloudHub escolhe o válido no momento cuja validade começou mais recentemente; passe o alias ao sign-hash como certificateAlias para assinar exatamente com esse certificado.
byte[] certificate = await cloudhub.GetCertificateAsync(session);
Antes de assinar, compare o CPF (ou CNPJ) do certificado com o que você pediu: o usuário pode ter entrado no provedor com outra conta. Ler o certificado não conta como assinatura, então uma sessão SingleSignature ainda pode assinar uma vez.
Assinar um hash
POST /api/sessions/sign-hash com a sessão, o hash (em base64) e o algoritmo de hash dele, por OID (digestAlgorithmOid, que tem precedência) ou por nome (digestAlgorithm, como SHA-256). A resposta é o valor da assinatura, em base64, para a sua biblioteca de assinatura embutir no documento. A assinatura espera pelo provedor: aguarde até dois minutos.
byte[] signature = await cloudhub.SignHashAsync(new SignHashRequest {
Session = session,
Hash = toSignHash, // the hash, as bytes
DigestAlgorithmOid = digestAlgorithmOid,
});
O cliente Java 1.x devolve a string JSON bruta, com as aspas, nas operações de certificado e de sign-hash: tire as aspas antes de decodificar o base64, como mostra Primeiros passos.
Assinar um PDF de ponta a ponta
Dois handlers: o discover recebe o CPF, cria a sessão e lista os provedores; o complete, que é a URI de redirecionamento, lê o certificado, pede à biblioteca de assinatura que inicie a assinatura e devolva o hash a assinar, envia o hash ao sign-hash e pede à biblioteca que finalize o PDF. Assinando um PDF traz o exemplo PAdES completo com o PKI SDK (C#), o Rest PKI (PHP, Node.js, Java) e o PKI Express (Python). Para vários documentos com uma única autorização, use SignatureSession e repita as três últimas etapas para cada documento; CAdES e XAdES seguem o mesmo fluxo.
Autenticar um usuário
Crie uma sessão SingleSignature (e não AuthenticationSession), leia o certificado, peça ao CloudHub que assine um nonce criado pela sua biblioteca de assinatura e peça à biblioteca que confira a assinatura e valide o certificado; depois, encontre o usuário pelo CPF do certificado. O certificado sozinho não é prova. Veja Autenticação.
Descobrir provedores sem criar uma sessão
GET /api/sessions/services?identifier=… faz a descoberta sem criar uma sessão (sem authUrl); GET /api/sessions/services/{name}/availability?identifier=… consulta um único provedor; POST /api/sessions/services/{name} cria uma sessão no provedor que o usuário já escolheu, em que name é um serviceName como BirdID. Veja Descoberta.
Erros
O CloudHub recusa uma requisição com HTTP 422 e um corpo JSON com code, message e, às vezes, details. HTTP 400 lista os campos inválidos (como um redirectUri ausente), 401 indica uma chave de API ausente ou desconhecida e 500 traz um código curto para informar ao suporte da Lacuna. Veja Erros.
SessionExpired: o tempo de vida da sessão acabou, possivelmente já na primeira chamada, se o usuário demorou a autorizar. Crie uma nova sessão e envie o usuário ao provedor de novo.SessionInvalidOrAlreadyUsed: o CloudHub não conhece a sessão, ou o provedor não a aceita mais. Crie uma nova sessão.UnauthorizedRedirectUri(details.redirectUri) eInvalidRedirectUri: a URI de redirecionamento não está cadastrada para a sua chave, ou não é absoluta. Peça ao suporte que a cadastre, ou envie uma URI completa.IdentifierRequiredToDiscover,ServiceNotFound,DigestAlgorithmNameOrOidIsRequiredeInvalidDigestAlgorithm: corrija a requisição como indica a tabela de erros.- Lendo o código em um cliente: no .NET, capture
CloudhubApiExceptione compareCodeStr(o enumCodenão é preenchido); ocloudhub-client1.x para Node.js lança uma string que contém o erro em JSON; no PHP, decodifique o corpo daClientExceptiondo Guzzle; no Java, confiraApiException.getCode() == 422e o corpo da resposta.
Certificados e credenciais de teste
- O CloudHub assina com certificados em nuvem guardados nos provedores; os certificados da PKI de teste da Lacuna são arquivos que você instala localmente, e não certificados em nuvem, e essa página não tem uma seção sobre o CloudHub.
- Para desenvolvimento local, peça ao suporte que cadastre uma URI de redirecionamento como
https://localhost:5001ao lado das de produção. - Os exemplos confiam só em certificados ICP-Brasil; para testar com os certificados de teste de um provedor, confie na raiz deles na sua biblioteca de assinatura, só em desenvolvimento.
- Os PKI Suite Samples vêm com uma chave de API do CloudHub na configuração (no exemplo .NET,
CloudHubApiKeynoWeb.config); se o exemplo do CloudHub não funcionar, o próprio exemplo pede que você entre em contato com o suporte.
TODO(docs): se o CloudHub tem um ambiente de teste ou provedores de teste, quais provedores emitem certificados em nuvem de teste e quais URIs de redirecionamento a chave de API dos exemplos aceita.
Experimente
Abra a demonstração ou o projeto de exemplo da sua linguagem: