Pular para o conteúdo
ecdsa.com
← Todos os artigos

Authenticode: chaves, carimbos do tempo e verificação

Assinamos um pequeno executável do Windows com o osslsigncode 2.14 e um carimbo do tempo da DigiCert e depois o submetemos a três testes: uma cópia assinada sem carimbo do tempo, um byte de código alterado e dados escondidos na tabela de certificados. As regras vêm dos Code Signing Baseline Requirements 3.11.0 do CA/Browser Forum e da própria documentação da Microsoft, conferidas em outubro de 2026.

Resposta curta. O Authenticode embute um SignedData PKCS #7 em uma tabela no fim de um arquivo PE do Windows; a assinatura cobre um hash do arquivo que deixa de fora o checksum, o ponteiro para essa tabela e a própria tabela. Desde 1º de junho de 2023, a chave de um certificado de assinatura de código publicamente confiável precisa ser gerada e usada em um módulo de hardware (um token, um HSM local ou um serviço de HSM em nuvem com logs de auditoria completos), então o conhecido arquivo .pfx no servidor de build já não serve para obter um certificado. Os certificados emitidos desde 1º de março de 2026 duram no máximo 460 dias, e o Artifact Signing da Microsoft emite certificados que duram 72 horas, o que faz do carimbo do tempo RFC 3161 a parte que mantém a assinatura válida: assine sempre com /fd SHA256 /tr <TSA URL> /td SHA256. Certificados EV já não garantem reputação imediata no SmartScreen. Verifique com signtool verify /pa /tw no Windows ou com osslsigncode verify em outros sistemas.

O que é assinado

Um arquivo PE (.exe, .dll, .sys) tem um diretório de dados no cabeçalho opcional; a entrada 4, a tabela de certificados (Certificate Table), guarda o deslocamento no arquivo e o tamanho de uma lista de estruturas WIN_CERTIFICATE anexadas ao fim do arquivo. No Authenticode, a entrada tem a revisão 0x0200 e o tipo WIN_CERT_TYPE_PKCS_SIGNED_DATA (0x0002) e contém um SignedData PKCS #7 (formato PE).

O conteúdo assinado não é o arquivo, e sim um SpcIndirectDataContent que contém o hash do arquivo. Esse hash não pode cobrir a própria assinatura; por isso, nas palavras da Microsoft, ele “precisa excluir certos campos da imagem PE, como o checksum e a entrada da tabela de certificados nos diretórios de dados do cabeçalho opcional”, além da própria tabela de certificados. Todo o resto (cabeçalhos, seções e dados depois da última seção) entra no cálculo do hash.

Nossa amostra é um programa de 1,6 MB compilado de forma cruzada com o Go 1.27.3 (GOOS=windows GOARCH=amd64). Um pequeno script Python lê a entrada da tabela de certificados antes e depois da assinatura:

python3 pe.py hello.exe show; python3 pe.py hello-signed.exe show
file size 1651712, certificate table at offset 0, size 0
file size 1660952, certificate table at offset 1651712, size 9240
WIN_CERTIFICATE: dwLength 9240, wRevision 0x0200, wCertificateType 0x0002

Os 9.240 bytes são a assinatura, a cadeia de certificados e o token de carimbo do tempo. Listar os identificadores de objeto da assinatura extraída mostra as partes específicas do Authenticode ao lado das partes CMS habituais:

openssl asn1parse -inform DER -in sig.der | grep -o 'OBJECT *:[^ ]*' | sort | uniq -c (trecho)
      1 OBJECT            :1.3.6.1.4.1.311.2.1.11
      1 OBJECT            :1.3.6.1.4.1.311.2.1.12
      1 OBJECT            :1.3.6.1.4.1.311.2.1.15
      2 OBJECT            :1.3.6.1.4.1.311.2.1.4
      1 OBJECT            :1.3.6.1.4.1.311.3.3.1
      2 OBJECT            :id-smime-ct-TSTInfo
      2 OBJECT            :messageDigest
      2 OBJECT            :signingTime

1.3.6.1.4.1.311.2.1.4 é o SpcIndirectDataContent; …2.1.15, os dados da imagem PE dentro dele; …2.1.11 e …2.1.12, o tipo de declaração e o nome e a URL do programa; e 1.3.6.1.4.1.311.3.3.1, o token de carimbo do tempo RFC 3161 guardado como atributo não assinado, cujo tipo de conteúdo é id-smime-ct-TSTInfo.

Certificados e chaves em 2026

Os certificados de assinatura de código publicamente confiáveis seguem os Code Signing Baseline Requirements (CSBR) do CA/Browser Forum, versão 3.11.0, de 16 de junho de 2026. As regras que mudam o seu processo de build:

  • Chaves em hardware para todos. “A partir de 1º de junho de 2023”, a chave privada do titular do certificado precisa ser gerada, armazenada e usada em um módulo criptográfico de hardware com certificação mínima FIPS 140-2 Nível 2 ou Common Criteria EAL 4+ (§6.2.7.4.1). As opções permitidas são um módulo de hardware que você mesmo opera, um serviço de chaves em nuvem cujo HSM atenda a esse requisito e cuja conta (subscription) esteja “configurada para registrar todos os acessos, operações e alterações de configuração”, ou um serviço de assinatura. A CA precisa verificar isso, por exemplo, enviando um token com uma chave pré-gerada ou conferindo um atestado da chave (§6.2.7.4.2). Antes de 2023, isso valia só para certificados EV.
  • Tamanhos de chave. Chaves RSA de pelo menos 3072 bits, ou ECDSA em P-256, P-384 ou P-521 (§6.1.5.2).
  • Vida útil. No máximo 460 dias para certificados emitidos a partir de 1º de março de 2026; 39 meses antes disso (§6.3.2).
  • Autoridades de carimbo do tempo. Os certificados de TSA duram no máximo 135 meses, e as chaves deles podem ser usadas por 15 meses após o notBefore; tokens de carimbo do tempo com SHA-1 estão proibidos desde abril de 2022.

A Microsoft acrescenta camadas próprias por cima disso. Desde o Windows 10 versão 1607, drivers de modo kernel precisam ser assinados pela Microsoft por meio do Windows Hardware Dev Center, então um certificado comercial sozinho não faz um driver carregar (política de assinatura de drivers). E EV já não significa “sem aviso do SmartScreen”: a página status dos recursos de distribuição da Microsoft diz que “nenhum tipo de certificado (OV ou EV) concede um bypass imediato” desde a mudança de 2024 no Trusted Root Program; a reputação se acumula por hash de arquivo e por identidade de assinatura.

O serviço gerenciado da Microsoft, o Artifact Signing (antigo Trusted Signing), mantém as chaves em HSMs operados pela Microsoft e emite certificados que “são renovados diariamente e são válidos por apenas 72 horas” (gerenciamento de certificados). Só o digest sai da sua máquina de build. Com certificados de vida tão curta, uma assinatura sem carimbo do tempo não serve para nada três dias depois. Dois detalhes da mesma página: como o certificado muda todo dia, cada perfil de certificado recebe um EKU personalizado e permanente sob 1.3.6.1.4.1.311.97., ao qual se pode fixar a confiança (por exemplo, em políticas de controle de aplicativos); e as CAs do serviço estão sendo removidas do CCADB e já não fazem parte do Microsoft Trusted Root Program, mas “permanecem na Windows Platform Trust List”, de modo que o Windows continua confiando nelas, enquanto outras plataformas podem não confiar.

Assinatura e verificação com o osslsigncode

O osslsigncode implementa o Authenticode no Linux e no macOS. O laboratório (pacote Debian 2.14 em debian:forky-20261005-slim) criou uma hierarquia de teste privada (uma raiz, uma CA emissora e um certificado de assinatura de código RSA 3072 com o EKU codeSigning), assinou o programa e pediu um carimbo do tempo à TSA pública da DigiCert:

lab.sh (trecho): assinar com carimbo do tempo RFC 3161 e verificar
osslsigncode sign -certs chain.pem -key codesign.key -h sha256 -n "Hello" -i "https://example.test/hello" \
  -ts http://timestamp.digicert.com -in hello.exe -out hello-signed.exe
osslsigncode verify -CAfile root.pem -TSA-CAfile /etc/ssl/certs/ca-certificates.crt -in hello-signed.exe

Só um hash da assinatura vai para a TSA. A verificação, com as listagens repetidas da cadeia encurtadas:

osslsigncode verify (resumido)
Message digest algorithm  : SHA256
Current message digest    : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 
Calculated message digest : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 

Signer's certificate:
	------------------
	Signer #0:
		Subject: CN=Example Software GmbH,O=Example Software GmbH,C=DE
		Issuer : CN=Example Test Code Signing CA,O=Example Test
		Serial : 20ADC33D7219F573885B44765077E96312F72F6D
		Certificate expiration date:
			notBefore : Oct 22 06:32:28 2026 GMT
			notAfter : Nov 21 06:32:28 2026 GMT

Authenticated attributes:
	Signing time: Oct 22 06:32:28 2026 GMT
	Microsoft Individual Code Signing purpose
	Message digest: DE669CE86A064FAEB4DFE8E43E14090CE5912FA036C527C13D5558B0BF110BB7 
	URL description: https://example.test/hello
	Text description: Hello

Countersignatures:
	Timestamp time: Oct 22 06:32:29 2026 GMT
	Signing time: Oct 22 06:32:29 2026 GMT
	Hash Algorithm: sha256
	Issuer: CN=DigiCert Trusted G4 TimeStamping RSA4096 SHA256 2025 CA1,O=DigiCert\, Inc.,C=US
	Serial: 084FDC334F7E454EDBC30F8FF9921835
...
Timestamp Server Signature CRL verification: ok
Timestamp Server Signature verification: ok
Signature verification time: Oct 22 06:32:29 2026 GMT
...
Signature verification: ok

Number of verified signatures: 1
Succeeded

“Current message digest” é o hash armazenado na assinatura, e “Calculated”, o hash recalculado a partir do arquivo. O “Signing time” do próprio signatário é só o que o relógio do signatário afirma; o “Timestamp time” é assinado pela TSA, e é nele que os verificadores se baseiam. Em seguida, o osslsigncode validou a assinatura no momento do carimbo do tempo, que é o objetivo de toda essa construção.

Por que o carimbo do tempo importa

Para mostrar isso, o laboratório assinou o mesmo programa uma segunda vez sem -ts e depois verificou os dois arquivos como se fosse 60 dias depois, quando o certificado de teste de 30 dias já tinha expirado (osslsigncode verify -time):

Verificação na data atual + 60 dias (linhas filtradas, local do erro do OpenSSL encurtado)
hello-signed.exe   Signature verification time: Oct 22 06:32:29 2026 GMT Signature verification: ok Succeeded
hello-nots.exe     ...:PKCS7_verify:certificate verify error:...:Verify error: certificate has expired Signature verification time: Dec 21 06:32:29 2026 GMT Error: certificate has expired ... Signature verification: failed Failed

Com o carimbo do tempo, a verificação acontece no momento comprovado da assinatura, de modo que a assinatura sobrevive ao certificado de assinatura (nesta execução, o certificado do respondedor de carimbo do tempo da DigiCert, por sua vez, é válido até novembro de 2037). Sem ele, a verificação usa a hora atual e falha no dia em que o certificado expira, a mesma falha certificate has expired de qualquer cliente TLS. A Microsoft vai além: se o carimbo do tempo cair dentro dos períodos de validade tanto do certificado de assinatura quanto do certificado da TSA, a assinatura é válida, e “continua válida muito depois de o certificado de assinatura e o certificado da TSA expirarem (a menos que algum deles seja revogado)”.

A revogação também depende do tempo. Os CSBR permitem que a CA defina o revocationDate de um certificado de assinatura de código como a data em que ele passou a ser considerado inválido, que no caso de comprometimento da chave é “a data mais antiga de suspeita de comprometimento”, e dizem que “convém que o software continue tratando como válidos os objetos que contêm um carimbo do tempo com data anterior à data de revogação” (§4.9.6). Portanto, uma chave vazada invalida as assinaturas com carimbo do tempo posterior ao comprometimento, e não todas as versões que você já publicou.

O que quebra uma assinatura e o que não quebra

Duas modificações, verificadas com o mesmo comando:

Um byte de código alterado (deslocamento 4096), depois 64 bytes acrescentados dentro da tabela de certificados
### one byte of code changed
Signature verification: failed
Message digest algorithm  : SHA256
Current message digest    : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 
Calculated message digest : E754D0A7C1AB0CA1716866C67046BA54D2C195CDCBA4A182D56DD5C57FCD37B6     MISMATCH!!!
Failed
### extra bytes inside the certificate table
appended 64 bytes inside the certificate table: size 9240 -> 9304
Warning: invalid PE checksum
Calculated message digest : ADEAAA5366F711E064A9D54630D1E45520727730ECA3B85423D7570B0D41F7A7 
Timestamp Server Signature verification: ok
Signature verification: ok
Succeeded

No segundo arquivo, a entrada da tabela de certificados e o comprimento do WIN_CERTIFICATE aumentaram em 64 bytes, e esse espaço recebeu dados. Como a tabela fica fora do hash, a assinatura continua passando na verificação; só o checksum PE, também excluído, fica errado. É esse layout que está por trás da CVE-2013-3900: instaladores que leem a configuração do fim do próprio arquivo podiam receber o payload de um atacante sem que a assinatura fosse invalidada. A Microsoft corrigiu o problema em 2013 com uma configuração opcional do Registro, EnableCertPaddingCheck, e a republicação de 2022 diz que a empresa “não planeja impor como padrão o comportamento de verificação mais rigoroso”. A CISA incluiu a CVE no catálogo Known Exploited Vulnerabilities em janeiro de 2022. A lição para desenvolvedores: nunca leia dados da área da tabela de certificados do seu próprio executável e ative EnableCertPaddingCheck nas máquinas que você administra.

No Windows: signtool

O SignTool, do Windows SDK, é a implementação de referência. As versões atuais exigem que os algoritmos de digest sejam informados explicitamente: “O comando sign do SignTool exige a opção de algoritmo de digest do arquivo (/fd) e a opção de algoritmo de digest do carimbo do tempo (/td)” (SignTool). Com o certificado no repositório de certificados do Windows e a chave em um token, os comandos habituais são:

signtool (da documentação da Microsoft; não executado aqui)
signtool sign /n "Example Software GmbH" /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /d "Hello" MyApp.exe
signtool verify /pa /tw /v MyApp.exe
  • /n ou /sha1 seleciona o certificado; a chave fica no token ou no HSM, por trás do respectivo CSP ou KSP (/csp e /kc os indicam explicitamente).
  • /tr consulta um servidor RFC 3161; o /t, mais antigo, usa o protocolo legado de carimbo do tempo do Authenticode.
  • verify /pa usa a política padrão do Authenticode; sem essa opção, o SignTool aplica a política de drivers e rejeita assinaturas comuns. /tw emite um aviso quando uma assinatura não tem carimbo do tempo, algo que deve fazer parte de todo pipeline de release.
  • /dlib e /dmdf conectam uma biblioteca que assina o digest remotamente; o Artifact Signing fornece uma, e o Jsign, de código aberto (Apache-2.0, versão 7.6), faz o mesmo a partir de qualquer sistema operacional com AWS KMS, Google Cloud KMS, Azure Key Vault, tokens PKCS #11 e YubiKeys.

Do lado da verificação, o Get-AuthenticodeSignature do PowerShell retorna o status e o signatário, e a guia “Assinaturas Digitais” do Explorador de Arquivos do Windows mostra o carimbo do tempo.

Assinatura em um pipeline de build

Com as chaves presas ao hardware, o servidor de build não guarda mais um arquivo de chave. Três configurações funcionam:

  1. Um serviço de chaves em nuvem: gere a chave no AWS KMS, no Google Cloud HSM ou no Azure Key Vault Premium, obtenha o certificado de uma CA que aceite o atestado de chave ou o relatório de auditoria desse serviço e assine o digest remotamente com o Jsign ou com uma ferramenta do fornecedor. A exigência de logs de auditoria faz parte dos CSBR; o artigo assinatura com KMS em nuvem mostra quais logs vêm desativados por padrão.
  2. Um serviço de assinatura gerenciado, como o Artifact Signing ou o serviço da própria CA: ele guarda a chave e o certificado, e você envia digests.
  3. Um token em um host de assinatura dedicado, acessível apenas a partir do job de release. É simples, mas agora uma única máquina fica no seu caminho crítico.

Em todos os casos, assine apenas artefatos de release, a partir de um branch protegido, com carimbo do tempo, e registre o hash do arquivo e a identidade de assinatura nas notas de versão, para que usuários e ferramentas de EDR possam conferir o que você publicou.

Erros comuns

  • Sem carimbo do tempo. Tudo funciona até o certificado expirar; a partir daí, todas as cópias instaladas mostram uma assinatura inválida. Com certificados de 72 horas ou de 460 dias, esse dia chega logo.
  • SHA-1 em qualquer lugar. Antes, sem /fd, o padrão era SHA-1; o SignTool atual se recusa a assinar sem essa opção, mas scripts antigos copiados de fóruns ainda contêm /fd sha1 ou /t.
  • Política de verificação errada. signtool verify sem /pa informa que uma assinatura comercial válida não é confiável.
  • Um certificado sem o EKU codeSigning, ou um certificado TLS usado de improviso. Os verificadores o rejeitam, assim como o OpenSSL informa unsuitable certificate purpose.
  • Certificado intermediário ausente. Inclua a cadeia completa na assinatura (-certs chain.pem ou o intermediário da CA instalado no host de assinatura); caso contrário, a verificação depende do que a máquina do usuário tiver por acaso.
  • Esperar que o EV silencie o SmartScreen. Agora a reputação se constrói com o tempo; assine todas as versões com a mesma identidade para que cada nova versão herde a reputação acumulada.
  • Ler dados do fim do seu próprio arquivo assinado (instaladores, arquivos autoextraíveis): essa área não é coberta pela assinatura.

Fontes