Signing Windows application on Linux-based distros
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.
Solution 1:
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 2:
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 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