Como Integrar Assinatura Digital em Java / Spring
Se você desenvolve em Java e precisa adicionar assinatura digital com validade jurídica a uma aplicação Spring Boot, este guia é o caminho mais curto entre o seu pom.xml e um documento assinado. Vamos do zero ao fim: instalar o SDK oficial via Maven ou Gradle, autenticar com OAuth2, criar uma sessão de assinatura, adicionar signatários, enviar o documento, receber o webhook de conclusão com verificação HMAC em um @RestController e, por fim, baixar o PDF assinado.
O foco aqui é prático e idiomático. Todos os trechos de código usam padrões que você já conhece do ecossistema Spring — injeção de dependência, WebClient, RestTemplate, records, @RestController — e podem ser colados quase diretamente no seu projeto, ajustando apenas credenciais e nomes de pacote.
Para um panorama mais amplo de capacidades, autenticação e endpoints, vale conferir a documentação da API de assinatura digital da SignDocs. Se você ainda está decidindo a arquitetura geral da integração, comece por o que é uma API de assinatura digital. Aqui, vamos direto ao código Java.
Pré-requisitos para a integração em Java
Antes de escrever a primeira linha de código, garanta que seu ambiente atende ao básico:
- Java 11 ou superior (recomendado Java 17 LTS ou 21 LTS). O SDK oficial é compilado para o bytecode de Java 11.
- Maven 3.6+ ou Gradle 7+ como gerenciador de build.
- Spring Boot 2.7+ ou 3.x caso vá usar o exemplo de
@RestControllerpara webhooks (o SDK em si não depende de Spring). - Credenciais de API: um par
client_ideclient_secretobtidos no painel da SignDocs. Veja como obter sua API key de assinatura. - Um endpoint HTTPS público para receber webhooks (em desenvolvimento, use um túnel como o ngrok).
api-hml.signdocs.com.br (com hífen, não api.hml) e use credenciais de sandbox. Lembre-se de que entidades de homologação têm TTL de 7 dias. Detalhes em ambiente de homologação e sandbox.
Passo 1 — Adicionar a dependência do SDK Java
A SignDocs mantém SDKs oficiais para TypeScript/Node, Python, Go, Java, PHP e C#/.NET. O SDK Java é publicado no Maven Central, então adicioná-lo é uma questão de uma dependência. Para usuários do Maven, inclua o seguinte bloco no seu pom.xml:
Se você usa Gradle, basta uma linha no bloco dependencies do build.gradle:
io.github.signdocsbrasil:signdocsbrasil-api antes de fixar a dependência. Recomendamos fixar uma versão exata em produção e atualizá-la de forma controlada. A única dependência transitiva é o Gson.
Após sincronizar o projeto (mvn install ou gradle build), as classes do cliente ficam disponíveis para importação. Não quer usar o SDK? Sem problema: a API REST é agnóstica de linguagem e pode ser consumida com WebClient, RestTemplate ou a java.net.http.HttpClient nativa — mostramos as duas abordagens ao longo do artigo.
Passo 2 — Autenticar com OAuth2 (client-credentials)
A API SignDocs usa OAuth2 com fluxo client-credentials. Você troca seu client_id e client_secret por um access_token do tipo bearer, assinado com ECDSA ES256 e válido por 15 minutos, e o envia no header Authorization de cada requisição. Para entender a fundo o fluxo, os escopos e a rotação de tokens, leia o guia de autenticação OAuth2 na API de assinatura.
Com o SDK oficial
O SDK abstrai todo o ciclo de vida do token: ele obtém, faz cache e renova o access_token automaticamente antes da expiração. Você apenas inicializa o cliente uma vez, idealmente como um @Bean gerenciado pelo Spring:
Mantenha o client-secret fora do código-fonte. Use application.yml com variáveis de ambiente, Spring Cloud Config, AWS Secrets Manager ou Vault:
Sem SDK: obtendo o token manualmente com WebClient
Se preferir consumir a REST diretamente, o fluxo client-credentials cabe em poucas linhas com o WebClient do Spring WebFlux:
access_token em memória e só renove quando estiver perto de expirar (use o campo expires_in com uma margem de segurança de 60 segundos). Obter um token novo a cada requisição é desperdício e pode encostar nos rate limits. Para clientes enterprise em ambientes regulados (BACEN/Open Finance), o OAuth2 pode ser combinado com mTLS (mutual TLS) para autenticação mútua de ponta a ponta.
Escolhendo a superfície de API: Expressa ou Transacional
A SignDocs expõe duas superfícies de API, e a escolha define o restante da sua integração Java:
| Superfície | Quando usar | Caracterização |
|---|---|---|
| Assinatura Expressa (Signing Sessions) |
Um signatário, fluxo rápido, checkout hospedado ou widget embutido | Uma única chamada POST /v1/signing-sessions retorna a URL pronta de assinatura |
| API Transacional (Envelopes) |
Múltiplos signatários, ordem de assinatura, ciclo de vida completo | Criação de envelope, adição de documentos e signatários, controle de estado granular |
Neste tutorial usaremos a Assinatura Expressa como caminho principal por ser a forma mais rápida de colocar um documento para assinar em Java, e mostraremos a variação transacional onde for relevante. Para o desenho completo do ciclo de vida transacional, consulte o fluxo transacional da API.
Passo 3 — Criar uma sessão de assinatura e adicionar o signatário
Com o cliente autenticado, criar uma sessão de assinatura é uma única chamada. Você envia o documento (em base64 ou referência), define o signatário e o perfil de autenticação desejado. Veja o exemplo com o SDK Java, usando records imutáveis para o payload:
A resposta traz a sessão criada, incluindo o identificador e a URL para a qual você redireciona (ou embute) o signatário:
policy.profile é DIGITAL_CERTIFICATE para certificado ICP-Brasil — nunca DIGITAL_SIGN_A1, que é apenas um step.type na resposta; (2) a URL de compartilhamento real combina o campo url com o clientSecret como parâmetro de query ?cs=. O url sozinho não é o link de assinatura.
Perfis e métodos de autenticação disponíveis
O campo profile determina as etapas que o signatário percorre. As opções abrangem desde o clickwrap simples até biometria facial e certificado ICP-Brasil:
CLICK_ONLY— aceite por clique (clickwrap) com evidências.CLICK_PLUS_OTP— aceite + código de uso único por SMS ou e-mail.BIOMETRIC/BIOMETRIC_PLUS_OTP— prova de vida e match facial, com ou sem OTP.DIGITAL_CERTIFICATE— assinatura com certificado ICP-Brasil A1 via API (o passoDIGITAL_SIGN_A1aparece na resposta; titulares de token A3 usam o assinador desktop do aplicativo).
Para escolher a combinação certa por caso de uso e nível de garantia, leia a seção de métodos em o que é uma API de assinatura digital.
Variação transacional: envelope com múltiplos signatários
Quando o mesmo documento precisa de mais de um signatário e ordem de assinatura, use envelopes: crie o envelope com o modo sequencial e adicione uma sessão por signatário, com a posição definida pelo signerIndex:
Passo 4 — Enviar o documento para assinatura
Na Assinatura Expressa, a própria criação da sessão já dispara o fluxo: você entrega a URL ao signatário (por e-mail, link no seu app ou checkout embutido). Em envelopes, cada addSession devolve a URL daquele signatário — e, se o envelope tiver um owner, a SignDocs envia os convites por e-mail automaticamente.
Há três formas comuns de entregar o link em uma aplicação Java/Spring:
- Redirecionamento direto: seu controller responde com um
302para a URL da sessão (combinada com o?cs=). - Checkout hospedado: você simplesmente envia o link por e-mail e a SignDocs hospeda toda a experiência. Veja a página da Assinatura Expressa.
- Checkout embutido (popup): no frontend, o SDK
@signdocs-brasil/jsabre o checkout como popup a partir da sua UI, usando oclientSecretda sessão.
Um endpoint Spring que cria a sessão e devolve o link pronto ao frontend fica assim:
Passo 5 — Receber o webhook com verificação HMAC em Spring
Em vez de ficar consultando o status (polling), você registra uma URL de webhook e a SignDocs envia um POST HTTPS sempre que algo acontece: SIGNING_SESSION.COMPLETED, TRANSACTION.COMPLETED, STEP.FAILED, etc. Os webhooks são assinados com HMAC-SHA256, têm idempotência e retentativa com backoff exponencial. O aprofundamento está em webhooks e eventos da API de assinatura.
O ponto crítico em Java é validar a assinatura HMAC usando o corpo bruto (raw body) da requisição. Se você desserializar o JSON antes de validar, a recodificação pode alterar bytes e quebrar o HMAC. Por isso, recebemos o corpo como String:
MessageDigest.isEqual? Comparar a assinatura com equals() ou == é vulnerável a timing attacks, em que um atacante infere caracteres corretos medindo o tempo de resposta. MessageDigest.isEqual em JDKs modernos faz comparação de tempo constante. Sempre valide a assinatura antes de desserializar ou processar qualquer dado do payload.
Se preferir não implementar o HMAC manualmente, o SDK traz o helper estático WebhookVerifier.verifySignature(body, signature, timestamp, secret), que valida a assinatura e a tolerância de replay em uma chamada.
Como webhooks operam com entrega at-least-once, seu processador deve ser idempotente. Use o id do payload como chave de deduplicação (em Redis, banco ou cache), garantindo que processar o mesmo evento duas vezes produza o mesmo resultado:
Passo 6 — Baixar o PDF assinado e o pacote de evidências
Quando chega o evento TRANSACTION.COMPLETED, o documento final está pronto. Em Java, o método documents().download() devolve as URLs temporárias de download; você baixa os bytes e grava em disco, envia a um bucket S3 ou anexa a um e-mail:
Caso não esteja usando o SDK, o download é um simples GET autenticado com o bearer token, lido como byte[]:
O PDF assinado segue o padrão PAdES (assinatura embarcada no PDF), enquanto o .p7m é um container PKCS#7/CMS que reúne as assinaturas, o hash SHA-256 do documento, o carimbo de hora do servidor e a trilha de auditoria. Para entender quando usar cada padrão, veja PKCS#7, CMS, PAdES e CAdES no glossário, e para o valor probatório do pacote, o evidence pack .p7m como prova jurídica.
Validação pública independente
Qualquer documento assinado pode ser conferido por terceiros, sem depender da sua aplicação, no verificador público da SignDocs. Isso reforça a confiança e a transparência do fluxo — útil em auditorias e disputas.
Visão de ponta a ponta e tratamento de erros
Reunindo tudo, o fluxo idiomático de uma integração Java/Spring com a SignDocs fica assim:
- Adicionar o SDK via Maven/Gradle (ou consumir a REST com
WebClient). - Configurar credenciais e o cliente OAuth2 como um
@Bean. - Criar a sessão de assinatura (Expressa) ou o envelope (Transacional) e adicionar signatários.
- Entregar o link de assinatura (redirect, e-mail ou checkout embutido via SDK).
- Receber o webhook em um
@RestController, validar o HMAC e enfileirar de forma idempotente. - Ao concluir, baixar o PDF PAdES e o pacote de evidências
.p7m.
Em produção, trate erros de forma resiliente. A API usa convenções HTTP padrão; mapeie os principais cenários:
| Status | Significado | Ação recomendada em Java |
|---|---|---|
| 401 | Token inválido ou expirado | Renovar o access_token e repetir a chamada uma vez |
| 400 | Payload inválido (ex.: profile errado) |
Logar o corpo da resposta; não repetir sem corrigir |
| 429 | Rate limit excedido | Respeitar Retry-After com backoff exponencial |
| 5xx | Erro temporário do servidor | Retry com backoff; usar chave de idempotência |
profile: o erro 400 mais comum em integrações novas é enviar DIGITAL_SIGN_A1 como valor de policy.profile. Isso é rejeitado pela API. O valor correto para certificado ICP-Brasil é DIGITAL_CERTIFICATE; DIGITAL_SIGN_A1 aparece apenas como step.type na resposta.
Por que a SignDocs para projetos Java no Brasil
Diferente de soluções estrangeiras adaptadas, a SignDocs é nativa de ICP-Brasil, com produto e suporte em pt-BR e arquitetura LGPD-first. A validade jurídica se ancora na MP 2.200-2/2001, que institui a ICP-Brasil, e a plataforma cobre tanto a assinatura eletrônica avançada quanto a qualificada com certificado ICP-Brasil (A1 via API; A3 pelo assinador desktop do aplicativo).
- SDK Java oficial publicado no Maven Central, ao lado de SDKs para Node, Python, Go, PHP e C#/.NET.
- Webhooks robustos com HMAC-SHA256, idempotência e retry com backoff — fáceis de consumir em Spring.
- mTLS opcional para clientes enterprise e regulados (BACEN/Open Finance).
- Verificador público e pacote de evidências
.p7mcom força probatória. - Sandbox de homologação gratuito para validar a integração; o acesso de produção à API é um plano sob medida dimensionado com o time comercial — entenda os modelos em quanto custa uma API de assinatura.
Se você trabalha em outras stacks, há guias equivalentes para C#/.NET e PHP, todos seguindo o mesmo modelo de API.
Perguntas Frequentes
Existe um SDK oficial de assinatura digital para Java?
Sim. A SignDocs Brasil mantém SDKs oficiais para TypeScript/Node, Python, Go, Java, PHP e C#/.NET. O SDK Java é distribuído via Maven Central e funciona em qualquer projeto Java 11 ou superior, incluindo aplicações Spring Boot. Caso prefira não usar o SDK, a API REST é totalmente agnóstica de linguagem e pode ser consumida com qualquer cliente HTTP, como RestTemplate, WebClient ou a HttpClient nativa do Java.
Como adiciono a dependência do SDK Java via Maven e Gradle?
No Maven, adicione um bloco <dependency> com o groupId io.github.signdocsbrasil, artifactId signdocsbrasil-api e a versão desejada ao seu pom.xml. No Gradle, basta uma linha do tipo implementation 'io.github.signdocsbrasil:signdocsbrasil-api:1.2.0' no bloco dependencies do build.gradle. Após sincronizar o projeto, as classes do cliente ficam disponíveis para importação. Sempre confira a versão mais recente publicada no Maven Central.
Como funciona a autenticação OAuth2 no SDK Java?
A API usa OAuth2 com fluxo client-credentials. Você envia seu client_id e client_secret ao endpoint de token e recebe um access_token (bearer) assinado com ECDSA ES256, válido por 15 minutos. O SDK Java cuida automaticamente da obtenção, cache e renovação do token antes da expiração, mas você também pode implementar o fluxo manualmente com WebClient. Em integrações enterprise reguladas, é possível combinar OAuth2 com mTLS para autenticação mútua.
Como valido a assinatura HMAC de um webhook em um @RestController?
Receba o corpo bruto (raw body) do webhook como String, recalcule o HMAC-SHA256 usando seu webhook secret e compare com o header de assinatura enviado pela SignDocs. Use uma comparação timing-safe como MessageDigest.isEqual para evitar timing attacks e valide também o header de timestamp para prevenir ataques de replay. Só processe o evento depois que a assinatura for confirmada como válida; caso contrário, retorne HTTP 401.
Preciso de um certificado ICP-Brasil no servidor para usar a API em Java?
Não para o caso mais comum. A assinatura eletrônica avançada (clickwrap, OTP, biometria) não exige certificado no seu servidor. O certificado ICP-Brasil entra apenas quando você usa o perfil DIGITAL_CERTIFICATE (o passo DIGITAL_SIGN_A1 aparece na resposta), e nesse caso é o próprio signatário quem aplica o certificado A1 — a chave privada nunca passa pela sua aplicação Java. Seu backend apenas orquestra a transação via API.
Como baixo o PDF assinado e o pacote de evidências em Java?
Após receber o evento TRANSACTION.COMPLETED via webhook, chame documents().download(transactionId) no SDK Java. A resposta traz a URL temporária do PDF assinado (signedUrl); baixe os bytes e grave em disco, envie a um bucket S3 ou anexe a um e-mail. Além do PDF no padrão PAdES, você pode baixar o pacote de evidências .p7m (PKCS#7/CMS), que reúne a prova jurídica completa da assinatura.
Posso testar a integração Java em ambiente de homologação antes de ir para produção?
Sim. Aponte o cliente para o host de homologação api-hml.signdocs.com.br (com hífen, não api.hml) usando credenciais de sandbox. Esse ambiente replica o comportamento de produção sem custos nem efeitos legais reais. Lembre-se de que entidades criadas em homologação têm TTL de 7 dias, ou seja, são apagadas automaticamente após esse período — ideal para testes de CI/CD.
Coloque assinatura digital no seu app Java em minutos
SDK Java oficial no Maven Central, autenticação OAuth2, webhooks com HMAC-SHA256 e validade jurídica ICP-Brasil. O acesso à API é um plano sob medida, com sandbox de homologação gratuito para integrar antes de qualquer contrato.
Fale com o time comercial Conheça a plataforma grátis