35

I have prepared an application and website where the customer can set several options for this application before he downloads it. Settings are stored in binary format on the end of the file (appended), then the edited file is sent to the end user. The problem is that the change of "contents" of the file will break the file signature - is there any chance to re-sign this changed file with any command line tools? I've tried to use Microsoft's SignTool, but it does not work properly on Linux.

Tomasz Banasiak
  • 1,540
  • 2
  • 13
  • 19

3 Answers3

51

You can try osslsigncode

To sign an EXE or MSI file you can now do:

osslsigncode sign -certs <cert-file> -key <der-key-file> \
        -n "Your Application" -i http://www.yourwebsite.com/ \
        -in yourapp.exe -out yourapp-signed.exe

or if you are using a PEM or PVK key file with a password together with a PEM certificate:

osslsigncode sign -certs <cert-file> \
        -key <key-file> -pass <key-password> \
        -n "Your Application" -i http://www.yourwebsite.com/ \
        -in yourapp.exe -out yourapp-signed.exe

or if you want to add a timestamp as well:

osslsigncode sign -certs <cert-file> -key <key-file> \
        -n "Your Application" -i http://www.yourwebsite.com/ \
        -t http://timestamp.verisign.com/scripts/timstamp.dll \
        -in yourapp.exe -out yourapp-signed.exe

You can use a certificate and key stored in a PKCS#12 container:

osslsigncode sign -pkcs12 <pkcs12-file> -pass <pkcs12-password> \
        -n "Your Application" -i http://www.yourwebsite.com/ \
        -in yourapp.exe -out yourapp-signed.exe

To sign a CAB file containing java class files:

osslsigncode sign -certs <cert-file> -key <key-file> \
        -n "Your Application" -i http://www.yourwebsite.com/ \
        -jp low \
        -in yourapp.cab -out yourapp-signed.cab
emkey08
  • 5,059
  • 3
  • 33
  • 34
EFernandes
  • 1,336
  • 12
  • 11
  • This is the solution that has worked for me. The signcode tool did not sign the file (although it reported signing as successful) – treaz May 28 '15 at 13:09
  • It will be useful for signing my Windows installers (wrapping Java softwares) created under Mageia Linux with NSIS and Ant. Thanks a lot :) – gouessej Nov 04 '16 at 14:49
  • I'm glad I found this great solution! I actually wanted to use something other that Microsoft's signtool.exe within Windows to sign my code so I've used Bash on Ubuntu on Windows after reading your answer. If anyone else is in the same boat, here's the rundown https://blog.synapp.nz/2017/06/16/code-signing-a-windows-application-on-linux-on-windows/ – Stacey Richards Jun 16 '17 at 10:49
  • It's 2022 and this still works wonders. – Sthe Aug 07 '22 at 02:57
31

It's actually quite straight forward to do using Mono's signtool; the tricky part (described in more detail in the linked Mozilla article) is copying the certificate in the correct format from Windows to Linux.

Converting the Windows PFX certificate file to PVK and SPC files, only needs to be done once when copying the certificate from Windows to Linux;

openssl pkcs12 -in authenticode.pfx -nocerts -nodes -out key.pem
openssl rsa -in key.pem -outform PVK -pvk-strong -out authenticode.pvk
openssl pkcs12 -in authenticode.pfx -nokeys -nodes -out cert.pem
openssl crl2pkcs7 -nocrl -certfile cert.pem -outform DER -out authenticode.spc

Actually signing the exe is straight forward;

signcode \
 -spc authenticode.spc \
 -v authenticode.pvk \
 -a sha1 -$ commercial \
 -n My\ Application \
 -i http://www.example.com/ \
 -t http://timestamp.digicert.com/scripts/timstamp.dll \
 -tr 10 \
 MyApp.exe
bitoffdev
  • 3,134
  • 1
  • 13
  • 16
Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • Have you had personal experience with using this tool? That article you're referring to is [over 2 years old](https://developer.mozilla.org/en-US/docs/Signing_an_executable_with_Authenticode$history), so some assurance that it's still up-to-date would be nice. – Rob W Dec 21 '13 at 09:24
  • @RobW I've signed executables using that command using Mono 3.2.5 and it works well (in fact I just tested). I can't test the exact steps for exporting the certificate from Windows right now since I'm not on a Mac, but I do know the flow given to be very similar to what I've used recently. – Joachim Isaksson Dec 21 '13 at 09:33
  • Thanks for the confirmation! Don't worry about the certificates, OpenSSL is capable of converting anything to anything. – Rob W Dec 21 '13 at 09:36
  • You might get an error when retyping: `openssl:Error: 'cr12pkcs7' is an invalid command.` make sure you note that the third character is lower case 'L' and not number 1 – user391035 Jun 13 '19 at 14:14
  • @user391035 check your spelling. It's `crl2pkcs7` with an L and not a 1. – Tenders McChiken May 01 '21 at 06:48
3

If you want to do that programmatically in runtime you can usee Jsign tool. Especially it could be quite helpful when you generate self-executable archive on the backend by request signing it after. And you do that using Java/Kotlin obviously (name of the tool is suggesting). Here is API provided from the official site:

Simply add this dependency to the project:

    <dependency>
      <groupId>net.jsign</groupId>
      <artifactId>jsign-core</artifactId>
      <version>3.1</version>
    </dependency> 

and then use the AuthenticodeSigner class like this:

 KeyStore keystore = KeyStoreUtils.load(newFile("keystore.p12"), "PKCS12", "password", null);

 AuthenticodeSigner signer = new AuthenticodeSigner(keystore, "test", "secret");  signer.withProgramName("My Application")
       .withProgramURL("http://www.example.com")
       .withTimestamping(true)
       .withTimestampingAuthority("http://timestamp.comodoca.com/authenticode");

 Signable file = Signable.of(new File("application.exe")); 
 signer.sign(file); 

See the Javadoc for more details about the API.

Besides signing via Java KeyStore AuthenticodeSigner has (Certificate, PrivateKey) constructor and you can freely use it like I did in my "Spring on Kotlin" backend:

    @Bean
    fun certsChain(): Array<Certificate> {
        val fact: CertificateFactory = CertificateFactory.getInstance("X.509")
        val `is` = ResourceUtil.getResourceFileAsInputStream("cert/certificate.pem")
        val cer: X509Certificate = fact.generateCertificate(`is`) as X509Certificate
        return arrayOf(cer)
    }

    @Bean
    fun privateKey(): PrivateKey {
        var key = ResourceUtil.getResourceFileAsString("cert/privateKey.pem")
        key = key.replace("-----BEGIN PRIVATE KEY-----", "")
        key = key.replace("\n", "")
        key = key.replace("-----END PRIVATE KEY-----", "")
        val encoded = Base64.getDecoder().decode(key)
        val kf = KeyFactory.getInstance("RSA")
        val keySpec = PKCS8EncodedKeySpec(encoded)
        return kf.generatePrivate(keySpec) as RSAPrivateKey
    }

    @Bean
    fun signer(
            certs: Array<Certificate>,
            privateKey: PrivateKey
    ): AuthenticodeSigner =
            AuthenticodeSigner(certs, privateKey)
                    .withProgramName("Your Company Name")
                    .withProgramURL("https://something.com")
                    .withTimestamping(true)
                    .withTimestampingAuthority("http://timestamp.comodoca.com/authenticode");

after, you can just @Autowire the signer bean and call its method sign() with the required file

Serhii Povísenko
  • 3,352
  • 1
  • 30
  • 48
  • 1
    The Jsign tool is better than the osslsigncode tool, because you can add signatures easily. I couldn't do that with osslsigncode, it always replaces the previous signature with the new one. Note that on my CentOS system, the /bin/jsign command line tool had some new line issue (it was probably created on Windows). So I made a new one with the same content, then it worked flawlessly. – Gabriel Hautclocq Nov 25 '21 at 11:07
  • The new line issue has been fixed in the latest releases – Emmanuel Bourg Apr 06 '23 at 07:36