sgcSign Server: assinatura de código e de documentos auto-hospedada via REST

· Componentes

A assinatura de código tem um problema de gerenciamento de chaves. A configuração habitual copia um arquivo .pfx, ou conecta um token USB, em cada agente de build que precisa assinar uma release. A chave privada acaba em máquinas que executam scripts de build não confiáveis, não há registro do que foi assinado ou por quem, e rotacionar o certificado significa mexer em cada agente. O sgcSign 2026.6 responde a isso com um novo servidor de assinatura: um daemon auto-hospedado que expõe a assinatura por meio de uma API REST TLS, para que seus pipelines e desenvolvedores assinem remotamente enquanto a chave de assinatura permanece em exatamente um lugar.

A chave nunca sai do servidor. Melhor ainda, o servidor pode estar à frente de um token de hardware ou de um KMS na nuvem, de modo que a chave nunca exista como arquivo. Toda requisição é autenticada com uma chave de API, tem limite de taxa e é gravada em um log de auditoria à prova de adulteração. Este post aborda o que o servidor pode assinar, como configurá-lo, um início rápido de cinco minutos e as duas formas de chamá-lo: curl simples e o cliente de linha de comando sgcsign incluído.

O que ele assina

Um servidor, uma API, oito formatos de assinatura. O mesmo formato de endpoint (POST /api/v1/sign/<format>) cobre tanto executáveis quanto documentos:

Todo formato aceita uma URL de timestamp RFC 3161 opcional, de modo que as assinaturas permaneçam válidas após o certificado de assinatura expirar. Um endpoint complementar POST /api/v1/verify verifica uma assinatura existente e retorna o subject do signatário, a validade e o timestamp.

Provedores de chave plugáveis

Um provedor é um handle nomeado para uma chave de assinatura. O chamador nomeia um provedor em cada requisição; ele nunca vê a chave. Você pode registrar quantos precisar, e o mesmo servidor pode combinar um PFX local para ferramentas internas com um certificado EV respaldado pela nuvem para releases públicas. São suportados nove tipos de provedor:

Os segredos ficam fora do arquivo de configuração. Qualquer parâmetro cujo nome termina em _env é lido de uma variável de ambiente, de modo que a senha do PFX, o PIN do token ou o segredo da nuvem é fornecido pelo ambiente do serviço, não gravado em disco.

Configurando o servidor

Todo o servidor é controlado por um único arquivo JSON, sgcSignServer.conf.json (um exemplo documentado é fornecido como sgcSignServer.conf.sample.json). A estrutura de nível superior é um punhado de blocos:

{
  "server": {
    "listen": "0.0.0.0",
    "port": 8443,
    "tls": {
      "enabled": true,
      "cert_file": "certs/server.crt",
      "key_file":  "certs/server.key"
    },
    "max_upload_mb": 512,
    "firewall": {
      "enabled": true,
      "whitelist": ["10.0.0.0/8"],
      "brute_force": { "enabled": true, "max_attempts": 5, "ban_duration_sec": 900 },
      "rate_limit":  { "enabled": true, "max_connections_per_ip": 30, "time_window_sec": 60 }
    }
  },
  "storage": { "sqlite_path": "data/sgcsignserver.db" },
  "admin": {
    "initial_user": "admin",
    "initial_password_env": "SGCSIGN_ADMIN_INIT_PW",
    "session_timeout_minutes": 60
  },
  "audit": { "retention_days": 365 },
  "providers": [
    {
      "name": "pfx-build",
      "type": "PFX",
      "params": { "file": "certs/build.pfx", "password_env": "SGCSIGN_PFX_PW" }
    },
    {
      "name": "ev-release",
      "type": "AzureTS",
      "params": {
        "tenant_id": "00000000-0000-0000-0000-000000000000",
        "client_id": "00000000-0000-0000-0000-000000000000",
        "client_secret_env": "SGCSIGN_AZURE_TS_SECRET",
        "account": "your-account",
        "certificate_profile": "your-profile",
        "endpoint": "https://eus.codesigning.azure.net/"
      }
    }
  ]
}

Os blocos mapeiam diretamente para os recursos. server define o endereço de bind, a porta (8443 por padrão), o certificado TLS e o firewall integrado (listas de permissão e negação de IP, bloqueio por força bruta, limitação de taxa de conexões, proteções contra path-traversal e payload). storage aponta para o banco de dados SQLite que armazena usuários, chaves de API, o log de auditoria e a fila de webhooks. admin define o primeiro usuário e quanto tempo as sessões duram. audit define a janela de retenção. providers é a lista acima.

A senha de administrador de bootstrap nunca é armazenada no arquivo. Na primeira inicialização, quando a tabela de usuários está vazia, o servidor lê a variável de ambiente indicada por initial_password_env (padrão SGCSIGN_ADMIN_INIT_PW) e cria a conta de administrador. Em cada inicialização posterior, essa variável é ignorada, de modo que ela só semeia o primeiro login.

Início rápido em cinco minutos

Instale o servidor (o assistente de instalação registra o serviço do Windows, ou execute sgcSignServer.exe --install) e então:

1. Crie um certificado de assinatura de código de teste e exporte-o como um PFX. Para uma implantação real, você importa seu certificado emitido pela CA, ou pula esta etapa e aponta um provedor para o seu token ou KMS na nuvem.

$cert = New-SelfSignedCertificate -Type CodeSigningCert `
  -Subject "CN=sgcSign Quickstart" -KeyAlgorithm RSA -KeyLength 3072 `
  -CertStoreLocation Cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(1)
$pw = ConvertTo-SecureString "QuickStartPFX!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -Password $pw `
  -FilePath "C:\Program Files\sgcSign Server\certs\quickstart.pfx"

2. Defina os segredos como variáveis de ambiente da máquina para que a conta de serviço possa lê-los:

setx /M SGCSIGN_ADMIN_INIT_PW "ChangeMeNow!"
setx /M SGCSIGN_PFX_PW        "QuickStartPFX!"

3. Escreva uma configuração mínima com um provedor PFX (TLS desligado apenas para um teste em localhost):

{
  "server":   { "listen": "127.0.0.1", "port": 8443, "tls": { "enabled": false } },
  "storage":  { "sqlite_path": "data/sgcsignserver.db" },
  "admin":    { "initial_user": "admin", "initial_password_env": "SGCSIGN_ADMIN_INIT_PW" },
  "audit":    { "retention_days": 365 },
  "providers": [
    { "name": "pfx-quickstart", "type": "PFX",
      "params": { "file": "certs/quickstart.pfx", "password_env": "SGCSIGN_PFX_PW" } }
  ]
}

4. Inicie o serviço e confirme que ele está escutando:

sc start sgcSignServer
sc query sgcSignServer

Se ele parar na inicialização, execute-o em primeiro plano com sgcSignServer.exe --console --config <path> para ver o erro ao vivo (uma SGCSIGN_ADMIN_INIT_PW ausente ou um PFX ilegível são as causas usuais). A flag --selftest-providers carrega a configuração, inicializa todos os provedores e sai, para que você possa validar as chaves antes de entrar em produção.

5. Faça login e crie uma chave de API. Abra http://localhost:8443/admin, entre como admin com a senha de bootstrap e crie uma chave de API. A chave em texto puro é exibida uma única vez na criação; copie-a imediatamente. As chaves carregam o prefixo sgcsk_.

6. Assine um executável de teste com curl:

curl -X POST http://localhost:8443/api/v1/sign/authenticode ^
  -H "X-API-Key: sgcsk_..." ^
  -F file=@app.exe ^
  -F provider=pfx-quickstart ^
  -F hash=sha256 ^
  --output app-signed.exe

Esse é o ciclo completo: configurar um provedor, emitir uma chave, assinar. Daqui você troca o PFX autoassinado por um certificado real, liga o TLS e tranca o firewall na sub-rede do seu CI.

Assinatura via REST

Toda requisição de assinatura é um POST com o arquivo e alguns campos de formulário. A autenticação é um único header, seja X-API-Key: sgcsk_... ou Authorization: Bearer sgcsk_.... A resposta é o artefato assinado como application/octet-stream, com o subject do signatário e a duração da assinatura retornados nos headers X-Sgcsign-Signer-Subject e X-Sgcsign-Duration-Ms.

Assinando um PDF, com motivo e localização de assinatura visíveis:

curl -X POST https://sign.acme.local:8443/api/v1/sign/pades \
  -H "X-API-Key: sgcsk_..." \
  -F file=@invoice.pdf \
  -F provider=qualified-eu \
  -F tsa_url=http://timestamp.digicert.com \
  -F reason=Approved \
  -F location="Madrid, Spain" \
  -F signer_name="Acme Billing" \
  -o invoice-signed.pdf

Assinando uma fatura XML com um perfil XAdES eIDAS:

curl -X POST https://sign.acme.local:8443/api/v1/sign/xades \
  -H "X-API-Key: sgcsk_..." \
  -F file=@invoice.xml \
  -F provider=qualified-eu \
  -F profile=eidas \
  -F xades_type=enveloped \
  -o invoice-signed.xml

Verificar uma assinatura retorna JSON em vez de um arquivo:

curl -X POST https://sign.acme.local:8443/api/v1/verify \
  -H "X-API-Key: sgcsk_..." \
  -F file=@app-signed.exe \
  -F format=authenticode

{ "valid": true, "status": "valid",
  "subject": "CN=Acme Inc., O=Acme, C=US",
  "has_timestamp": true, "timestamp_time": "2026-06-15T13:01:17.000Z" }

O cliente de linha de comando sgcsign

O servidor vem com sgcsign, uma pequena CLI multiformato com quatro verbos: sign, verify, keys e health. A URL do servidor, a chave de API e o provedor podem vir de flags ou das variáveis de ambiente SGCSIGN_SERVER, SGCSIGN_APIKEY e SGCSIGN_PROVIDER, o que mantém os segredos fora da linha de comando no CI:

set SGCSIGN_SERVER=https://sign.acme.local:8443
set SGCSIGN_APIKEY=sgcsk_...

sgcsign sign -f authenticode -p pfx-build -o app-signed.exe app.exe ^
  --tsa http://timestamp.digicert.com --desc "Acme Installer"

sgcsign verify -f authenticode app-signed.exe

Para o Authenticode há um truque que economiza largura de banda. Com o modo pré-hash (--prehash, ativado por padrão) a CLI calcula o hash do PE localmente e envia apenas o hash, algumas centenas de bytes em vez de um binário de vários megabytes. O servidor assina o hash e retorna um blob PKCS#7 detached, que o cliente incorpora no arquivo. Em um agente de CI com largura de banda limitada assinando um instalador grande, isso transforma um upload de vários megabytes em um minúsculo.

Segurança e operações

O servidor foi construído para rodar sem supervisão à frente de uma chave de assinatura real, então a superfície operacional importa tanto quanto a criptografia:

Por que auto-hospedar

O propósito do servidor é reduzir uma frota de agentes de build arriscados, portadores de chaves, a um único endpoint endurecido. A chave de assinatura reside apenas no servidor, ou no HSM ou KMS na nuvem que o servidor representa, nunca nas máquinas que executam seus scripts de build. O acesso é controlado por tokens de API por chave com cotas, restritos a projetos, e cada assinatura é registrada em um log de auditoria que você controla. Rotacionar um certificado, revogar a chave de um agente comprometido ou provar quem assinou o quê passam a ser um único lugar para verificar, em vez de muitos.

Disponibilidade

O sgcSign Server faz parte do sgcSign 2026.6.0. Ele roda como um serviço do Windows ou como uma aplicação de console, é configurado pelo único arquivo JSON mostrado acima e é controlado a partir do curl, da CLI sgcsign incluída, ou de qualquer cliente HTTP por meio de sua descrição OpenAPI.

Dúvidas, feedback ou ajuda para colocá-lo em funcionamento? Entre em contato. Você receberá uma resposta das pessoas que escreveram o código.