Guias de Desenvolvimento

Política e Conformidade

Signing Sessions

Sessão de Confiança

Modelos de Solução

Guias dos SDKs

Certificado Digital A1 (DIGITAL_CERTIFICATE)

Assinatura com certificado digital ICP-Brasil tipo A1. A chave privada nunca sai do lado do cliente — a API fornece o hash a ser assinado, o cliente assina localmente e envia a assinatura de volta.

Etapas geradas: DIGITAL_SIGN_A1


Fluxo Criptográfico

Cliente                                    API SignDocsBrasil
  │                                              │
  │  1. certificateChainPems ──────────────────► │  Valida cadeia
  │                                              │  Prepara documento
  │  ◄──────────────────── 2. hashToSign         │
  │                                              │
  │  3. Assina hash localmente                   │
  │     (chave privada NUNCA sai do cliente)      │
  │                                              │
  │  4. rawSignatureBase64 ────────────────────► │  Embute no documento
  │                                              │  Gera CMS/PKCS#7
  │  ◄──────────────────── 5. Documento assinado  │

Passo 1: Criar transação

Use o perfil DIGITAL_CERTIFICATE. Opcionalmente, configure metadados da assinatura digital.

const tx = await client.transactions.create({
  purpose: 'DOCUMENT_SIGNATURE',
  policy: { profile: 'DIGITAL_CERTIFICATE' },
  signer: {
    name: 'João Silva',
    userExternalId: 'user-001',
    cpf: '12345678900',
  },
  document: {
    content: documentBase64,
    filename: 'contrato.pdf',
  },
  digitalSignature: {
    signatureFieldName: 'Assinatura1',
    signatureReason: 'Aprovação do contrato',
    signatureLocation: 'São Paulo, SP',
  },
});
tx = client.transactions.create(CreateTransactionRequest(
    purpose='DOCUMENT_SIGNATURE',
    policy=Policy(profile='DIGITAL_CERTIFICATE'),
    signer=Signer(name='João Silva', user_external_id='user-001', cpf='12345678900'),
    document=InlineDocument(content=document_base64, filename='contrato.pdf'),
    digital_signature=DigitalSignatureMetadata(
        signature_field_name='Assinatura1',
        signature_reason='Aprovação do contrato',
        signature_location='São Paulo, SP',
    ),
))
tx, _ := client.Transactions.Create(ctx, &signdocs.CreateTransactionRequest{
    Purpose: signdocs.TransactionPurposeDocumentSignature,
    Policy:  signdocs.Policy{Profile: signdocs.PolicyProfileDigitalCertificate},
    Signer:  signdocs.Signer{Name: "João Silva", UserExternalID: "user-001", CPF: "12345678900"},
    Document: &signdocs.DocumentInline{Content: documentBase64, Filename: "contrato.pdf"},
    DigitalSignature: &signdocs.DigitalSignatureMetadata{
        SignatureFieldName: "Assinatura1",
        SignatureReason:    "Aprovação do contrato",
        SignatureLocation:  "São Paulo, SP",
    },
})
CreateTransactionRequest request = new CreateTransactionRequest();
request.purpose = "DOCUMENT_SIGNATURE";
request.policy = new Policy("DIGITAL_CERTIFICATE");
request.signer = new Signer("João Silva", null, "user-001");
request.signer.cpf = "12345678900";
request.document = new CreateTransactionRequest.InlineDocument(documentBase64, "contrato.pdf");
request.digitalSignature = new CreateTransactionRequest.DigitalSignatureMetadata();
request.digitalSignature.signatureFieldName = "Assinatura1";
request.digitalSignature.signatureReason = "Aprovação do contrato";
request.digitalSignature.signatureLocation = "São Paulo, SP";

Transaction tx = client.transactions().create(request);
$tx = $client->transactions->create(new CreateTransactionRequest(
    purpose: 'DOCUMENT_SIGNATURE',
    policy: new Policy(profile: 'DIGITAL_CERTIFICATE'),
    signer: new Signer(name: 'João Silva', userExternalId: 'user-001', cpf: '12345678900'),
    document: ['content' => $documentBase64, 'filename' => 'contrato.pdf'],
    digitalSignature: [
        'signatureFieldName' => 'Assinatura1',
        'signatureReason' => 'Aprovação do contrato',
        'signatureLocation' => 'São Paulo, SP',
    ],
));
using SignDocsBrasil.Api;
using SignDocsBrasil.Api.Models;

var client = SignDocsBrasilClient.CreateBuilder()
    .ClientId("your-client-id")
    .ClientSecret("your-client-secret")
    .Build();

var tx = await client.Transactions.CreateAsync(new CreateTransactionRequest
{
    Purpose = "DOCUMENT_SIGNATURE",
    Policy = new Policy { Profile = "DIGITAL_CERTIFICATE" },
    Signer = new Signer { Name = "João Silva", UserExternalId = "user-001", Cpf = "12345678900" },
    Document = new InlineDocument { Content = documentBase64, Filename = "contrato.pdf" },
    DigitalSignature = new DigitalSignatureMetadata
    {
        SignatureFieldName = "Assinatura1",
        SignatureReason = "Aprovação do contrato",
        SignatureLocation = "São Paulo, SP",
    },
});

Passo 2: Preparar assinatura (enviar cadeia de certificados)

Envie a cadeia de certificados PEM (leaf + intermediários). A API valida a cadeia e prepara o hash do documento.

const prep = await client.signing.prepare(tx.transactionId, {
  certificateChainPems: [leafCertPem, intermediateCertPem],
});
console.log(prep.signatureRequestId);  // ID da requisição
console.log(prep.hashToSign);          // Hash a ser assinado (hex)
console.log(prep.hashAlgorithm);       // SHA-256
console.log(prep.signatureAlgorithm);  // RSASSA-PKCS1-v1_5
from signdocs_brasil.models import PrepareSigningRequest

prep = client.signing.prepare(tx.transaction_id, PrepareSigningRequest(
    certificate_chain_pems=[leaf_cert_pem, intermediate_cert_pem],
))
print(prep.signature_request_id)
print(prep.hash_to_sign)
print(prep.hash_algorithm)       # SHA-256
print(prep.signature_algorithm)  # RSASSA-PKCS1-v1_5
prep, _ := client.Signing.Prepare(ctx, tx.TransactionID, &signdocs.PrepareSigningRequest{
    CertificateChainPEMs: []string{leafCertPem, intermediateCertPem},
})
fmt.Println(prep.SignatureRequestID)
fmt.Println(prep.HashToSign)
fmt.Println(prep.HashAlgorithm)      // SHA-256
fmt.Println(prep.SignatureAlgorithm) // RSASSA-PKCS1-v1_5
PrepareSigningResponse prep = client.signing().prepare(tx.transactionId,
    new PrepareSigningRequest(List.of(leafCertPem, intermediateCertPem)));
System.out.println(prep.signatureRequestId);
System.out.println(prep.hashToSign);
System.out.println(prep.hashAlgorithm);      // SHA-256
System.out.println(prep.signatureAlgorithm); // RSASSA-PKCS1-v1_5
$prep = $client->signing->prepare($tx->transactionId, new PrepareSigningRequest(
    certificateChainPems: [$leafCertPem, $intermediateCertPem],
));
echo $prep->signatureRequestId;
echo $prep->hashToSign;
echo $prep->hashAlgorithm;      // SHA-256
echo $prep->signatureAlgorithm; // RSASSA-PKCS1-v1_5
using System.Security.Cryptography.X509Certificates;

var pfxBytes = await File.ReadAllBytesAsync("certificado.pfx");
var cert = new X509Certificate2(pfxBytes, "senha_do_pfx", X509KeyStorageFlags.Exportable);
var leafCertPem = cert.ExportCertificatePem();

var prep = await client.Signing.PrepareAsync(tx.TransactionId, new PrepareSigningRequest
{
    CertificateChainPems = new[] { leafCertPem, intermediateCertPem },
});
Console.WriteLine($"SignatureRequestId: {prep.SignatureRequestId}");
Console.WriteLine($"HashToSign: {prep.HashToSign}");
Console.WriteLine($"HashAlgorithm: {prep.HashAlgorithm}");          // SHA-256
Console.WriteLine($"SignatureAlgorithm: {prep.SignatureAlgorithm}"); // RSASSA-PKCS1-v1_5

Passo 3: Assinar hash localmente

Assine prep.hashToSign com a chave privada do certificado usando RSASSA-PKCS1-v1_5 com SHA-256. Converta o resultado para base64.

A chave privada NUNCA é enviada para a API. Toda a operação criptográfica ocorre no ambiente do cliente.

import crypto from 'crypto';

const sign = crypto.createSign('SHA256');
sign.update(Buffer.from(prep.hashToSign, 'hex'));
const signatureBase64 = sign.sign(privateKeyPem, 'base64');
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import base64

private_key = serialization.load_pem_private_key(private_key_pem.encode(), password=None)
signature = private_key.sign(
    bytes.fromhex(prep.hash_to_sign),
    padding.PKCS1v15(),
    hashes.SHA256(),
)
signature_base64 = base64.b64encode(signature).decode()
import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "encoding/base64"
    "encoding/hex"
)

hashBytes, _ := hex.DecodeString(prep.HashToSign)
signature, _ := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashBytes)
signatureBase64 := base64.StdEncoding.EncodeToString(signature)
import java.security.Signature;
import java.util.Base64;

byte[] hashBytes = hexToBytes(prep.hashToSign);
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey);
sig.update(hashBytes);
byte[] signature = sig.sign();
String signatureBase64 = Base64.getEncoder().encodeToString(signature);
$hashBytes = hex2bin($prep->hashToSign);
openssl_sign($hashBytes, $signature, $privateKey, OPENSSL_ALGO_SHA256);
$signatureBase64 = base64_encode($signature);
using System.Security.Cryptography;

var rsa = cert.GetRSAPrivateKey()!;
var hashBytes = Convert.FromHexString(prep.HashToSign);
var signatureBytes = rsa.SignData(hashBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
var signatureBase64 = Convert.ToBase64String(signatureBytes);

Passo 4: Completar assinatura (enviar assinatura raw)

Envie a assinatura base64 de volta para a API. Ela será embutida no documento junto com a cadeia de certificados (PAdES para PDF, CAdES .p7s para demais formatos).

const result = await client.signing.complete(tx.transactionId, {
  signatureRequestId: prep.signatureRequestId,
  rawSignatureBase64: signatureBase64,
});
console.log(result.status); // COMPLETED
console.log(result.result.digitalSignature.certificateSubject);
console.log(result.result.digitalSignature.signedAt);
from signdocs_brasil.models import CompleteSigningRequest

result = client.signing.complete(tx.transaction_id, CompleteSigningRequest(
    signature_request_id=prep.signature_request_id,
    raw_signature_base64=signature_base64,
))
print(result.status)  # COMPLETED
print(result.result.digital_signature.certificate_subject)
result, _ := client.Signing.Complete(ctx, tx.TransactionID, &signdocs.CompleteSigningRequest{
    SignatureRequestID: prep.SignatureRequestID,
    RawSignatureBase64: signatureBase64,
})
fmt.Println(result.Status)
fmt.Println(result.Result.DigitalSignature.CertificateSubject)
CompleteSigningResponse result = client.signing().complete(tx.transactionId,
    new CompleteSigningRequest(prep.signatureRequestId, signatureBase64));
System.out.println(result.status);
System.out.println(result.result.digitalSignature.certificateSubject);
$result = $client->signing->complete($tx->transactionId, new CompleteSigningRequest(
    signatureRequestId: $prep->signatureRequestId,
    rawSignatureBase64: $signatureBase64,
));
echo $result->status;
echo $result->result['digitalSignature']['certificateSubject'];
var result = await client.Signing.CompleteAsync(tx.TransactionId, new CompleteSigningRequest
{
    SignatureRequestId = prep.SignatureRequestId,
    RawSignatureBase64 = signatureBase64,
});
Console.WriteLine($"Status: {result.Status}"); // COMPLETED
Console.WriteLine($"Subject: {result.Result.DigitalSignature.CertificateSubject}");
Console.WriteLine($"SignedAt: {result.Result.DigitalSignature.SignedAt}");

Carimbo do tempo ICP-Brasil (opcional)

Com o recurso de carimbo do tempo habilitado no contrato, o servidor embute na assinatura, durante este passo, um carimbo do tempo RFC 3161 emitido por uma Autoridade de Carimbo do Tempo (ACT) credenciada na ICP-Brasil (PAdES-B-T / CAdES-T). O código de assinatura não muda; o resultado passa a trazer signatureTimestamp:

SDK Acesso
TypeScript result.result.digitalSignature.signatureTimestamp?.genTime
Python result.result.digital_signature.signature_timestamp.gen_time
Go result.Result.DigitalSignature.SignatureTimestamp.GenTime (ponteiro, nil sem o recurso)
Java result.getResult().getDigitalSignature().getSignatureTimestamp().getGenTime()
PHP $result->signatureTimestamp()['genTime'] (null sem o recurso)
C# (.NET) result.Result.DigitalSignature.SignatureTimestamp?.GenTime

Se a ACT estiver indisponível, complete lança o erro 503 com código TIMESTAMP_UNAVAILABLE (veja Tratamento de erros). A solicitação de assinatura continua pendente: reenvie a mesma rawSignatureBase64 com o mesmo signatureRequestId após o Retry-After, sem assinar de novo.


Passo 5: Finalizar transação

const finalized = await client.transactions.finalize(tx.transactionId);
const evidence = await client.evidence.get(tx.transactionId);
const download = await client.documents.download(tx.transactionId);
finalized = client.transactions.finalize(tx.transaction_id)
evidence = client.evidence.get(tx.transaction_id)
download = client.documents.download(tx.transaction_id)
client.Transactions.Finalize(ctx, tx.TransactionID)
evidence, _ := client.Evidence.Get(ctx, tx.TransactionID)
download, _ := client.Documents.Download(ctx, tx.TransactionID)
client.transactions().finalize(tx.transactionId);
Evidence evidence = client.evidence().get(tx.transactionId);
DownloadResponse download = client.documents().download(tx.transactionId);
$client->transactions->finalize($tx->transactionId);
$evidence = $client->evidence->get($tx->transactionId);
$download = $client->documents->download($tx->transactionId);
await client.Transactions.FinalizeAsync(tx.TransactionId);
var evidence = await client.Evidence.GetAsync(tx.TransactionId);
var download = await client.Documents.DownloadAsync(tx.TransactionId);

Campos do resultado da assinatura digital

Campo Descrição
certificateSubject DN do certificado (ex: CN=JOAO SILVA, ...)
certificateSerial Número serial do certificado
certificateIssuer DN da autoridade certificadora
algorithm Algoritmo usado (ex: SHA256withRSA)
signedAt Timestamp da assinatura (ISO 8601)
signedPdfHash Hash SHA-256 do documento assinado (PDF). Para formatos genéricos, use signedP7sHash.
signatureFieldName Nome do campo de assinatura no PDF (apenas PAdES)
signatureTimestamp Carimbo do tempo da ACT (genTime, tsaName, serial, policyOid, tokenSha256), apenas com o recurso habilitado. genTime é a hora atestada pela ACT; signedAt continua sendo a hora do servidor SignDocs.