Signing Windows application on Linux-based distros
Solution 1
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
Solution 2
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
Solution 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
Tomasz Banasiak
I am passionate about new technologies and programming. I have over 5 years of commercial experience working as a programmer, I had the opportunity to participate in creating a number of projects aimed at the world market, many of which enjoyed strong success. I am happy to developing and improving my skills. I feel well both as a leader and part of a larger team. Contact me if you are looking for a person who is not afraid of new challenges. I'm a big fan of the tasks that are seemingly unsolvable, and to find ways to optimize existing algorithms.
Updated on February 24, 2021Comments
-
Tomasz Banasiak over 3 years
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.
-
Rob W over 10 yearsHave you had personal experience with using this tool? That article you're referring to is over 2 years old, so some assurance that it's still up-to-date would be nice.
-
Joachim Isaksson over 10 years@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.
-
Rob W over 10 yearsThanks for the confirmation! Don't worry about the certificates, OpenSSL is capable of converting anything to anything.
-
treaz almost 9 yearsThis is the solution that has worked for me. The signcode tool did not sign the file (although it reported signing as successful)
-
gouessej over 7 yearsIt will be useful for signing my Windows installers (wrapping Java softwares) created under Mageia Linux with NSIS and Ant. Thanks a lot :)
-
Stacey Richards almost 7 yearsI'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 blog.synapp.nz/2017/06/16/…
-
user391035 almost 5 yearsYou 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 -
Tenders McChiken about 3 years@user391035 check your spelling. It's
crl2pkcs7
with an L and not a 1. -
Gabriel Hautclocq over 2 yearsThe 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.