Esta es una función que me he montado en una DLL externa para firmar los XML con las Chilkat, por si te sirve de ayuda. Me base en los posts de Galaxian, aunque haciendo ciertas modificaciones porque tampoco me iban, aparte de que hay que retocar direcciones por vienen, por ejemplo, con h_t_t_p y no http.
Código PHP:
function Firma_CLK(var Hacienda: WideString; var Pfx: WideString; var Password: WideString; var Fichero: WideString): boolean; stdcall;
var
xmlToSign: HCkXml;
gen: HCkXmlDSigGen;
object1: HCkXml;
cert: HCkCert;
sbXml: HCkStringBuilder;
begin
// Load the XML to be signed from a file...
xmlToSign := CkXml_Create();
try
CkXml_LoadXmlFile(xmlToSign, PWideChar(Fichero));
gen := CkXmlDSigGen_Create();
CkXmlDSigGen_putBehaviors(gen, 'TransformSignatureXPath, LocalSigningTime, IndentedSignature'); // Añadido LocalSigningTime para que salga la hora como con Autofirma
CkXmlDSigGen_putSigLocation(gen, 'T:TicketBai');
CkXmlDSigGen_putSigLocationMod(gen, 0);
CkXmlDSigGen_putSigId(gen, 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-Signature');
CkXmlDSigGen_putSigValueId(gen, 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-SignatureValue');
CkXmlDSigGen_putSigNamespacePrefix(gen, 'ds');
CkXmlDSigGen_putSigNamespaceUri(gen, 'http://www.w3.org/2000/09/xmldsig#'); // Aquí faltaban las '//' despues de http:
CkXmlDSigGen_putSignedInfoCanonAlg(gen, 'C14N');
CkXmlDSigGen_putSignedInfoDigestMethod(gen, 'sha512'); // Cambiado a sha512, en el ejemplo viene sha256
// Set the KeyInfoId before adding references..
CkXmlDSigGen_putKeyInfoId(gen, 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-KeyInfo');
// Create an Object to be added to the Signature.
object1 := CkXml_Create();
CkXml_putTag(object1, 'xades:QualifyingProperties');
CkXml_AddAttribute(object1, 'Id', 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-QualifyingProperties');
CkXml_AddAttribute(object1, 'Target', '#Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-Signature');
CkXml_AddAttribute(object1, 'xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#');
CkXml_AddAttribute(object1, 'xmlns:xades', 'http://uri.etsi.org/01903/v1.3.2#');
CkXml_UpdateAttrAt(object1, 'xades:SignedProperties', True, 'Id', 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-SignedProperties');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime', 'TO BE GENERATED BY CHILKAT');
CkXml_UpdateAttrAt(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:CertDigest|ds:DigestMethod', True, 'Algorithm','http://www.w3.org/2001/04/xmlenc#sha512');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:CertDigest|ds:DigestValue', 'TO BE GENERATED BY CHILKAT');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:IssuerSerial|ds:X509IssuerName', 'TO BE GENERATED BY CHILKAT');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:IssuerSerial|ds:X509SerialNumber', 'TO BE GENERATED BY CHILKAT');
// Según la Hacienda Foral
if Hacienda = '01' then // Araba
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyId|xades:Identifier', 'https://ticketbai.araba.eus/tbai/sinadura/')
else if Hacienda = '20' then // Gipuzkoa
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyId|xades:Identifier', 'https://www.gipuzkoa.eus/ticketbai/sinadura')
else if Hacienda = '48' then // Bizkaia
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyId|xades:Identifier', 'https://www.batuz.eus/fitxategiak/batuz/ticketbai/sinadura_elektronikoaren_zehaztapenak_especificaciones_de_la_firma_electronica_v1_0.pdf');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyId|xades:Description', '');
CkXml_UpdateAttrAt(object1,'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyHash|ds:DigestMethod',True,'Algorithm','http://www.w3.org/2001/04/xmlenc#sha256');
// Según la Hacienda Foral
if Hacienda = '01' then // Araba
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyHash|ds:DigestValue', 'iOgvkX7/yHIDRRiPy/LYQ0UUn7QV8/11D1BFbs8yMuQ=')
else if Hacienda = '20' then // Gipuzkoa
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyHash|ds:DigestValue', '6NrKAm60o7u62FUQwzZew24ra2ve9PRQYwC21AM6In0=')
else if Hacienda = '48' then // Bizkaia
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedSignatureProperties|xades:SignaturePolicyIdentifier|xades:SignaturePolicyId|xades:SigPolicyHash|ds:DigestValue', 'Quzn98x3PMbSHwbUzaj5f5KOpiH0u8bvmwbbbNkO9Es=');
CkXml_UpdateAttrAt(object1, 'xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat', True, 'ObjectReference', '#Reference-7e6f3481-4acc-47de-90fd-67878ad15e8e');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:Description', '');
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:MimeType', 'application/octet-stream'); // Cambio a octet-stream como Autofirma
CkXml_UpdateChildContent(object1, 'xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:Encoding', '');
CkXmlDSigGen_AddObject(gen, '', CkXml__getXml(object1), '', '');
// -------- Reference 1 --------
CkXmlDSigGen_AddSameDocRef(gen, '', 'sha512', 'C14N', '', 'http://www.w3.org/2000/09/xmldsig#Object');
CkXmlDSigGen_SetRefIdAttr(gen, '', 'Reference-7e6f3481-4acc-47de-90fd-67878ad15e8e');
// -------- Reference 2 --------
CkXmlDSigGen_AddObjectRef(gen, 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-SignedProperties', 'sha512', '', '', 'http://uri.etsi.org/01903#SignedProperties');
// -------- Reference 3 --------
CkXmlDSigGen_AddSameDocRef(gen, 'Signature-63c35f38-2b5f-4600-b3da-3ddee86d62b3-KeyInfo', 'sha512' , '', '', '');
// Load a certificate that has been pre-installed on the Windows system
// This includes certificates on smartcards and USB tokens
cert := CkCert_Create();
result := CkCert_LoadPfxFile(cert, PWideChar(Pfx), PWideChar(Password));
if (not result) then
begin
ShowMessage('Fallo al cargar el certificado PFX: '+Pfx+' / '+Password);
Exit;
end;
CkXmlDSigGen_SetX509Cert(gen, cert, True);
// Cambiado Certificate a CertChain para que salgan todos los certificados incluidos,
// porque en certificados como los de UANATACA se incluyen varios certificados en x509Certificate
// y con Certificate sólo aparece el primero, que no tiene porque ser el que necesitamos
CkXmlDSigGen_putX509Type(gen,'CertChain');
CkXmlDSigGen_putKeyInfoType(gen,'X509Data+KeyValue');
// Load XML to be signed...
sbXml := CkStringBuilder_Create();
CkXml_GetXmlSb(xmlToSign, sbXml);
// Sign the XML...
result := CkXmlDSigGen_CreateXmlDSigSb(gen, sbXml);
if (not result) then
begin
ShowMessage('Fallo en la firma');
Exit;
end;
// Save the signed XML to a file.
// Utilizo el mismo fichero de entrada
// El último parámetro es False (no BOM o true BOM)
result := CkStringBuilder_WriteFile(sbXml, PWideChar(Fichero), 'utf-8', False);
except
ShowMessage('Error de excepcion');
result := False;
end;
end;
|