12 de junio de 2026
WSAA error "Firma inválida o algoritmo no soportado" (cms.sign.invalid) desde Node.js
AFIP rechaza el login con faultcode ns1:cms.sign.invalid cuando el CMS/PKCS#7 está firmado DETACHED. El WSAA exige firma ATTACHED (contenido embebido, SHA-256). Con node-forge, setear p7.content antes de llamar p7.sign() — nunca pasar {detached:true}. Eso resuelve el error completamente.
Fault verbatim capturado de producción
SOAP Fault:
faultcode: ns1:cms.sign.invalid
faultstring: Firma inválida o algoritmo no soportado
host: wsaa3.intranet.afip.gob.arEste fault es la respuesta HTTP 500 que devuelve el endpoint SOAP de WSAA cuando rechaza el LoginTicketRequest (TRA) que le enviamos.
Por qué ocurre
El TRA debe empaquetarse como un mensaje CMS (PKCS#7) en formato signed-data ATTACHED — el contenido del TRA queda embebido dentro del blob CMS.
Algunos wrappers y SDKs de Node.js firman en modo DETACHED por defecto: el blob CMS solo contiene la firma, sin el contenido. Ese formato es válido en muchos contextos (código, documentos) pero el WSAA de AFIP no lo acepta y devuelve ns1:cms.sign.invalid.
Otras causas del mismo faultcode:
- Algoritmo de hash incorrecto (MD5 en lugar de SHA-256).
- Certificado expirado o no vinculado al servicio wsfe en el portal de ARCA.
Fix con node-forge
const forge = require('node-forge');
function signTRA(traXml, certPem, keyPem) {
const p7 = forge.pkcs7.createSignedData();
// CRÍTICO: setear content ANTES de sign() → produce firma ATTACHED
p7.content = forge.util.createBuffer(traXml, 'utf8');
p7.addCertificate(certPem);
p7.addSigner({
key: forge.pki.privateKeyFromPem(keyPem),
certificate: forge.pki.certificateFromPem(certPem),
digestAlgorithm: forge.pki.oids.sha256, // SHA-256 requerido
authenticatedAttributes: [],
});
// NO pasar { detached: true } — ese flag genera firma sin contenido
p7.sign();
return forge.util.encode64(
forge.asn1.toDer(p7.toAsn1()).getBytes()
);
}El base64 resultante es el parámetro in del LoginCms.