From 134cdea2e2900bb81c0689d34b81c8e82662afb2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 13 Jul 2025 23:55:04 +0800 Subject: [PATCH] Add command to show details about a signed object It's useful to know whether an executable is already signed and by whom. Either to make sure that you have properly signed it or to check if somebody else has signed it. Example: ``` > java -jar jsign/target/jsign-7.2-SNAPSHOT.jar show foo.exe jsign show foo.exe Signature 0 Digest Algorithm: SHA256 Digest Value: c481bb3892d066ffacba0650adaa4c252580b776b1dd6026cf4a8bea6c813939 Is Timestamped? false Certificate Subject: CN=net.jsign.signing-cert Issuer: CN=net.jsign.issuing-cert Not Before: Fri Dec 03 14:34:46 CST 2021 Not After: Wed May 24 14:34:46 CST 2119 Expired: false Serial: 148957645726085760686199624248870688956 ``` Signed-off-by: Daniel Schaefer --- docs/index.html | 23 +++++++++- .../src/main/java/net/jsign/JsignCLI.java | 4 ++ .../src/main/java/net/jsign/SignerHelper.java | 43 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/docs/index.html b/docs/index.html index 8336abf5..9aebad6f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -131,6 +131,7 @@

Command Line Tool

  • timestamp
  • extract
  • remove
  • +
  • show
  • tag
  • @@ -368,6 +369,27 @@

    extract


    +

    Show signature

    + +

    To check whether a file has been signed and with which certificate, use the show command to list details about any signature present on the file.

    + +
    +  jsign show foo.exe
    +  Signature 0
    +  Digest Algorithm:   SHA256
    +  Digest Value:       c481bb3892d066ffacba0650adaa4c252580b776b1dd6026cf4a8bea6c813939
    +  Is Timestamped?     false
    +  Certificate
    +    Subject:          CN=net.jsign.signing-cert
    +    Issuer:           CN=net.jsign.issuing-cert
    +    Not Before:       Fri Dec 03 14:34:46 CST 2021
    +    Not After:        Wed May 24 14:34:46 CST 2119
    +    Expired:          false
    +    Serial:           148957645726085760686199624248870688956
    +
    + +
    +

    tag

    @@ -1040,7 +1062,6 @@

    GitHub Actions

    ${{ github.workspace }}/dist/application.exe -

    API

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

    diff --git a/jsign-cli/src/main/java/net/jsign/JsignCLI.java b/jsign-cli/src/main/java/net/jsign/JsignCLI.java index 0173bd29..0bb2eea1 100644 --- a/jsign-cli/src/main/java/net/jsign/JsignCLI.java +++ b/jsign-cli/src/main/java/net/jsign/JsignCLI.java @@ -147,6 +147,10 @@ private Map getOptions() { map.put("remove", options); + options = new Options(); + + map.put("show", options); + options = new Options(); options.addOption(Option.builder().hasArg().longOpt(PARAM_VALUE).argName("VALUE").desc("The value of the unsigned attribute").build()); diff --git a/jsign-core/src/main/java/net/jsign/SignerHelper.java b/jsign-core/src/main/java/net/jsign/SignerHelper.java index d9457f4c..3408357d 100644 --- a/jsign-core/src/main/java/net/jsign/SignerHelper.java +++ b/jsign-core/src/main/java/net/jsign/SignerHelper.java @@ -333,6 +333,9 @@ public void execute(File file) throws SignerException { case "remove": remove(file); break; + case "show": + show(file); + break; case "tag": tag(file); break; @@ -496,6 +499,32 @@ private void attach(Signable signable, File detachedSignature) throws IOExceptio // todo warn if the hashes don't match } + private void printSignatures(Signable signable) throws IOException { + List signatures = signable.getSignatures(); + for (int i = 0; i < signatures.size(); i++) { + CMSSignedData signature = signatures.get(i); + SignerInformation signerInformation = signature.getSignerInfos().iterator().next(); + + String digestAlgorithmName = new DefaultAlgorithmNameFinder().getAlgorithmName(signerInformation.getDigestAlgorithmID()); + SignerId signerId = signerInformation.getSID(); + X509CertificateHolder cert = (X509CertificateHolder) signature.getCertificates().getMatches(signerId).iterator().next(); + + byte[] digest = signable.computeDigest(DigestAlgorithm.of(signerInformation.getDigestAlgorithmID().getAlgorithm())); + + System.out.println("Signature " + i); + System.out.println(" Digest Algorithm: " + digestAlgorithmName); + System.out.println(" Digest Value: " + Hex.toHexString(digest)); + System.out.println(" Is Timestamped? " + SignatureUtils.isTimestamped(signature)); + System.out.println(" Certificate"); + System.out.println(" Subject: " + cert.getSubject()); + System.out.println(" Issuer: " + cert.getIssuer()); + System.out.println(" Not Before: " + cert.getNotBefore()); + System.out.println(" Not After: " + cert.getNotAfter()); + System.out.println(" Expired: " + cert.getNotAfter().before(new Date())); + System.out.println(" Serial: " + cert.getSerialNumber()); + } + } + private void detach(Signable signable, File detachedSignature) throws IOException { List signatures = signable.getSignatures(); @@ -576,6 +605,20 @@ private void remove(File file) throws SignerException { } } + private void show(File file) throws SignerException { + if (!file.exists()) { + throw new SignerException("Couldn't find " + file); + } + + try (Signable signable = Signable.of(file)) { + printSignatures(signable); + } catch (UnsupportedOperationException | IllegalArgumentException e) { + throw new SignerException(e.getMessage(), e); + } catch (Exception e) { + throw new SignerException("Couldn't show the signatures of" + file, e); + } + } + private void tag(File file) throws SignerException { if (!file.exists()) { throw new SignerException("Couldn't find " + file);