View on GitHub

Jsign

Java implementation of Microsoft Authenticode
for signing Windows executable files, installers and scripts

Download Jsign

Jsign is a Java implementation of Microsoft Authenticode that lets you sign and timestamp executable files for Windows, Microsoft Installers (MSI), Cabinet files (CAB), Catalog files (CAT), Windows packages (APPX/MSIX), Microsoft Dynamics 365 extension packages, NuGet packages and scripts (PowerShell, VBScript, JScript, WSF). Jsign is platform independent and provides an alternative to native tools like signcode/signtool on Windows or the Mono development tools on Unix systems.

Jsign comes as an easy to use task/plugin for the main build systems (Maven, Gradle, Ant). It's especially suitable for signing executable wrappers and installers generated by tools like NSIS, msitools, install4j, exe4j or launch4j. Jsign can also be used programmatically or standalone as a command line tool.

Jsign is free to use and licensed under the Apache License version 2.0.

Features

Ant Task

Here is an example showing how the signing works with Ant, using a Java keystore:

 <taskdef name="jsign" classname="net.jsign.JsignTask" classpath="jsign-6.0.jar"/>

 <jsign file="application.exe"
        name="My Application"
        url="http://www.example.com"
        keystore="keystore.jks"
        storepass="password"
        alias="test"
        tsaurl="http://timestamp.sectigo.com"/>

Another example with SPC and PVK files commonly used with signcode.exe:

 <jsign file="application.exe"
        certfile="certificate.spc"
        keyfile="key.pvk"
        keypass="password"
        tsaurl="http://timestamp.digicert.com"/>

The task also accepts a fileset to sign multiple files:

 <jsign keystore="keystore.p12"
        storepass="password"
        alias="test">
   <fileset dir="build/binaries" includes="*.exe"/>
 </jsign>


Attributes

Attribute Description Required
file The file to be signed. The supported files are Windows executables (EXE), DLLs, Microsoft Installers (MSI), Cabinet files (CAB), Catalog files (CAT), Windows packages (APPX/MSIX), Microsoft Dynamics 365 extension packages, NuGet packages and scripts (PowerShell, VBScript, JScript, WSF) Yes, unless a fileset is specified.
name The name of the application No
url The URL of the application No
keystore The keystore file, the SunPKCS11 configuration file, the cloud keystore name, or the smart card or hardware token name. Yes for file based keystores, PKCS#11 and cloud signing services. Optional for cards/tokens. No when certfile and keyfile are specified.
storepass The password to open the keystore. The password can be loaded from a file by using the file: prefix followed by the path of the file, or from an environment variable by using the env: prefix followed by the name of the variable. No
storetype The type of the keystore:
  • JKS: Java keystore (.jks files)
  • JCEKS: SunJCE keystore (.jceks files)
  • PKCS12: Standard PKCS#12 keystore (.p12 or .pfx files)
  • PKCS11: PKCS#11 hardware token
  • ETOKEN: SafeNet eToken
  • NITROKEY: Nitrokey HSM
  • OPENPGP: OpenPGP card
  • OPENSC: Smart card
  • PIV: PIV card
  • YUBIKEY: YubiKey security key (requires ykcs11)
  • AWS: AWS Key Management Service
  • AZUREKEYVAULT: Azure Key Vault key management system
  • DIGICERTONE: DigiCert ONE Secure Software Manager
  • ESIGNER: SSL.com eSigner
  • GOOGLECLOUD: Google Cloud KMS
  • HASHICORPVAULT: Google Cloud KMS via HashiCorp Vault
  • ORACLECLOUD: Oracle Cloud Key Management Service
No, automatically detected for file based keystores.
alias The alias of the certificate used for signing in the keystore. Yes, if keystore is specified and more than one alias exist
certfile The file containing the PKCS#7 certificate chain (.p7b or .spc files). Yes, unless keystore is specified.
keyfile The file containing the private key. PEM and PVK files are supported.
keypass The password of the private key. When using a keystore, this parameter can be omitted if the keystore shares the same password. The password can be loaded from a file by using the file: prefix followed by the path of the file, or from an environment variable by using the env: prefix followed by the name of the variable. No
alg The digest algorithm (SHA-1, SHA-256, SHA-384 or SHA-512). No; defaults to SHA-256 or a format specific value
tsaurl The URL of the timestamping authority, either RFC 3161 or Authenticode services. You can use for example the Sectigo (http://timestamp.sectigo.com) or the DigiCert (http://timestamp.digicert.com) services.
Several URLs separated by a comma can be specified to fallback on alternative servers.
No
tsmode The timestamping mode (RFC3161 or Authenticode) No; defaults to Authenticode
tsretries The number of retries for timestamping No; defaults to 3
tsretrywait The number of seconds to wait between timestamping retries No; defaults to 10 seconds
replace Tells if previous signatures should be replaced. No; defaults to "false"
encoding The encoding of the script to be signed (if it doesn't contain a byte order mark). No; defaults to "UTF-8"
detached Tells if a detached signature should be generated or reused. The detached signature is a file in the same directory using the name of the file signed with the .sig suffix added (for example application.exe.sig).
  • If the signature doesn't exist, the file is signed as usual and the detached signature is generated.
  • If the signature exists it is attached to the file, replacing any existing signature (in this case the private key isn't used for signing and no timestamping is performed).
No; defaults to "false"

Maven plugin

Here is an example showing how the signing works with Maven. The parameters are the same as those described above for the Ant task. The execution is bound by default to the package phase.

    <build>
      <plugins>
        <plugin>
          <groupId>net.jsign</groupId>
          <artifactId>jsign-maven-plugin</artifactId>
          <version>6.0</version>
          <executions>
            <execution>
              <goals>
                <goal>sign</goal>
              </goals>
              <configuration>
                <file>application.exe</file>
                <name>My Application</name>
                <url>http://www.example.com</url>
                <keystore>keystore.jks</keystore>
                <alias>test</alias>
                <storepass>password</storepass>
              </configuration>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>

It's possible to sign multiple files by using a fileset element instead of file:

              ...
              <configuration>
                <fileset>
                  <directory>target</directory>
                  <includes>
                    <include>*.exe</include>
                  </includes>
                </fileset>
                <keystore>keystore.jks</keystore>
                <alias>test</alias>
                <storepass>password</storepass>
              </configuration>
              ...

The value of the storepass and keypass elements can be encrypted using the Maven master password:

              <configuration>
                <file>application.exe</file>
                <keystore>keystore.jks</keystore>
                <storepass>{COQLCE6DU6GtcS5P=}</storepass>
              </configuration>

The storepass and keypass elements may also reference a password defined in the Maven settings file.

In ${user.home}/.m2/settings.xml a server is defined with the password of the keystore:

  <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
    ...
    <servers>
      <server>
        <id>keystore</id>
        <password>password</password>
      </server>
    </servers>
    ...
  </settings>

And in the Maven project file, the password element references the server id prefixed with mvn::

              <configuration>
                <file>application.exe</file>
                <keystore>keystore.jks</keystore>
                <storepass>mvn:keystore</storepass>
              </configuration>

Gradle plugin

Here is an example showing how to use Jsign with Gradle. The parameters are the same as those described above for the Ant task.

With the Groovy syntax:

    plugins {
        id 'net.jsign' version '6.0'
    }
    
    task sign {
        doLast {
            jsign(file      : 'application.exe',
                  name      : 'My Application',
                  url       : 'http://www.example.com',
                  keystore  : 'keystore.p12',
                  alias     : 'test',
                  storepass : 'secret',
                  tsaurl    : 'http://timestamp.sectigo.com')
        }
    }

With the Kotlin syntax:

    plugins {
        id("net.jsign") version "6.0"
    }
    
    task("sign") {
        doLast {
            val jsign = project.extensions.getByName("jsign") as groovy.lang.Closure<*>
            jsign("file"      to "application.exe",
                  "name"      to "My Application",
                  "url"       to "http://www.example.com",
                  "keystore"  to "keystore.p12",
                  "alias"     to "test",
                  "storepass" to "secret",
                  "tsaurl"    to "http://timestamp.sectigo.com")
        }
    }

Command Line Tool

Jsign can also be used as a command line tool, packages are available for various systems:

On these systems the command line is invoked with:

 jsign [OPTIONS] [FILE]...

On other systems the command line is invoked by running the jar with:

 java -jar jsign-6.0.jar [OPTIONS] [FILE]...

The parameters expected are the same as those used by the Ant task:

  usage: jsign [OPTIONS] [FILE]...
  Sign and timestamp Windows executable files, Microsoft Installers (MSI), Cabinet
  files (CAB), Catalog files (CAT), Windows packages (APPX/MSIX), Microsoft Dynamics
  365 extension packages, NuGet packages and scripts (PowerShell, VBScript, JScript, WSF).
  
  -s,--keystore <FILE>       The keystore file, the SunPKCS11 configuration file,
                             the cloud keystore name, or the card/token name
     --storepass <PASSWORD>  The password to open the keystore
     --storetype <TYPE>      The type of the keystore:
                             - JKS: Java keystore (.jks files)
                             - JCEKS: SunJCE keystore (.jceks files)
                             - PKCS12: Standard PKCS#12 keystore (.p12 or .pfx files)
                             - PKCS11: PKCS#11 hardware token
                             - ETOKEN: SafeNet eToken
                             - NITROKEY: Nitrokey HSM
                             - OPENPGP: OpenPGP card
                             - OPENSC: Smart card
                             - PIV: PIV card
                             - YUBIKEY: YubiKey security key
                             - AWS: AWS Key Management Service
                             - AZUREKEYVAULT: Azure Key Vault key management system
                             - DIGICERTONE: DigiCert ONE Secure Software Manager
                             - ESIGNER: SSL.com eSigner
                             - GOOGLECLOUD: Google Cloud KMS
                             - HASHICORPVAULT: Google Cloud KMS via HashiCorp Vault
                             - ORACLECLOUD: Oracle Cloud Key Management Service
  -a,--alias <NAME>          The alias of the certificate used for signing in the keystore.
     --keypass <PASSWORD>    The password of the private key. When using a keystore,
                             this parameter can be omitted if the keystore shares the
                             same password.
     --keyfile <FILE>        The file containing the private key (supports PEM & PVK files)
  -c,--certfile <FILE>       The file containing the PKCS#7 certificate chain
                             (.p7b or .spc files).
  -d,--alg <ALGORITHM>       The digest algorithm (SHA-1, SHA-256, SHA-384 or SHA-512)
  -t,--tsaurl <URL>          The URL of the timestamping authority. Several URLs separated
                             by a comma can be specified to fallback on alternative servers
  -m,--tsmode <MODE>         The timestamping mode (RFC3161 or Authenticode)
  -r,--tsretries <NUMBER>    The number of retries for timestamping
  -w,--tsretrywait <SECONDS> The number of seconds to wait between timestamping retries
  -n,--name <NAME>           The name of the application
  -u,--url <URL>             The URL of the application
     --proxyUrl <URL>        The URL of the HTTP proxy
     --proxyUser <NAME>      The user for the HTTP proxy. If an user is needed.
     --proxyPass <PASSWORD>  The password for the HTTP proxy user. If an user is needed.
     --replace               Tells if previous signatures should be replaced.
  -e,--encoding <ENCODING>   The encoding of the script to be signed (UTF-8 by default,
                             or the encoding specified by the byte order mark if there is one)
     --detached              Tells if a detached signature should be generated or reused.
  -h,--help                  Print the help

Examples

Signing with a Java keystore

 jsign --keystore keystore.jks --storepass password --alias test \
       --tsaurl http://timestamp.sectigo.com application.exe

Signing with a SPC certificate and a PVK key

 jsign --certfile certificate.spc --keyfile key.pvk --keypass password application.exe

Signing with a YubiKey

When using a Yubikey, the alias is required only if the device contains more than one certificate. The certificate is specified by its name (typically X.509 Certificate for Digital Signature for the slot 9c, or X.509 Certificate for PIV Authentication for the slot 9a). The ykcs11 library from the Yubico PIV Tool must be installed on the system at the default location.

 jsign --storetype YUBIKEY --storepass 123456 --certfile full-chain.pem application.exe

Alternatively, the PIV storetype can also be used to sign with a Yubikey and doesn't require the ykcs11 library.

Signing with a Nitrokey HSM

Signing with a Nitrokey HSM requires the installation of OpenSC.

 jsign --storetype NITROKEY --storepass 123456 --alias test \
       --certfile full-chain.pem application.exe

Other Nitrokeys based on the OpenPGP card standard are also supported with this storetype, but an X.509 certificate must be imported into the Nitrokey (using the gnupg writecert command). Keys without certificates are ignored. Alternatively, the OPENPGP storetype can also be used, it doesn't require OpenSC and any key can be used by providing an external certificate.

Signing with a SafeNet eToken

Signing with a SafeNet eToken requires the installation of the SafeNet Authentication Client.

  jsign --storetype ETOKEN --storepass <PIN> --certfile full-chain.pem application.exe

Signing with a smart card

Signing with a smart card requires the installation of OpenSC.

 jsign --storetype OPENSC --storepass 123456 --alias test \
       --certfile full-chain.pem application.exe

If multiple devices are connected, the keystore parameter can be used to specify the name of the one to use.

Signing with an OpenPGP card

OpenPGP cards contain up to 3 keys, one for signing, one for encryption, and one for authentication. All of them can be used for code signing (except encryption keys based on an elliptic curve). The alias to select the key is either, SIGNATURE, ENCRYPTION or AUTHENTICATION. The OPENPGP storetype can be used with a Nitrokey (non-HSM models) or a Yubikey.

 jsign --storetype OPENPGP --storepass 123456 --alias SIGNATURE \
       --certfile full-chain.pem application.exe

X.509 certificates stored on the card are automatically used, and the certfile parameter is only required if the certificate chain contains an intermediate certificate.

If multiple devices are connected, the keystore parameter can be used to specify the name of the one to use.

Signing with a PIV card

PIV cards contain up to 24 keys and certificates. The alias to select the key is either AUTHENTICATION, SIGNATURE, KEY_MANAGEMENT, CARD_AUTHENTICATION, or RETIRED<1-20>. Slot numbers are also accepted (for example 9c for the digital signature key).

 jsign --storetype PIV --storepass 123456 --alias SIGNATURE \
       --certfile full-chain.pem application.exe

X.509 certificates stored on the card are automatically used, and the certfile parameter is only required if the certificate chain contains an intermediate certificate.

If multiple devices are connected, the keystore parameter can be used to specify the name of the one to use.

Signing with AWS Key Management Service

AWS Key Management Service (KMS) stores only the private key, the certificate must be provided separately. The keystore parameter references the AWS region.

The AWS access key, secret key, and optionally the session token, are concatenated and used as the storepass parameter; if the latter is not provided, Jsign attempts to fetch the credentials from the environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN) or from the IMDSv2 service when running on an AWS EC2 instance.

In any case, the credentials must allow the following actions: kms:ListKeys, kms:DescribeKey and kms:Sign.

The alias parameter can specify either the key id or an alias.

 jsign --storetype AWS \
       --keystore eu-west-3 \
       --storepass "<access-key>|<secret-key>|<session-token>" \
       --alias 12345678-abcd-1234-cdef-1234567890ab \
       --certfile full-chain.pem application.exe

Signing with Azure Key Vault

Certificates and keys stored in the Azure Key Vault key management system can be used with:

 jsign --storetype AZUREKEYVAULT \
       --keystore vaultname \
       --storepass <api-access-token> \
       --alias test application.exe

Signing with DigiCert ONE

Certificates and keys stored in the DigiCert ONE Secure Software Manager can be used directly without installing the DigiCert client tools. It requires an API key and a PKCS#12 keystore holding a client certificate for the authentication.

 jsign --storetype DIGICERTONE \
       --storepass "<api-key>|/path/to/Certificate_pkcs12.p12|<password>" \
       --alias test application.exe

Signing with SSL.com eSigner

When signing with the SSL.com eSigner service, the SSL.com username and password are used as the keystore password, and the base64 encoded TOTP secret is used as the key password:

 jsign --storetype ESIGNER \
       --storepass "<username>|<password>" \
       --alias 8b072e22-7685-4771-b5c6-48e46614915f \
       --keypass <totp-secret> application.exe

SSL.com provides a sandbox environment, to use a test certificate simply add the parameter --keystore https://cs-try.ssl.com.

Signing with Google Cloud KMS

Google Cloud KMS stores only the private key, the certificate must be provided separately. The keystore parameter references the path of the keyring. The alias specifies the name and the version of the key:

 jsign --storetype GOOGLECLOUD \
       --keystore projects/first-rain-123/locations/global/keyRings/mykeyring \
       --storepass <api-access-token> \
       --alias test/cryptoKeyVersions/1 \
       --certfile full-chain.pem application.exe

The version of the key can be omitted (e.g. --alias test), in this case the most recent version of the key is picked automatically. This avoids modifying the parameters every time the key is updated, but the signing process is slightly slower due to an additional API call, and it requires an extra permission.

The access token is typically provided by the gcloud tool:

 gcloud auth print-access-token

When creating the key the purpose must be set to "Asymmetric sign", and the algorithm must be either Elliptic Curve or RSA with PKCS#1 v1.5 padding and SHA digest. Keys with PSS padding or raw RSA mode are not supported.

The Google Cloud account used must have the following permissions:

These permissions are covered by the Cloud KMS CryptoKey Signer and Cloud KMS Viewer roles.

Signing with Google Cloud KMS via HashiCorp Vault

Google Cloud KMS stores only the private key, the certificate must be provided separately. The keystore parameter references the URL of the HashiCorp Vault secrets engine, consisting of the Vault server URL, the API version v1 and the secrets engine path. The alias specifies the name of the key in Vault and the key version in Google Cloud separated by a colon character.

 jsign --storetype HASHICORPVAULT \
       --keystore https://vault.example.com/v1/gcpkms \
       --storepass <vault-token> \
       --alias test:1 \
       --certfile full-chain.pem application.exe

Signing with Oracle Cloud Key Management Service

Signing with the Oracle Cloud Infrastructure Key Management Service requires the configuration file or the environment variables used by the OCI CLI. The OCI CLI isn't required for signing, but it may be used to initialize the configuration file with oci setup bootstrap.

The keystore parameter specifies the profile used in the configuration file (the default value is DEFAULT), and the storepass parameter specifies the path to the configuration file (~/.oci/config by default).

The certificate must be provided separately using the certfile parameter. The alias specifies the OCID of the key.

The general syntax looks like this:

 jsign --storetype ORACLECLOUD \
       --keystore <profile> \
       --storepass <oci-config-file> \
       --alias ocid1.key.oc1.eu-paris-1.abcdefghijklm.abrwiljrwkhgllb5zfqchmvdkmqnzutqeq5pz7 \
       --certfile full-chain.pem application.exe

When using the default configuration file and profile, the command is simplified to:

 jsign --storetype ORACLECLOUD \
       --alias ocid1.key.oc1.eu-paris-1.abcdefghijklm.abrwiljrwkhgllb5zfqchmvdkmqnzutqeq5pz7 \
       --certfile full-chain.pem application.exe

The configuration file can be replaced (or overridden) by environment variables. Here are the variables expected:

API

Jsign also provides a simple API for signing files and can be embedded in another application.

Simply add this dependency to the project:

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

and then use the AuthenticodeSigner class like this:

 KeyStore keystore = new KeyStoreBuilder().keystore("keystore.p12").storepass("password").build();

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

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

See the Javadoc for more details about the API.

JCA security provider

Jsign implements a JCA security provider that can be integrated with other Java based signing tools to extend their range of supported keystores.

JAR signing

The JCA provider can be used to sign JAR files with with the jarsigner tool.

With Java 11 or later the syntax looks like this:

 jarsigner -J-cp -Jjsign-6.0.jar -J--add-modules -Jjava.sql \
           -providerClass net.jsign.jca.JsignJcaProvider \
           -providerArg <keystore> \
           -keystore NONE \
           -storetype <storetype> \
           -storepass <storepass> \
           -keypass <keypass> \
           -certchain <certfile> \
           application.jar <alias>

With Java 8 the classpath must reference tools.jar from the JDK and the --add-modules parameter is removed:

 jarsigner -J-cp -Jjsign-6.0.jar:$JAVA_HOME/lib/tools.jar \
           ...

The keystore parameter must be set to NONE, the actual value of the keystore is specified with the providerArg parameter instead.

APK signing

The JCA provider can be used to sign Android applications with the apksigner tool. The syntax looks like this:

 java -cp apksigner.jar:jsign.jar com.android.apksigner.ApkSignerTool sign \
      --provider-class net.jsign.jca.JsignJcaProvider \
      --provider-arg <keystore> \
      --ks NONE \
      --ks-type <storetype> \
      --ks-pass pass:<storepass> \
      --ks-key-alias <alias> \
      --key-pass pass:<keypass> \
      --cert <certfile> \
      --in application.apk

Downloads

Credits

Jsign leverages the cryptography API developed by the Bouncy Castle project.
PVK parsing is based on the pvktool by Stephen N Henson.
MSI signing was possible thanks to the work done by the osslsigncode and Apache POI projects.

Jsign includes contributions from Emmanuel Bourg, Florent Daigniere, Michael Szediwy, Michael Peterson, Markus Kilås, Erwin Tratar, Björn Kautler, Joseph Lee, Maria Merkel, Vincent Malmedy and Sebastian Stamm.

Contact

Emmanuel Bourg (ebourg@apache.org, @smanux)