ebourg / jsign

Java implementation of Microsoft Authenticode for signing Windows executables, installers & scripts
https://ebourg.github.io/jsign
Apache License 2.0
250 stars 107 forks source link

Support MS-CAB #83

Closed jc-lab closed 3 years ago

jc-lab commented 3 years ago

Support MS-CAB

All tests OK

ebourg commented 3 years ago

Thank you very much, this looks excellent. Who should I credit?

jc-lab commented 3 years ago

Thank you for your positive response.

What is means, "Who should I credit?"? I am not good at English... :'(

ebourg commented 3 years ago

I'd like to thank you in the changelog for this contribution, but I can't see your name.

jc-lab commented 3 years ago

Fixed! My name is Joseph Lee.

jc-lab commented 3 years ago

Note. b7e4066 commit: Fix infinite-loop for larger then 1MB in MSCabFile

ebourg commented 3 years ago

@jc-lab I got quick look and it worked fine except for files with multiple signatures where the validation failed. I haven't checked yet if this case works with signtool and what the differences are with jsign.

ebourg commented 3 years ago

I checked with signtool and .cab files can be signed multiple times. Also I noticed that the signature of the file signed in testReplaceSignature() fails to validate. So it looks like there is something wrong when a file already signed is processed.

jc-lab commented 3 years ago

@ebourg If sign with signtool and then re-sign with withSignaturesReplaced(true), it works correctly. How did you test it? Can you show a test code that fails verification?

My test code:

@Test
    public void testReplaceSignature2() throws Exception {
        File sourceFile = new File("target/test-classes/cabinet-sample-1-already-signed/disk1/sample.cab");
        File targetFile = new File("target/test-classes/cabinet-sample-1-replaced-signed-2/disk1/sample.cab");

        FileUtils.copyFile(sourceFile, targetFile);

        MSCabFile file = new MSCabFile(targetFile);

        List<CMSSignedData> signatures = file.getSignatures();
        assertNotNull(signatures);
        assertEquals("number of signatures", 2, signatures.size());

        AuthenticodeSigner signer = new AuthenticodeSigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD)
                .withDigestAlgorithm(DigestAlgorithm.SHA512)
                .withProgramName("Minimal Package")
                .withProgramURL("http://example.com")
                .withSignaturesReplaced(true);

        signer.sign(file);

        file = new MSCabFile(targetFile);
        signatures = file.getSignatures();
        file.close();
        assertNotNull(signatures);
        assertEquals("number of signatures", 1, signatures.size());

        assertNotNull(signatures.get(0));

        assertEquals("Digest algorithm", DigestAlgorithm.SHA512.oid, signatures.get(0).getDigestAlgorithmIDs().iterator().next().getAlgorithm());
    }

cabinet-sample-1-already-signed/disk1/sample.cab download : https://pastebin.com/he6eLNHL (base64 encoded)

ebourg commented 3 years ago

I just ran testReplaceSignature() from MSCabinetSignerTest.java and the signature failed to validate. Do you get a valid signature with this test?

jc-lab commented 3 years ago

@ebourg I just tested it again. Works fine.

Please delete the jsign-core/target/test-classes directory and try again.

Maybe the directory is dirty.

ebourg commented 3 years ago

How do you check the signatures?

jc-lab commented 3 years ago

I just ran the test code and only checked in the file properties tab of explorer. I confirmed that verification fails with the signtool verify command. (SignTool Error: WinVerifyTrust returned error: 0x80096010) I will fix it. Thank you.

jc-lab commented 3 years ago

https://gist.github.com/jc-lab/05a12fc2f5deae65c78f77da3d4298d3 All of the verification through signtool was successful.

ebourg commented 3 years ago

I confirm the signature replacement and the multiple signatures work fine now.

I didn't notice it before but the signature of cabinet-sample-3-signed.cab fails to validate too.

jc-lab commented 3 years ago

sample-3 is not used. It is the same as sample-2. sample-2 (Multi-volume) was also fixed in the c25756f commit.

netmackan commented 3 years ago

I think this looks great and seems to work. Anything open before it can be merged?

ebourg commented 3 years ago

I have to find some time to check the tests again.

ebourg commented 3 years ago

In the multi disk case (sample 2), is it normal that only the first disk is signed?

jc-lab commented 3 years ago

Um... I can't remember well because it happened a long time ago.. I will compare it with Microsoft's tools and let you know.

jc-lab commented 3 years ago

The digest process was incorrect due to insufficient References on Cabinet AuthentiCode. I found the correct way and fixed it.

The headerDigestUpdate part has been fixed.

image

Below is the test result with automatic verification using the signtool tool.

CAB File
  Name:          sample.cab
  Size:          17515
  Last Modified: Sun Mar 21 15:57:51 KST 2021

Verifying: D:\jcworkspace\jsign\jsign-core\target\test-classes\cabinet-sample-2-signed\disk1\sample.cab

Signature Index: 0 (Primary Signature)
Hash of file (sha256): E1A96C65E376EE3B4D0EC46EA2ABC144E390E5C97D0376D5EFF886D68884E387

Signing Certificate Chain:
    Issued to: Jsign Root Certificate Authority
    Issued by: Jsign Root Certificate Authority
    Expires:   Thu Jun 11 04:55:56 2037
    SHA1 hash: D23C30ECFB196B2F349B614CFB43AA8B4B35C091

        Issued to: Jsign Code Signing CA
        Issued by: Jsign Root Certificate Authority
        Expires:   Thu Jun 11 04:55:56 2037
        SHA1 hash: E8410255F9D237BFB5F476EC38B8A117FE6061B2

            Issued to: Jsign Code Signing Test Certificate
            Issued by: Jsign Code Signing CA
            Expires:   Thu Jun 11 04:55:56 2037
            SHA1 hash: 9E2FD81E5A61B81FF41C4268AB2B1FC90506FE05

File is not timestamped.

Successfully verified: D:\jcworkspace\jsign\jsign-core\target\test-classes\cabinet-sample-2-signed\disk1\sample.cab

Number of files successfully Verified: 1
Number of warnings: 0
Number of errors: 0
CAB File
  Name:          sample.cab
  Size:          14099
  Last Modified: Sun Mar 21 15:57:51 KST 2021

Verifying: D:\jcworkspace\jsign\jsign-core\target\test-classes\cabinet-sample-2-signed\disk2\sample.cab

Signature Index: 0 (Primary Signature)
Hash of file (sha256): 9AD30179C67D2AD43AE78DDC8C2C35C28417EA3324F04C0288F93219FE5A600F

Signing Certificate Chain:
    Issued to: Jsign Root Certificate Authority
    Issued by: Jsign Root Certificate Authority
    Expires:   Thu Jun 11 04:55:56 2037
    SHA1 hash: D23C30ECFB196B2F349B614CFB43AA8B4B35C091

        Issued to: Jsign Code Signing CA
        Issued by: Jsign Root Certificate Authority
        Expires:   Thu Jun 11 04:55:56 2037
        SHA1 hash: E8410255F9D237BFB5F476EC38B8A117FE6061B2

            Issued to: Jsign Code Signing Test Certificate
            Issued by: Jsign Code Signing CA
            Expires:   Thu Jun 11 04:55:56 2037
            SHA1 hash: 9E2FD81E5A61B81FF41C4268AB2B1FC90506FE05

File is not timestamped.

Successfully verified: D:\jcworkspace\jsign\jsign-core\target\test-classes\cabinet-sample-2-signed\disk2\sample.cab

Number of files successfully Verified: 1
Number of warnings: 0
Number of errors: 0
ebourg commented 3 years ago

It looks great, thank you! I'll merge that soon with a few cosmetic changes.

ebourg commented 3 years ago

I've stumbled on an infinite loop when signing an old cab file from Windows 95:

[WIN95_03.CAB.gz]()

I haven't figured out the issue yet.

jc-lab commented 3 years ago

I also got sigpos wrong and found that channel.read fails to read the file and causes an infinite loop. I will fix it. Thank you for finding the case.

jc-lab commented 3 years ago

Can you tell me the origin of the file? I think it could be helpful in analyzing the cause.

ebourg commented 3 years ago

I got it from an old Windows NT 4.0 ISO.

jc-lab commented 3 years ago

Can you share the entire file (ISO or full CAB)? My email is development@jc-lab.net. Among the files I found on the Internet, I could not find a cab similar to the format.

Also, if I verify with signtool, I get the following error.

SignTool Error: CryptCATAdminCalcHashFromFileHandle returned error: 0x0000000B (ERROR_BAD_FORMAT)

Even unsigned files do not normally cause CryptCATAdminCalcHashFromFileHandle errors.

ebourg commented 3 years ago

Actually, I first got a buffer overflow with the file WIN95_03.CAB:

java.nio.BufferOverflowException
    at java.nio.Buffer.nextPutIndex(Buffer.java:527)
    at java.nio.HeapByteBuffer.putInt(HeapByteBuffer.java:372)
    at net.jsign.mscab.MSCabFile.setSignature(MSCabFile.java:424)
    at net.jsign.AuthenticodeSigner.sign(AuthenticodeSigner.java:349)

The file got corrupted and then triggered an infinite loop when I ran the test again.

jc-lab commented 3 years ago

There was a mistake in not clearing the buffer. Fixed it.

ebourg commented 3 years ago

This is now merged, many thanks! Let me know how it works for you. If it goes well I'll try to publish the 3.2 release next week.

jc-lab commented 3 years ago

I tested an exe and a cab used in our production. It works fine! The code is also beautiful! Thank you.