XML-DSig
Assine XML com signXml e valide com verifyXml: referência enveloped, X.509 no KeyInfo, verificação sem signatário.
O que você vai fazer: assinar uma string XML com signXml na camada do Signatures, depois validar com verifyXml — que não exige signatário e roda em qualquer lugar.
O formato XML vive em @signature-kit/xml e consome o serviço Signatures fornecido por um signatário.
npm install @signature-kit/xmlsignXml recebe um XmlSigningRequest e retorna Effect<string, XmlError | SignatureKitError, Signatures> — o XML assinado como string. Satisfaça o requisito Signatures com Effect.provide(a1SignaturesLayer(...)): o A1 fornece o poder de assinatura, o módulo de formato só altera o documento.
import { signXml } from "@signature-kit/xml/sign"
import { xmlRuntimeLayer } from "@signature-kit/xml/runtime"
import { a1SignaturesLayer } from "@signature-kit/a1/signer"
import { Effect, Redacted } from "effect"
const layer = a1SignaturesLayer({
pfx, // Uint8Array — bytes of the .pfx/.p12
password: Redacted.make(process.env.A1_PASSWORD ?? ""),
})
// signXml -> Effect<string, XmlError | SignatureKitError, Signatures | XmlRuntime>
const signed: string = yield* signXml({
xml, // string — input document
referenceId: "nfe-1",
}).pipe(Effect.provide(layer), Effect.provide(xmlRuntimeLayer))
// 'signed' is the signed XML, with the embedded <Signature>verifyXml não tem requisito Signatures — apenas signXml precisa da camada de assinatura.
referenceId define o alvo da assinatura enveloped: passar referenceId: "nfe-1" produz uma Reference com URI "#nfe-1", apontando para o elemento de mesmo Id no próprio documento. Os demais campos de XmlSigningRequest são opcionais:
// XmlSigningRequest — only 'xml' is required
const signed = yield* signXml({
xml,
algorithm: "rsa-sha256", // default; or "rsa-sha512"
referenceId: "nfe-1", // -> Reference URI "#nfe-1"
signatureId: "SignatureKit-NFe", // id of the <Signature> element
signingTime: new Date(),
}).pipe(Effect.provide(layer), Effect.provide(xmlRuntimeLayer))Sem algorithm, vale o padrão "rsa-sha256"; a outra opção é "rsa-sha512". O certificado X.509 do signatário é embutido no KeyInfo da assinatura, então o verificador não precisa da chave por fora — a menos que você queira fixá-la, o que o último passo abaixo cobre.
A NF-e é um caso comum: o Id de infNFe é a chave de acesso, e a assinatura aponta para ele. Use a chave de acesso como referenceId para que a URI fique "#NFe35...".
// NF-e: the id of <infNFe> is the target of the enveloped reference
const xml = `<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe35200114200166000187550010000000071234567890" versao="4.00">
<!-- ide, emit, dest, det, total, transp, ... -->
</infNFe>
</NFe>`
const signed = yield* signXml({
xml,
referenceId: "NFe35200114200166000187550010000000071234567890",
}).pipe(Effect.provide(layer), Effect.provide(xmlRuntimeLayer))
// the <Signature> is inserted as a sibling of <infNFe>, pointing to "#NFe35..."A string de saída é o XML pronto para enviar. Como signXml retorna string, jogue-a
direto no payload da SEFAZ.
verifyXml recebe um XmlVerificationRequest e retorna Effect<XmlVerificationResult, XmlError>. Não há requisito Signatures: a verificação usa o certificado embutido no KeyInfo. Use requireReferenceUri para exigir uma URI específica entre as referências assinadas.
import { verifyXml } from "@signature-kit/xml/verify"
import { xmlRuntimeLayer } from "@signature-kit/xml/runtime"
import { Effect } from "effect"
// verifyXml -> Effect<XmlVerificationResult, XmlError, XmlRuntime>
// NO Signatures requirement: runs outside the signer boundary
const result = yield* verifyXml({
xml: signed,
requireReferenceUri: "#nfe-1", // fails if the URI does not exist
}).pipe(Effect.provide(xmlRuntimeLayer))
// XmlVerificationResult
result.valid // boolean
result.signatureCount // number — how many <Signature> elements were verified
result.referenceUris // readonly string[] — e.g. ["#nfe-1"]Por padrão, a chave pública vem do certificado embutido. Para fixar a chave esperada — ignorando o KeyInfo — passe publicKeyDer (o SubjectPublicKeyInfo em DER). Útil quando você já confia em uma chave específica e não aceita a que veio no documento.
// Verification with an explicit key (instead of the cert embedded in KeyInfo)
const result = yield* verifyXml({
xml: signed,
publicKeyDer, // Uint8Array — SubjectPublicKeyInfo DER
requireReferenceUri: "#nfe-1",
})
// result.valid: booleanErros que você pode encontrar
Toda falha de assinatura é um SignatureKitError tipado no canal de erro; falhas de parsing ou canonicalização de XML chegam como XmlError. As mais comuns: