luisgoncalves / xades4j

A Java library for XAdES signature services
GNU Lesser General Public License v3.0
111 stars 66 forks source link

How to Manually Set DigestValue for SignaturePolicyIdentifier in xades4j #285

Closed CASV97 closed 7 months ago

CASV97 commented 7 months ago

Hello,

I'm currently working with xades4j version 1.7 to implement digital signing according to specific regulatory requirements for an electronic invoicing system. According to these requirements, I need to include a precise SHA256 hash in the DigestValue element of the SignaturePolicyIdentifier as part of the XAdES-EPES signature.

Here's a simplified version of my code for signing XML documents:

public Document signXML(Document doc, String policyIdentifierURL, String policyFileHash, String policyFilePath) throws Exception {
 try (InputStream policyDocumentStream = getClass().getResourceAsStream(politicaDeFirmaFilePath);) {
    KeyingDataProvider kp;

    log.debug("Policy Provider");
    SignaturePolicyInfoProvider policyInfoProvider = new SignaturePolicyInfoProvider() {
        @Override
        public SignaturePolicyBase getSignaturePolicy() {
            return new SignaturePolicyIdentifierProperty(new ObjectIdentifier(politicafirmaUrl),
                    policyDocumentStream).withLocationUrl(politicafirmaUrl);
        }
    };
    // Signing process code here...
}

I understand that xades4j calculates the DigestValue based on the content of the provided policy document. However, due to updates on the policy documents' hosting, I'm facing challenges where the automatically calculated hash does not match the regulatory specified hash (Quzn98x3PMbSHwbUzaj5f5KOpiH0u8bvmwbbbNkO9Es=).

Is there a way to manually set or override the DigestValue in the SignaturePolicyIdentifier using xades4j to comply with these requirements? Or any guidance on how to properly include a pre-calculated hash value for the policy file?

Any help or guidance on this matter would be greatly appreciated.

Thank you!

luisgoncalves commented 7 months ago

Hi

Currently there's no direct way to achieve this in the library. I've seen cases where people were confused about what to put in the policy digest, so I think it is a good that the library handles it. But I see your point about having an "escape hatch".

A possible workaround could be to implement a PropertyDataObjectGenerator<SignaturePolicyIdentifierProperty> and register it in XadesSigningProfile#withPropertyDataObjectGenerator(). Check DataGenSigPolicy (in the lib source code) for what to return, but you won't need any of the digest stuff. The idea is that this generator would override the built-in one, but I haven't tested it (I don't recall all the details of how the DI binding are done).

Can you give it a try?

If this doesn't work, I'd have to consider changing some bits. Perhaps SignaturePolicyInfoProvider could have an additional method to return the policy property and some indication if the library should do the digest calculation. But then SignaturePolicyIdentifierProperty#getPolicyDocumentStream becomes a weird name.. Another alternative would be a different type extending SignaturePolicyBase to represent this, which is also a bit weird... I'd need to dig a bit more, but I won't have time for it this week.

CASV97 commented 7 months ago

Thank you very much for your prompt and helpful response. I followed your suggestion and implemented my own PropertyDataObjectGenerator for SignaturePolicyIdentifierProperty as you outlined. I'm pleased to report that it worked as expected for my use case.

To test it, I used a placeholder hash value, and I was able to see that it was correctly applied in the signature process. This workaround has provided a viable solution for us to manually include the policy hash directly, bypassing the automatic digest calculation by the library.

I appreciate your guidance and the workaround you proposed. It has been instrumental in resolving the issue we faced with the policy digest. Please let me know if there's any further information I should consider or any best practices I should follow when using this approach.

Best regards, `

public class CustomSigPolicyGenerator implements PropertyDataObjectGenerator { private String badHash = "oeZb1zSi24qWQEJBklyxMKpljO/4b1S3Ty5ZqJBVRIE=";

@Override
public PropertyDataObject generatePropertyData(SignaturePolicyIdentifierProperty prop,
        PropertiesDataGenerationContext ctx) throws PropertyDataGenerationException {
    // Aquí puedes crear tu SignaturePolicyData personalizado, basado en la
    // instancia prop
    // y usar el hash directamente.
    byte[] decodedHash = Base64.getDecoder().decode(badHash);
    SignaturePolicyData sigPolicyData = new SignaturePolicyData(prop.getIdentifier(),
            "http://www.w3.org/2001/04/xmlenc#sha256",decodedHash,
            "https://www.batuz.eus/fitxategiak/batuz/ticketbai/sinadura_elektronikoaren_zehaztapenak_especificaciones_de_la_firma_electronica_v1_0.pdf");

    return sigPolicyData;
}

} XadesSigningProfile p = new XadesEpesSigningProfile(kp, policyInfoProvider).withPropertyDataObjectGenerator( SignaturePolicyIdentifierProperty.class, new CustomSigPolicyGenerator());`

luisgoncalves commented 7 months ago

I'm glad it worked out.

The code looks good. The only detail I see is that you could get the "location URL" also from prop.