Signing Windows application on Linux-based distros
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 togetherwith 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
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.pemopenssl rsa -in key.pem -outform PVK -pvk-strong -out authenticode.pvkopenssl pkcs12 -in authenticode.pfx -nokeys -nodes -out cert.pemopenssl 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
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