jsign icon indicating copy to clipboard operation
jsign copied to clipboard

Missing APIs to write content to PE signature entries in Jsign 7.0

Open mduft opened this issue 1 year ago • 8 comments

We are using jsign to implement signature stuffing for installers (i.e. embedding additional branding information in the signature section of a binary while maintaining a properly signed state for the rest of the actual executable. Many installers do this - that's why MS decided to intentionally not forbid this by default in windows). This is the code we use:

    /**
     * Embeds the given bytes into given signed PE/COFF executable.
     */
    public static void embed(Path executable, byte[] data) throws IOException {
        try (PEFile pe = new PEFile(executable.toFile())) {

            List<CMSSignedData> signatures = pe.getSignatures();
            if (signatures.isEmpty()) {
                throw new IllegalStateException("Only signed executables can be modified.");
            }

            // we only support a single top level signature.
            CertificateTableEntry topSignature = new CertificateTableEntry(signatures.get(0));
            byte[] signature = topSignature.toBytes();

            // append data right after each other
            // bitwise round up to next multiple of 8
            byte[] bytes = new byte[(signature.length + data.length + 7) & (-8)];
            System.arraycopy(signature, 0, bytes, 0, signature.length);
            System.arraycopy(data, 0, bytes, signature.length, data.length);

            // update the executable, table size, checksum, etc.
            pe.writeDataDirectory(DataDirectoryType.CERTIFICATE_TABLE, bytes);
        }
    }

This no longer compiles with 7.0 since APIs have been made private or removed all together:

  • CertificateTableEntry class is now private. Unsure whether there is another way of retrieving the bytes though.
  • DataDirectoryType class is now private.
  • PEFile#writeDataDirectory has been removed alltogether.

Is there a chance to get those things back, or another way I don't know about to achieve the same? Otherwise I'd be locked on an older version indefinitely - which I would not like at all :)

mduft avatar Feb 04 '25 06:02 mduft

The API can be modified if necessary, but have you checked out the new tagging feature of Jsign 7.0? You can now embed arbitrary data into the signature without invalidating it, would that fit your needs?

ebourg avatar Feb 04 '25 06:02 ebourg

That does sound like we could use it as a replacement to doing (basically the same thing?) ourselves. Is there API to do this? We don't have a chance to use a CLI tool, as this is running on the server where installers are created on the fly in Java code.

mduft avatar Feb 04 '25 07:02 mduft

The API is in the SignerHelper class but it's package private. I plan to make this class public at some point, but in the meantime you can simply call JsignCLI programmatically (you'll need a dependency on jsign-cli instead of jsign-core):

JsignCLI cli = new JsignCLI();
cli.execute("tag", "--value", "userid:1234-ABCD-5678-EFGH", "" + signedFile);

ebourg avatar Feb 04 '25 07:02 ebourg

Woah, I really dislike that approach TBH. I will stick with 6.0 for the time being and (try to) switch once there is a tagging API.

mduft avatar Feb 04 '25 07:02 mduft

What do you dislike? If SignerHelper was public it wouldn't be that different:

SignerHelper helper = new SignerHelper();
helper.value("userid:1234-ABCD-5678-EFGH").tag(signedFile)

ebourg avatar Feb 04 '25 07:02 ebourg

Using CLIs programmatically has proven difficult to us in the past due to quoting, splitting, escaping/unescaping behavior. The data we're embedding may contain all kinds of different characters (it is basically a whole file), so I'd rather avoid passing that through another set of processing steps before ending up in the file. It is just my personal alarm sirens going off when I see something like that.

The API on the other hand would be expected by me to write the content unprocessed directly to the file, so I'd like that much better.

mduft avatar Feb 04 '25 09:02 mduft

Using CLIs programmatically has proven difficult to us in the past due to quoting, splitting, escaping/unescaping behavior.

Yes I agree in general, but here it's not calling an external command line tool by using java.lang.ProcessBuilder and spawning a new process. JsignCLI is just another Java class, no need to quote, split or escape any of the parameters.

ebourg avatar Feb 04 '25 10:02 ebourg

But isn't that using commons.cli to parse and interpret arguments given on the CLI? That does involve unquoting, unescaping and so on as far as I'm aware... But nevermind; If you are actually planning on providing an API at some point - I can wait for that, as long as version 6 does not pose a security risk (vulnerabilities, etc.) :)

mduft avatar Feb 04 '25 11:02 mduft