PeculiarVentures/xadesjs

Signature with file attachments

cholokla opened this issue ยท 10 comments

Hello,
I would like to sign XML document with references to the file attachments using browser.

Attachments are:
https://i.picsum.photos/id/360/536/111.jpg
https://i.picsum.photos/id/237/536/555.jpg

If I use dedicated signing software I can open XML file to sign, select attachments and result will contain file names with digests for every file, as shown below.

<xml> <test>123</test> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="ID-94100763-8da0-4023-b13f-58d9d046c244"> <ds:SignedInfo Id="ID-daa7f869-4d1c-415c-afbc-eee64c9d421d"> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <ds:Reference Id="ID-f6a916f3-b195-45b0-8c81-99b4a5b121d4" URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath xmlns="http://uri.etsi.org/01903/v1.3.2#">not(ancestor-or-self::ds:Signature)</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>9nRkKviccL1aDWE6kLNsrUA9EZQoIEJfYv3Vm4cIaYg=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-1bd339a0-9a8b-49e8-8afc-13919c54304d" URI="111.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>A75NtFdNywCxe/W2KPL2XeP0iOUyWZy072xZKBDhCZs=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-e9cc3262-30b7-49e8-b8cb-f71857eb5220" URI="555.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>KXrbTTYzL3WDh0Ehb+OU+eDG2lkpkpA1uoA/FODtno8=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-67c3385a-d048-48a3-b323-243eb10f8395" Type="http://uri.etsi.org/01903#SignedProperties" URI="#ID-717a243b-71cc-4173-ae07-f680ddf48f76"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>8o2Lv+QVZ49bSr/iyPQB5leJXwwKrrejA76e6AwElrQ=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> ...

With your library I can add OptionsSignReference for every file (have file URL's) but the digest is not calculated (in fact it's the same as in the main element).

<xml> <test>456</test> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="id-5a1ba3578966"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <ds:Reference URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-0" URI="111.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-1" URI="555.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#xades-id-5a1ba3578966" Type="http://uri.etsi.org/01903#SignedProperties"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>d4EJq8Ojca8oXyspPQ0QCOk6UArBU/GvV2uKOa+cDOY=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> ...

Is there a possiblity to automatically calculate digests for every file using http uri's or at least manually specify digests for every reference?

Regards,
cholo

Please try an updated version of xmldsigjs@next

See working example PeculiarVentures/xmldsigjs#44

Let me know if it works. I'll publish production version

Let me know if it works. I'll publish production version

Thanks for the extremely fast response :)
Could you please give me any clue how to download file via url on the frontend (Chrome/Firefox/IE11) and pass to the "crypto.subtle.digest" function - is that even possible?

Yeah, it seems to work perfectly, now I can calculate digest and set in the reference.
Please, update xadesjs library too.

done

xmldsigjs@2.0.28 is available

You can use a new version of xmldsigjs with the current version of xadesjs

image

Maybe I'm doing something wrong, but for XmlDSigJs I'm getting proper digests:

<ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <ds:Reference Id="ID-99e50a74-2b6b-433b-8ccd-570e50b8b337" URI="111.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>A75NtFdNywCxe/W2KPL2XeP0iOUyWZy072xZKBDhCZs=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-f8977bf2-aec7-42fc-8f08-e32b77f7c05c" URI="555.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>KXrbTTYzL3WDh0Ehb+OU+eDG2lkpkpA1uoA/FODtno8=</ds:DigestValue> </ds:Reference> <ds:Reference URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> </ds:SignedInfo>

but if I use XAdES - digests are the same:

<ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <ds:Reference Id="ID-f45f7041-457c-427d-99e4-669c69d83e49" URI="111.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> <ds:Reference Id="ID-a4dfcc6c-b9bf-47cd-8b88-0c2be88a28f4" URI="555.jpg"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> <ds:Reference URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>dGJZXYeN0uqOG5NB6O8UjKvQ1GHMtS7my/2Qm7QgDkw=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#xades-id-5ecc4f25a357" Type="http://uri.etsi.org/01903#SignedProperties"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>xgfLJoBdz27ne0Zz1d3f8viHYfsYatYYhQ6EONljQPI=</ds:DigestValue> </ds:Reference> </ds:SignedInfo>

Here is my code:

`function prepareSignDocumentsPromises() {
var x509Cert = stripPemHeaderFromCertificate(cryptoKey.cert);
var promises = []

for (var i = 0; i < _documents.length; ++i) {

    if (_documents[i].isBase64) {
        _documents[i].body = decodeB64(_documents[i].body);
        _documents[i].isBase64 = false;
    }

    var referencesPromises = [];

    if (_documents[i].attachmentsUris) {
        for (var j = 0; j < _documents[i].attachmentsUris.length; ++j) {
            (function(attachment) {
                var refPromise = downloadAsBuffer(attachment).then(function(buffer) {
                    return crypto.subtle.digest("SHA-256", new Uint8Array(buffer)).then(function(digest) {
                        var ref = new XmlDSigJs.Reference();
                        var uri = attachment.replace(/\\/g, '/').substring((attachment.lastIndexOf('/') + 1), attachment.length);
                        ref.Id = "ID-" + uuidv4();
                        ref.Uri = uri;
                        ref.DigestMethod.Algorithm = XmlDSigJs.SHA256_NAMESPACE;
                        ref.DigestValue = new Uint8Array(digest);
                        return ref;
                    });
                });

                referencesPromises.push(refPromise);

            })(_documents[i].attachmentsUris[j]);
        }
    }

    (function(document) {

        promises.push(Promise.all(referencesPromises).then(function(references) {

            var xml = new XmlDSigJs.SignedXml();
            // var xml = new XAdES.SignedXml();

            for (var j = 0; j < references.length; ++j) {
                xml.XmlSignature.SignedInfo.References.Add(references[j]);
            }

            signedXml.push(xml);

            return xml.Sign(
               { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } },
               cryptoKey.privateKey,
               XmlDSigJs.Parse(document),
               {
                   keyValue: cryptoKey.publicKey,
                   references: [ { hash: "SHA-256", transforms: ["enveloped"] } ],
                   x509: [x509Cert],
                   signingCertificate: x509Cert
               }
           );
        }));

    })(_documents[i].body);
}

return promises;

}`

@cholokla Please double-check deps

npm list xmldsigjs

It must be

โ”œโ”€โ”ฌ xadesjs@2.0.16
โ”‚ โ””โ”€โ”€ xmldsigjs@2.0.28  deduped
โ””โ”€โ”€ xmldsigjs@2.0.28 

Otherwise you've got 2 different versions of xmldsigjs

I'm signing inside the html page in the browser so I have:

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.7.0/polyfill.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/node-forge@0.7.0/dist/forge.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/xadesjs@2.0.16/build/xades.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/xmldsigjs@2.0.28/build/xmldsig.min.js" type="text/javascript"></script>

I've even downloaded libs via npm and included as a static content on the page but same problem occurs when I use "new XAdES.SignedXml()" to create signing object.
For "new XmlDSigJs.SignedXml()" everything works perfect.

I see. The problem is that xadesjs bundle includes xmldisgjs

Please try a new version of xadesjs@2.0.17. Just published it

Wow, great, it's working now.
Best tech support ever!!! :D