Uma assinatura digital registra quem assinou um documento. Um carimbo de tempo confiável registra quando, e esse horário vem de uma autoridade de carimbo de tempo (TSA) independente, em vez do relógio no computador do assinante. Este tutorial mostra como assinar um documento Word com uma assinatura XAdES‑T com carimbo de tempo em Python e, em seguida, confirmar que o carimbo de tempo está no arquivo.
Principais conclusões
- Defina ambos
SignOptions.xml_dsig_level = XmlDsigLevel.X_AD_ES_TeSignOptions.timestamp_settings. Qualquer um sozinho produz uma assinatura sem carimbo de tempo, e nenhum erro informa isso. - Aspose.Words solicita o carimbo de tempo durante
DigitalSignatureUtil.sign, portanto essa chamada precisa de acesso à rede ao TSA. - DOCX e DOC podem receber carimbo de tempo. Arquivos ODT não podem.
DigitalSignature.is_validverifica a assinatura, não o carimbo de tempo. Confirme o carimbo de tempo separadamente.
O que um Carimbo de Tempo Confiável adiciona a uma assinatura
Cada assinatura que o Aspose.Words cria contém um horário de assinatura, definido por SignOptions.sign_time. Esse valor vem da máquina do assinante, portanto quem contestar o documento pode contestar também o horário.
Uma assinatura XAdES‑T adiciona evidência independente. Depois que o documento é assinado, Aspose.Words envia um hash do valor da assinatura para um TSA. O TSA devolve um token RFC 3161, assinado com seu próprio certificado, que vincula o hash a um horário específico. Esse token é armazenado dentro da assinatura. Um verificador pode então demonstrar que a assinatura existia naquele momento, o que é mais importante quando o certificado de assinatura expira ou é revogado posteriormente.
Pré-requisitos
Antes de executar o exemplo, certifique-se de que você tem:
- Aspose.Words for Python via .NET 26.9 ou posterior. Instale ou atualize a partir de PyPI:
pip install --upgrade "aspose-words>=26.9"
- Um certificado de assinatura no formato PKCS#12 (
.pfxou.p12) e sua senha. - Uma URL de autoridade de carimbo de tempo. O exemplo usa o FreeTSA (
https://freetsa.org/tsr), um TSA público gratuito que é conveniente para testes. Para documentos de produção, use o TSA que sua organização ou provedor de certificados recomenda, pois as pessoas que verificam seus documentos devem confiar no certificado desse TSA. - Credenciais do TSA, somente se o seu TSA exigir autenticação.
Sem uma licença, Aspose.Words funciona no modo de avaliação com limitações. Uma licença temporária remove essas limitações enquanto você testa.
Assine um documento Word com um carimbo de tempo confiável
O script a seguir assina um arquivo DOCX com uma assinatura XAdES‑T e incorpora um carimbo de tempo do TSA.
import datetime
import aspose.words as aw
# Replace these values with your own files and credentials.
INPUT_DOC = "contract.docx"
OUTPUT_DOC = "contract-signed.docx"
CERT_FILE = "signing-cert.pfx"
CERT_PASSWORD = "your-pfx-password"
TSA_URL = "https://freetsa.org/tsr"
TSA_USER = "" # Fill in only if your TSA requires authentication.
TSA_PASSWORD = ""
# Load the signing certificate from a PKCS#12 file.
cert_holder = aw.digitalsignatures.CertificateHolder.create(
file_name=CERT_FILE, password=CERT_PASSWORD)
# Request an XAdES-T signature and point it at a timestamp authority.
sign_options = aw.digitalsignatures.SignOptions()
sign_options.xml_dsig_level = aw.digitalsignatures.XmlDsigLevel.X_AD_ES_T
sign_options.timestamp_settings = aw.digitalsignatures.DigitalSignatureTimestampSettings(
server_url=TSA_URL,
user_name=TSA_USER,
password=TSA_PASSWORD,
timeout=datetime.timedelta(seconds=60), # Default is 100 seconds.
)
# Sign the document. Aspose.Words contacts the TSA during this call.
aw.digitalsignatures.DigitalSignatureUtil.sign(
src_file_name=INPUT_DOC,
dst_file_name=OUTPUT_DOC,
cert_holder=cert_holder,
sign_options=sign_options,
)
print(f"Signed with a trusted timestamp: {OUTPUT_DOC}")
Como o Código Funciona
CertificateHolder.createlê a chave privada e a cadeia de certificados do arquivo.pfx. Uma senha incorreta falha aqui, antes de qualquer assinatura começar.XmlDsigLevel.X_AD_ES_Tindica ao Aspose.Words para construir uma assinatura XAdES‑T, que é XAdES‑EPES mais um carimbo de tempo da assinatura.DigitalSignatureTimestampSettingscontém a URL do TSA, um nome de usuário e senha opcionais, e um tempo limite opcional. Strings vazias são aceitáveis para um TSA que aceita solicitações anônimas. Se o TSA responder com um desafio de autenticação HTTP, o Aspose.Words envia as credenciais fornecidas.DigitalSignatureUtil.signgrava uma cópia assinada emOUTPUT_DOCe deixa o arquivo de entrada inalterado. Assine um documento não assinado: se a entrada já possuir uma assinatura, a saída conterá tanto a assinatura existente quanto a nova.
Verifique se o carimbo de data/hora foi incorporado
Os objetos DigitalSignature que o Aspose.Words retorna não expõem o carimbo de data/hora, e is_valid não o verifica. Nos testes, um documento cujo token de carimbo de data/hora havia sido deliberadamente corrompido ainda relatou is_valid como True. Para confirmar o carimbo de data/hora, examine o XML da assinatura armazenado no pacote DOCX:
import base64
import re
import zipfile
import aspose.words as aw
SIGNED_DOC = "contract-signed.docx"
# 1. Check the signature itself.
for sig in aw.digitalsignatures.DigitalSignatureUtil.load_signatures(SIGNED_DOC):
print(f"Signer: {sig.subject_name} | valid: {sig.is_valid}")
# 2. Check that a timestamp token was embedded, and save it for inspection.
with zipfile.ZipFile(SIGNED_DOC) as package:
for part in package.namelist():
if part.startswith("_xmlsignatures/sig") and part.endswith(".xml"):
xml = package.read(part).decode("utf-8")
match = re.search(r"<(?:\w+:)?EncapsulatedTimeStamp[^>]*>([^<]+)<", xml)
if match:
with open("timestamp-token.der", "wb") as f:
f.write(base64.b64decode(match.group(1)))
print(f"{part}: timestamp embedded (saved to timestamp-token.der)")
else:
print(f"{part}: no timestamp found")
Para um DOCX assinado, você deve ver uma saída semelhante a esta:
Signer: CN=Your Name | valid: True
_xmlsignatures/sig1.xml: timestamp embedded (saved to timestamp-token.der)
Para ler o horário certificado pela TSA, passe o token salvo para o OpenSSL:
openssl ts -reply -token_in -in timestamp-token.der -token_out -text
A linha Time stamp mostra o horário certificado em GMT, e a linha TSA identifica a autoridade que o emitiu.
Esta verificação lê o formato de pacote DOCX. Um arquivo DOC armazena sua assinatura em um contêiner binário, portanto o script baseado em ZIP não se aplica a ele.
Solucionando Erros de Assinatura e Carimbo de Tempo
| Sintoma | Causa provável | O que fazer |
|---|---|---|
| A assinatura tem sucesso, mas nenhum carimbo de tempo é incorporado | Apenas um de xml_dsig_level = X_AD_ES_T e timestamp_settings foi definido | Defina ambos antes de chamar sign. Com XML_D_SIG ou X_AD_ES_EPES, as configurações de carimbo de tempo são ignoradas. |
RuntimeError mencionando (401) Unauthorized | O TSA requer credenciais, ou as credenciais estão incorretas | Passe o nome de usuário e a senha fornecidos pelo seu provedor de TSA. |
RuntimeError mencionando uma conexão recusada ou erro de proxy | O URL do TSA está errado, ou um firewall ou proxy bloqueia a solicitação | Verifique o URL e confirme que a máquina que executa seu script pode alcançar o TSA. |
RuntimeError mencionando The operation has timed out | O TSA não respondeu dentro do tempo limite | Tente novamente, ou passe um timeout maior para DigitalSignatureTimestampSettings. |
| Um arquivo de saída vazio permanece após um erro | sign cria o arquivo de destino antes que a solicitação ao TSA falhe | Exclua o arquivo de destino antes de tentar novamente, ou escreva em um caminho temporário e renomeie após uma chamada bem‑sucedida. |
RuntimeError dizendo que carimbo de tempo não é suportado por este formato de arquivo | A entrada é um arquivo ODT | Carimbe arquivos DOCX ou DOC, ou converta para PDF e use a rota de assinatura de PDF descrita abaixo. |
No usable version of libssl was found, ou uma falha sobre um pacote ICU ausente, no Linux | O runtime .NET incluído no pacote Python precisa do OpenSSL 1.1 e de uma versão suportada do ICU | Instale o OpenSSL 1.1, ou instale um ICU suportado. Se sua aplicação não precisar de formatação específica de cultura, defina DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 para o erro de ICU. |
Alternativa: Timestamp de uma Assinatura na Saída PDF
Se os seus destinatários precisam de um PDF em vez de um arquivo Word assinado, você não precisa do DigitalSignatureUtil. Assine e adicione timestamp ao PDF ao salvá‑lo definindo PdfSaveOptions.digital_signature_details.timestamp_settings para um objeto PdfDigitalSignatureTimestampSettings. Essa abordagem está disponível há muito mais tempo que a 26.9. A referência PdfDigitalSignatureTimestampSettings inclui um exemplo completo.
Próximas Etapas
Para adicionar linhas de assinatura, assinar com uma imagem de linha de assinatura ou remover assinaturas existentes, consulte Trabalhar com Assinaturas Digitais na documentação do Aspose.Words for Python.
Perguntas Frequentes
- O que um timestamp confiável adiciona a uma assinatura digital?
Uma autoridade de timestamp (TSA) certifica o horário em que a assinatura foi feita. Esse horário provém de uma terceira parte independente, e não do relógio do computador do assinante, permitindo que um verificador demonstre que a assinatura existia antes que o certificado de assinatura expirasse ou fosse revogado. - Qual versão do Aspose.Words suporta timestamping no DigitalSignatureUtil?
A versão 26.9 do Aspose.Words for Python via .NET adicionouSignOptions.timestamp_settings,XmlDsigLevel.X_AD_ES_Te a classeDigitalSignatureTimestampSettings. Versões anteriores podem apenas aplicar timestamp a assinaturas na saída PDF. - Preciso definir tanto xml_dsig_level quanto timestamp_settings?
Sim. Definindo apenas um deles, o Aspose.Words ainda assina o documento, mas não solicita nem incorpora um timestamp, e não gera um erro. - Quais formatos de arquivo podem receber timestamp?
Arquivos DOCX e DOC assinados comX_AD_ES_Trecebem um timestamp. Assinar um arquivo ODT com timestamp gera um erro indicando que o formato não suporta timestamp. - O método is_valid confirma que o timestamp é válido?
Não.DigitalSignature.is_validverifica apenas a assinatura em si. Para confirmar o timestamp, verifique se o XML da assinatura contém um token de timestamp e inspecione o token com uma ferramenta como o OpenSSL. - O que acontece se a TSA não puder ser alcançada?
DigitalSignatureUtil.signgera umRuntimeErrorque descreve o problema de rede, autenticação ou tempo limite. O caminho de destino pode ficar como um arquivo vazio, portanto exclua‑o antes de tentar novamente.
