Skip to main content
Skip table of contents

Signing Use Case: Perform PQC Signing and Validation

After completing the Initial Setup of PQC Lab Test Drive, you are ready to start testing post-quantum signing and validation operations using SignServer.

This page provides a step-by-step guide and example commands for signing and verifying files, hashes, and software artifacts using PQC algorithms. You will learn how to perform signing and timestamping with SignServer through the CLI and REST API, and how to validate PQC signatures using OpenSSL and other verification tools.

Prerequisites

  • OpenSSL 3.5 or later is used for the validation. OpenSSL must be installed to follow parts of this tutorial that use the OpenSSL command.

    CODE
    bash-5.2$ openssl version
    OpenSSL 3.6.0 1 Oct 2025 (Library: OpenSSL 3.6.0 1 Oct 2025)
  • JQ installed for parsing json data

    CODE
    bash-5.2$ which jq
    /usr/bin/jq
  • JQ & OpenSSL should be in the PATH variable on Linux or system path on Windows.

  • If you haven’t already done this, download clients.zip and signer_certs.zip and unzip them in the same work directory. For details, see Initial Setup of PQC Lab Test Drive.

  • Download ManagementCA JKS truststore from EJBCA RA Web → CA Certificates and CRLs → Certificate chain, and place in the same directory as clients and signer_certs.

  • Replace the variables with the applicable values for the commands below:

    • Linux:

      • TEST_DRIVE_HOST: the fully qualified domain name (FQDN) of the test drive host; e.g. exampletestdrive.eastus2.cloudapp.azure.com

        CODE
        $ TEST_DRIVE_HOST=pqclabpbi213zqo8w3.eastus2.cloudapp.azure.com
      • TEST_DRIVE_OAUTH_USERNAME: the admin username provided in the Test Drive portal for accessing the test drive using OAuth; e.g. admin57g9n

        CODE
        $ TEST_DRIVE_OAUTH_USERNAME=admin57g9n
      • TEST_DRIVE_OAUTH_PASSWD: the admin password provided in the Test Drive portal for accessing the test drive using OAuth

        CODE
        $ TEST_DRIVE_OAUTH_PASSWD=<your-password>
    • Windows:

    • CODE
      $env:TEST_DRIVE_HOST = "pqclabpbi213zqo8w3.eastus2.cloudapp.azure.com"
      $env:TEST_DRIVE_OAUTH_USERNAME = "admin57g9n"
      $env:TEST_DRIVE_OAUTH_PASSWD = "<your-password>"
  • Create testFiles directory in clients and place sample files of type txt, msi, cs, deb, and jar.

    CODE
    bash-5.2$ cd <testDriveWorkDir>/clients
    bash-5.2$ mkdir testFiles
  • To begin testing, open a terminal on Linux/MacOS or Command Prompt terminal with PowerShell on Windows. Change to the directory where you unzipped the Client CLI's.

    CODE
    $ cd <testDriveWorkDir>/clients/signserver

PlainSigner PQC Test

Use the PlainSigner to get a detached signature for the hash of the artifact.

Signclient Test

Test signing with the PlainSigner worker by sending the file with signclient to SignServer and receiving a detached signature file. Client-side hashing is not supported for PQC algorithms at this time.

Sign file with PlainSignerPQC

Chang to signserver directory under clients
Sign the HelloDeb.txt text file:
Linux:

BASH
bin/signclient signdocument \
  -host "$TEST_DRIVE_HOST" \
  -port 8445 \
  -workername PlainSignerPqc \
  -truststore ../signer_certs/ManagementCA-chain.jks \
  -truststorepwd changeit \
  -infile ../testFiles/HelloDeb.txt \
  -outfile ../testFiles/HelloDeb.txt.sig

Windows:

BASH
bin/signclient signdocument `
  -host $env:TEST_DRIVE_HOST `
  -port 8445 `
  -workername PlainSignerPqc `
  -truststore ..\signer_certs\ManagementCA-chain.jks `
  -truststorepwd changeit `
  -infile ..\testFiles\HelloDeb.txt `
  -outfile ..\testFiles\HelloDeb.txt.sig

Timestamp Plain Signed file

Timestamp the HelloDeb.txt signature file:
Linux:

BASH
bin/signclient timestamp \
  -url "https://$TEST_DRIVE_HOST:8445/signserver/tsa?workerName=TimeStampSignerPqc" \
  -infile ../testFiles/HelloDeb.txt.sig \
  -outrep ../testFiles/HelloDeb.txt.sig.ts \
  -truststore ../signer_certs/ManagementCA-chain.jks \
  -truststorepwd changeit

Windows:

BASH
bin/signclient timestamp `
  -url "https://$env:TEST_DRIVE_HOST:8445/signserver/tsa?workerName=TimeStampSignerPqc" `
  -infile ..\testFiles\HelloDeb.txt.sig `
  -outrep ..\testFiles\HelloDeb.txt.sig.ts `
  -truststore ..\signer_certs\ManagementCA-chain.jks `
  -truststorepwd changeit

Verify PlainSigner PQC signed file

  1. Extract the publicKey from the PlainSigner PQC certificate file

BASH
$ openssl x509 \
	-in ../signer_certs/PlainSignerPqc.crt -noout \
	-pubkey > ../signer_certs/PlainSignerPqc.pubkey
  1. Verify the signature with OpenSSL

Linux:

BASH
$ openssl pkeyutl -verify \
	-pubin -inkey ../signer_certs/PlainSignerPqc.pubkey \
	-sigfile ../testFiles/HelloDeb.txt.sig \
	-in ../testFiles/HelloDeb.txt

Windows:

BASH
openssl pkeyutl -verify `
  -pubin -inkey ..\signer_certs\PlainSignerPqc.pubkey `
  -sigfile ..\testFiles\HelloDeb.txt.sig `
  -in ..\testFiles\HelloDeb.txt

Signserver PlainSigner Rest API Test

The Rest API can be used to send a hash created locally or to send the file to SignServer to get signed.

Sign and verify a file with the Rest API PlainSigner

  1. Use the SignServer Rest API to get the file signed with the PQC PlainSigner
    Linux:

BASH
$ curl -ksL -X POST \
  "https://$TEST_DRIVE_HOST:8445/signserver/rest/v1/workers/PlainSignerPqc/process" \
  -H "X-Keyfactor-Requested-With: X" \
  -H "Accept: application/octet-stream" \
  --form 'myfile=@"../testFiles/HelloDeb.txt";type=multipart/form-data;charset=utf-8' \
  -o ../testFiles/HelloDeb.txt-via-rest.sig

Windows:

BASH
curl -ksL -X POST `
  "https://$env:TEST_DRIVE_HOST:8445/signserver/rest/v1/workers/PlainSignerPqc/process" `
  -H "X-Keyfactor-Requested-With: X" `
  -H "Accept: application/octet-stream" `
  --form "myfile=@..\testFiles\HelloDeb.txt;type=multipart/form-data;charset=utf-8" `
  -o ..\testFiles\HelloDeb.txt-via-rest.sig
  1. Verify the signature with OpenSSL

Linux:

BASH
$ openssl pkeyutl -verify \
        -pubin -inkey ../signer_certs/PlainSignerPqc.pubkey \
        -sigfile ../testFiles/HelloDeb.txt-via-rest.sig \
        -in ../testFiles/HelloDeb.txt
Signature Verified Successfully

Windows:

BASH
openssl pkeyutl -verify `
  -pubin `
  -inkey ..\signer_certs\PlainSignerPqc.pubkey `
  -sigfile ..\testFiles\HelloDeb.txt-via-rest.sig `
  -in ..\testFiles\HelloDeb.txt

Sign and verify a hash with Rest API PlainSigner

  1. Hash a file to sign, and save the hash to the variable PS_HASH

Linux:

BASH
$ PS_HASH=$(openssl dgst -sha384 -binary ../testFiles/HelloDeb.txt | base64)

Windows:

BASH
PS_HASH = (openssl dgst -sha384 -binary ..\testFiles\HelloDeb.txt | openssl base64 -A)
  1. Sign the hash using Rest API and save the result as json

Linux:

BASH
curl -ksL -X POST \
  "https://$TEST_DRIVE_HOST:8445/signserver/rest/v1/workers/PlainSignerPqc/process" \
  -H "X-Keyfactor-Requested-With: X" \
  -H "Content-Type: application/json" \
  -d "{\"data\": \"$PS_HASH\",\"encoding\": \"BASE64\",\"metaData\": {\"USING_CLIENTSUPPLIED_HASH\": \"true\",\"CLIENTSIDE_HASHDIGESTALGORITHM\": \"SHA384\"}}" \
  | jq . > ../testFiles/HelloDeb.txt-rest-hash.sig

Windows:

CODE
curl -ksL -X POST `
  "https://$env:TEST_DRIVE_HOST:8445/signserver/rest/v1/workers/PlainSignerPqc/process" `
  -H "X-Keyfactor-Requested-With: X" `
  -H "Content-Type: application/json" `
  -d "{\"data\": \"$env:PS_HASH\",\"encoding\": \"BASE64\",\"metaData\": {\"USING_CLIENTSUPPLIED_HASH\": \"true\",\"CLIENTSIDE_HASHDIGESTALGORITHM\": \"SHA384\"}}" `
  | jq . > ..\testFiles\HelloDeb.txt-rest-hash.sig
  1. Extract the signature of the file from the json file

Linux:

BASH
$ cat ../testFiles/HelloDeb.txt-signed.json \
  | jq -r .data \
  | base64 -d > ../testFiles/HelloDeb.txt-rest-hash.sig

Windows (with jq)

BASH
Get-Content ..\testFiles\HelloDeb.txt-signed.json | `
jq -r .data | `
base64 -d > ..\testFiles\HelloDeb.txt-rest-hash.sig

Windows (Powershell builtin json)

CODE
$json = Get-Content ..\testFiles\HelloDeb.txt-signed.json -Raw | ConvertFrom-Json
[IO.File]::WriteAllBytes(
  "..\testFiles\HelloDeb.txt-rest-hash.sig",
  [Convert]::FromBase64String($json.data)
)
  1. Verify the signature with openSSL

Linux:

BASH
openssl pkeyutl -verify \
  -pubin \
  -inkey ../signer_certs/plainSignerPqc.pubkey \
  -sigfile ../testFiles/HelloDeb.txt-rest-hash.sig \
  -in <(openssl dgst -sha384 -binary ../testFiles/HelloDeb.txt)
  Signature Verified Successfully

Windows:

BASH
openssl dgst -sha384 -binary ..\testFiles\HelloDeb.txt | `
openssl pkeyutl -verify `
  -pubin `
  -inkey ..\signer_certs\plainSignerPqc.pubkey `
  -sigfile ..\testFiles\HelloDeb.txt-rest-hash.sig

Time Stamp PQC Test

Timestamp and verify artificat

The signclient is used to send an artifact to SignServer to be timestamped.

  1. Time stamp one of the previous signatures created with the PlainSigner:

Linux:

BASH
bin/signclient timestamp \
  -url "https://$TEST_DRIVE_HOST:8445/signserver/tsa?workerName=TimeStampSignerPqc" \
  -infile ../testFiles/HelloDeb.txt-rest-hash.sig \
  -base64 \
  -outrep ../testFiles/HelloDeb.txt-rest-hash.sig.ts \
  -truststore ../signer_certs/ManagementCA-chain.jks \
  -truststorepwd changeit

Windows:

BASH
bin\signclient.cmd timestamp `
  -url "https://$env:TEST_DRIVE_HOST:8445/signserver/tsa?workerName=TimeStampSignerPqc" `
  -infile ..\testFiles\HelloDeb.txt-rest-hash.sig `
  -base64 `
  -outrep ..\testFiles\HelloDeb.txt-rest-hash.sig.ts `
  -truststore ..\signer_certs\ManagementCA-chain.jks `
  -truststorepwd changeit
  1. Verify the response using signclient
    Linux:

BASH
$ bin/signclient timestamp \
  -verify \
  -inrep ../testFiles/HelloDeb.txt-rest-hash.sig.ts \
  -signerfile ../signer_certs/TimeStampSignerPqc.crt
2025-10-24T22:07:21,414 INFO  [TimeStampCommand] Token was validated successfully.
2025-10-24T22:07:21,424 INFO  [TimeStampCommand] Token was generated on: Fri Oct 24 21:57:12 CEST 2025
2025-10-24T22:07:21,424 INFO  [TimeStampCommand] MessageDigest=c1265ee7d642fe56780e96924bccf3ca8e9f39d75875d6923a3005641a3f2ece
2025-10-24T22:07:21,425 INFO  [TimeStampCommand] Processing took 116 ms

Windows:

BASH
bin\signclient.cmd timestamp `
  -verify `
  -inrep ..\testFiles\HelloDeb.txt-rest-hash.sig.ts `
  -signerfile ..\signer_certs\TimeStampSignerPqc.crt
  1. Verify the response using OpenSSL

Linux:

BASH
openssl ts -verify \
  -in ../testFiles/HelloDeb.txt-rest-hash.sig.ts \
  -data ../testFiles/HelloDeb.txt-rest-hash.sig \
  -CAfile ../signer_certs/PqcCaChain.crt \
  -signer ../signer_certs/TimeStampSignerPqc.crt

Windows:

BASH
openssl ts -verify `
  -in ..\testFiles\HelloDeb.txt-rest-hash.sig.ts `
  -data ..\testFiles\HelloDeb.txt-rest-hash.sig `
  -CAfile ..\signer_certs\PqcCaChain.crt `
  -signer ..\signer_certs\TimeStampSignerPqc.crt

Code Signer PQC Test

Sign msi file with Code Signer PQC and verify

  1. Sign the msi file with signclient

Linux:

BASH
$ bin/signclient signdocument \
  -host $TEST_DRIVE_HOST \
  -port 8445 \
  -workername MSAuthCodeSignerPqc \
  -truststore ../signer_certs/ManagementCA-chain.jks \
  -truststorepwd changeit \
  -infile ../testFiles/sample.msi \
  -outfile ../testFiles/sample-signed.msi
2025-10-25T00:55:14,593 INFO  [HostManager] Next host retrieved for signing: pqclabpbi213zqo8w3.eastus2.cloudapp.azure.com
2025-10-25T00:55:29,824 INFO  [SignDocumentCommand] Wrote ../testFiles/sample-signed.msi.

Windows:

BASH
bin/signclient signdocument `
  -host $env:TEST_DRIVE_HOST `
  -port 8445 `
  -workername MSAuthCodeSignerPqc `
  -truststore ..\signer_certs\ManagementCA-chain.jks `
  -truststorepwd changeit `
  -infile ..\testFiles\sample.msi `
  -outfile ..\testFiles\sample-signed.msi
  1. If using Linux copy the signed MSI file to a Windows device (Server, desktop, VM, etc).

  2. In the Windows environment right click on the signed file and click Properties and then the Digital Signature tab.

  3. Click the Details button to see the Digital Signature Information.

Jar Signer PQC Test

Sign file with Jar Signer PQC and verify

  1. Use signclient to sign the jar file

Linux:

BASH
bin/signclient signdocument \
  -host $TEST_DRIVE_HOST \
  -port 8445 \
  -workername JarSignerPqc \
  -truststore ../signer_certs/ManagementCA-chain.jks \
  -truststorepwd changeit \
  -infile ../testFiles/HelloJar.jar \
  -outfile ../testFiles/HelloJar-signed.jar

Windows:

BASH
bin/signclient signdocument `
  -host $env:TEST_DRIVE_HOST `
  -port 8445 `
  -workername JarSignerPqc `
  -truststore ..\signer_certs\ManagementCA-chain.jks `
  -truststorepwd changeit `
  -infile ..\testFiles\HelloJar.jar `
  -outfile ..\testFiles\HelloJar-signed.jar

Linux:
2. Verify the signed jar file with jarsigner

BASH
jarsigner -verify \
  ../testFiles/HelloJar-signed.jar

Windows:

BASH
jarsigner -verify \
  ..\testFiles\HelloJar-signed.jar

NOTE: Jarsigner is not able to parse ML-DSA signatures and verification will fail

CMS Signer PQC Test

Sign file with CMS Signer PQC using signclient and verify

  1. Sign the file with signclient
    Linux:

BASH
$ bin/signclient signdocument \
  -host $TEST_DRIVE_HOST \
  -port 8445 \
  -workername CmsSignerPqc \
  -truststore ../signer_certs/ManagementCA-chain.jks \
  -truststorepwd changeit \
  -infile ../testFiles/HelloDeb.deb \
  -outfile ../testFiles/HelloDeb-signed.deb

Windows:

BASH
bin\signclient signdocument `
  -host $env:TEST_DRIVE_HOST `
  -port 8445 `
  -workername CmsSignerPqc `
  -truststore ..\signer_certs\ManagementCA-chain.jks `
  -truststorepwd changeit `
  -infile ..\testFiles\HelloDeb.deb `
  -outfile ..\testFiles\HelloDeb-signed.deb
  1. Verify the file with OpenSSL
    Linux:

BASH
$ openssl cms -verify \
  -in ../testFiles/HelloDeb-signed.deb \
  -inform DER \
  -content ../testFiles/HelloDeb.deb \
  -CAfile ../signer_certs/PqcCaChain.crt \
  -binary \
  > /dev/null
  CMS Verification successful

Windows:

BASH
openssl cms -verify `
  -in ..\testFiles\HelloDeb-signed.deb `
  -inform DER `
  -content ..\testFiles\HelloDeb.deb `
  -CAfile ..\signer_certs\PqcCaChain.crt `
  -binary `
  > $null

SignServer CMS Rest API Test and verify

When Using Windows user PowerShell in Command Prompt.

  1. Select a file to sign, and get the hash of the file:

Linux:

BASH
CMS_HASH=$(openssl dgst -sha384 -binary ../testFiles/HelloPE.cs | base64)

Windows:

BASH
$CMS_HASH=(openssl dgst -sha384 -binary .\testFiles\HelloPE.cs | openssl base64 -A)
  1. Use the SignServer Rest API to get the hash signed with the PQC CMS Signer

Linux:

CODE
$ curl -ksL -X POST \
  "https://$TEST_DRIVE_HOST:8445/signserver/rest/v1/workers/CmsSignerPqc/process" \
  -H "X-Keyfactor-Requested-With: X" \
  -H "Content-Type: application/json" \
  -d "{\"data\": \"$CMS_HASH\",\"encoding\": \"BASE64\",\"metaData\": {\"USING_CLIENTSUPPLIED_HASH\": \"true\",\"CLIENTSIDE_HASHDIGESTALGORITHM\": \"SHA384\"}}" \
  | jq . > ../testFiles/HelloPE.cs.json

Windows:

BASH
curl -ksL -X POST `
  "https://$env:TEST_DRIVE_HOST:8445/signserver/rest/v1/workers/CmsSignerPqc/process" `
  -H "X-Keyfactor-Requested-With: X" `
  -H "Content-Type: application/json" `
  -d "{\"data\": \"$env:CMS_HASH\",\"encoding\": \"BASE64\",\"metaData\": {\"USING_CLIENTSUPPLIED_HASH\": \"true\",\"CLIENTSIDE_HASHDIGESTALGORITHM\": \"SHA384\"}}" `
  | jq . > ..\testFiles\HelloPE.cs.json
  1. Extract the signature of the file from the json file

Linux:

CODE
cat ../testFiles/HelloPE.cs.json \
  | jq -r .data \
  | base64 -d \
  > ../testFiles/HelloPE.cs.sig

Windows (with jq):

BASH
Get-Content ..\testFiles\HelloPE.cs.json `
  | jq -r .data `
  | openssl base64 -d `
  | Set-Content -Encoding Byte ..\testFiles\HelloPE.cs.sig

Windows (with builtin json):

CODE
$json = Get-Content ..\testFiles\HelloPE.cs.json -Raw | ConvertFrom-Json
[IO.File]::WriteAllBytes("..\testFiles\HelloPE.cs.sig", [Convert]::FromBase64String($json.data))
  1. Verify the signature with OpenSSL

Linux:

BASH
openssl cms -verify \
  -in ../testFiles/HelloPE.cs.sig \
  -inform DER \
  -content ../testFiles/HelloPE.cs \
  -CAfile ../signer_certs/PqcCaChain.crt \
  -binary \
  > /dev/null
  CMS Verification successful

Windows:

BASH
openssl cms -verify `
  -in ..\testFiles\HelloPE.cs.sig `
  -inform DER `
  -content ..\testFiles\HelloPE.cs `
  -CAfile ..\signer_certs\PqcCaChain.crt `
  -binary `
  > $null

Next steps

You can also explore the PQC Lab PKI use case:

To learn more about post-quantum cryptography and how you should prepare, see Post-Quantum Readiness.

Contact us

Schedule a live demo with one of our experts to discuss your environment, integration options, and how we can help you move from test drive to production.

Request a Demo

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.