Client-Side Hashing
The following describes various options for performing the hashing on the client-side instead of making all the steps in the signing on the server-side. Client-side hashing is also sometimes referred to as "hash signing" and the main advantage is that the original file does not need to be sent to the server.
CMS Detached Signatures
Detached signatures allow the signature to be placed in a separate file next to the original file, and thus the original file does not have to be updated. With CMS signatures, what is covered by the signature is a set of attributes (unless it is a direct signature) where one of the attributes is called messageDigest, which is the hash of the original file. This means that in order to create a CMS detached signature, it is sufficient to get the hash of the original file as input.
The CMS Signer is for instance configured to accept a hash as input by setting CLIENTSIDEHASHING=TRUE, or by allowing the client to specify if the input is the original file or a hash of it, by configuring ALLOW_CLIENTSIDEHASHING_OVERRIDE=TRUE.
When the client sends the request, the following properties need to be provided as request metadata:
USING_CLIENTSUPPLIED_HASH=true
CLIENTSIDE_HASHDIGESTALGORITHM=SHA-256
where SHA-256 should be replaced with the name of the hash algorithm used to digest the data. The algorithm must be one of those configured in the signer with the ACCEPTED_HASH_DIGEST_ALGORITHMS property.
The following commands can be used to create a detached CMS signature using this mode with a CMSSigner set up to default to consider requests as client-side computed hash (using the CLIENTSIDEHASHING property) (assuming a GNU/Linux, or Unix-like system is used):
$ echo
"data-to-be-signed"
| openssl sha256 -binary > pre-computed-hash.bin
$ ./bin/signclient signdocument -workername CMSSigner
-metadata USING_CLIENTSUPPLIED_HASH=
true
-metadata CLIENTSIDE_HASHDIGESTALGORITHM=SHA-
256
-infile pre-computed-hash.bin -outfile signed.p7s
Plain Signatures
For plain signatures (as produced by the Plain Signer), generally the whole file is sent to the signer and returned is the small signature. However, as the plain signature schemes involve a hash operation, it is possible to perform that on the client-side.
SignServer supports the following two options of using plain signatures with client-side hashing:
- Explicitly specifying client-side hashing using request metadata properties: Works with algorithms RSASSA-PKCS1_v1.5, RSASSA-PSS, and ECDSA, for known hash algorithms and is the recommended option as of SignServer version 5.9. See Explicitly using Request Metadata Properties.
- Implicitly using client-side hashing without request metadata properties: Supported for RSASSA-PKCS1_v1.5 and ECDSA but not for RSASSA-PSS. The input may also need special encoding, see Implicitly and With Encoding Depending on Algorithm.
recommended Explicitly using Request Metadata Properties
We recommend explicitly specifying client-side hashing using request metadata properties. This is achieved by configuring the Plain Signer with CLIENTSIDEHASHING=true and SIGNATUREALGORITHM set to one of the NONEwith... signature algorithms and letting the client supply the CLIENTSIDE_HASHDIGESTALGORITHM request metadata property in addition to the hash value as data.
This configuration works with algorithms NONEwithRSA (RSASSA-PKCS1_v1.5), NONEwithRSAandMGF1 (RSASSA-PSS), and NONEwithECDSA, for known hash algorithms and is recommended as of SignServer version 5.9.
When the client sends the request, the following properties need to be provided as request metadata:
- USING_CLIENTSUPPLIED_HASH=true (unless the signer is configured with CLIENTSIDEHASHING=true)
- CLIENTSIDE_HASHDIGESTALGORITHM=SHA-256
where SHA-256 is replaced with the name of the hash algorithm used to digest the data. The algorithm must be one of those configured in the signer with the ACCEPTED_HASH_DIGEST_ALGORITHMS property. For descriptions of available properties, see Plain Signer and for algorithm support, see Plain Signer Algorithm Support.
Example for RSASSA-PKCS1_v1.5
openssl x509 -in PlainSigner-certificate.pem -noout -pubkey > plainsigner-pubkey.pem
openssl dgst -sha256 -out pre-computed-hash.bin -binary sample.txt
bin/signclient signdocument -workername PlainSigner -metadata USING_CLIENTSUPPLIED_HASH=
true
-metadata CLIENTSIDE_HASHDIGESTALGORITHM=SHA-
256
-infile pre-computed-hash.bin -outfile sample.sig
openssl dgst -signature sample.sig -verify plainsigner-pubkey.pem -SHA256 sample.txt
Example for RSASSA-PSS
openssl x509 -in PlainSigner-certificate.pem -noout -pubkey > plainsigner-pubkey.pem
openssl dgst -sha256 -out pre-computed-hash.bin -binary sample.txt
bin/signclient signdocument -workername PlainSigner -metadata USING_CLIENTSUPPLIED_HASH=
true
-metadata CLIENTSIDE_HASHDIGESTALGORITHM=SHA-
256
-infile pre-computed-hash.bin -outfile sample.sig
openssl dgst -signature sample.sig -verify plainsigner-pubkey.pem -SHA256 -sigopt rsa_padding_mode:pss sample.txt
legacy Implicitly and with Encoding Depending on Algorithm
The legacy way of accomplishing client-side hashing is to configure the Plain Signer with a signature algorithm with the word NONE instead of the name of the hash and then let the client take care of sending in the input data encoded in the way expected by that signature algorithm.
In this case, CLIENTSIDEHASHING is set to false, or ALLOW_CLIENTSIDEHASHING_OVERRIDE is set to true, and the request specifies USING_CLIENTSUPPLIED_HASH=false.
Using this configuration, the hash is sent in differently depending on the algorithm:
- NONEwithECDSA (ECDSA): Send in the hash value.
- NONEwithRSA (RSASSA-PKCS1_v1.5): The input should be a DER encoded DigestInfo structure. Please refer to RFC 3447 and specifically page 42 for examples on the exact encoding. Alternatively, if the DigestInfo/ASN.1 structure is not wanted it could be left out when calling the signer.
- NONEwithRSAandMGF1 (RSASSA-PSS): Not supported. Use the option Explicitly using Request Metadata Properties instead.
For descriptions of available properties, see Plain Signer and for algorithm support, see Plain Signer Algorithm Support.
Example for ECDSA
openssl x509 -in PlainSigner-certificate.pem -noout -pubkey > plainsigner-pubkey.pem
openssl dgst -sha256 -out pre-computed-hash.bin -binary sample.txt
bin/signclient signdocument -workername PlainSigner -infile pre-computed-hash.bin -outfile sample.sig
openssl dgst -signature sample.sig -verify plainsigner-pubkey.pem -SHA256 sample.txt
Embedded Signature Formats
For signature formats where the signature is to be placed within the original document, additional logic has to be implemented on the client-side in order to, typically, first prepare the document for signing, compute the digest and send it to the server, and then finally embed the signature within the file.
On the client-side, support has been implemented for "client-side hashing and construction" for various signature formats (see below) for instance for Java Archives (.JAR, .APK,...) and for Authenticode signing of Portable Executable (PE) files (i.e. EXE, .DLL,..), Windows Installer files (.MSI) etc. Support for other file types such as PDF may also be implemented.
On the server-side, typically a CMS Signer is needed but since Authenticode and JAR signing are producing slightly different variants of the CMS signatures, different implementations are available for use. For instance for Authenticode, there is the MS Authenticode CMS Signer, and for JAR signing the JArchive CMS Signer.
Parameter | Description |
---|---|
-clientside | Required in order to use client-side hashing otherwise a normal request is sent. |
-digestalgorithm | The digest algorithm to use for the hash (or for the hashes within the hash file depending on file type). This option may be ignored if the algorithm to be used is determined in other way by the implementation. |
-filetype | Optional. One of the supported file type identifiers (see below). If not provided the implementation tries to guess the file type base on the file extension or content of the file. |
-extraoption KEY=VALUE | Flag to provide extra options for signing supported by a particular file type. |
Authenticode Signing
enterprise
When using the client-side hashing and construction for Authenticode file signing using the MS Authenticode CMS Signer the following options are available. Note that the MS Authenticode Signer supports the following formats:
- PE: Portable Executable files (.exe, .dll, ...)
- MSI: Windows Installer files (.msi)
- PS1: PowerShell (.ps1, .psd, .psm)
- CAB: Cabinet archives (.cab)
Another signer is available for signing of AppX/MSIX. See the APPX Signing section.
Parameter | Description |
---|---|
-clientside | Required in order to use client-side hashing otherwise a normal request is sent. |
-digestalgorithm | The digest algorithm to use for the hash. |
-filetype PE -filetype MSI -filetype PS1 -filetype CAB | Optional. For specifying a particular file type. If not provided SignClient will try to guess the type. |
-extraoption ENCODING= | For more information on the property ENCODING, see MS Authenticode Signer. |
-extraoption KEEPSIGNATURES= | For more information on the property KEEPSIGNATURE, see MS Authenticode Signer. |
Sample Usage
$ signclient signdocument -clientside -workername MSAuthCodeCMSSigner -infile unsigned.exe
-outfile signed.exe -digestalgorithm SHA-
512
$ signclient signdocument -clientside -workername MSAuthCodeCMSSigner -infile unsigned.msi
-outfile signed.msi -digestalgorithm SHA-
256
$ signclient signdocument -clientside -workername MSAuthCodeCMSSigner -infile unsigned.ps1
-outfile signed.ps1 -digestalgorithm SHA-
256
JAR Signing
enterprise
When using the client-side hashing and construction for JAR file signing using the JArchive CMS Signer, the following options are available.
Parameter | Description |
---|---|
-clientside | Required in order to use client-side hashing otherwise a normal request is sent. |
-digestalgorithm | The digest algorithm to use for the hashes in the manifest / signature file. |
-filetype ZIP | Specify that a ZIP/JAR signature is wanted. |
-extraoption KEEPSIGNATURE= | For more information on the property KEEPSIGNATURE, see JArchive Signer. |
-extraoption REPLACESIGNATURE= | For more information on the property REPLACESIGNATURE, see JArchive Signer. |
-extraoption SIGNATURE_NAME_VALUE= | For more information on the property SIGNATURE_NAME_VALUE, see JArchive Signer. |
-extraoption ZIPALIGN= | For more information on the property ZIPALIGN, see JArchive Signer. |
Sample Usage
$ signclient signdocument -clientside -workername JArchiveCMSSigner -infile unsigned.jar
-outfile signed.jar -digestalgorithm SHA-
256
$ signclient signdocument -clientside -workername JArchiveCMSSigner -infile unsigned.apk
-outfile signed.apk -digestalgorithm SHA-
256
-extraoption ZIPALIGN=TRUE -extraoption SIGNATURE_NAME_VALUE=SIGNER02
OpenPGP Signing
enterprise
When using the client-side hashing and construction with OpenPGP using the OpenPGPPlainSigner, the parameters key ID and key algorithm (when using a key with an algorithm other than RSA) need to be specified with the request using the -extraoption
options.
Parameter | Description |
---|---|
-filetype PGP | Specify that an OpenPGP signature is wanted. |
-extraoption KEY_ID= | Key ID (in hex format) for the PGP public key. Default: "3" (RSA) |
-extraoption KEY_ALGORITHM= | Key algorithm, either in text format (RSA, DSA, or ECDSA) or numeric decimal format. |
-extraoption RESPONSE_FORMAT= | Format of the output. Either binary or ASCII armored. Examples: "BINARY", "ARMORED". Optional. Default: "ARMORED". RESPONSE_FORMAT can be only be provided as "ARMORED" when DETACHED_SIGNATURE extra option is provided as "FALSE" |
-extraoption DETACHED_SIGNATURE= | Property specifying if a detached signature should be used or otherwise a clear-text signature. Examples: "true", "false". Required. |
Sample Usage
$ signclient signdocument -workername OpenPGPPlainSigner -clientside -digestalgorithm SHA-
256
-filetype PGP\
-infile file-to-be-signed -outfile signed-file -extraoption KEY_ID=1234567890ABC -extraoption KEY_ALGORITHM=ECDSA\
-extraoption RESPONSE_FORMAT=ARMORED -extraoption DETACHED_SIGNATURE=TRUE
$ signclient signdocument -workername OpenPGPPlainSigner -clientside -digestalgorithm SHA-
256
-filetype PGP\
-infile file-to-be-signed -outfile signed-file -extraoption KEY_ID=1234567890ABC -extraoption KEY_ALGORITHM=ECDSA\
-extraoption RESPONSE_FORMAT=ARMORED -extraoption DETACHED_SIGNATURE=FALSE
Debian Dpkg-sig Signing
enterprise
When using the client-side hashing and construction with Dpkg-sig using the OpenPGPPlainSigner, the key ID, key fingerprint, and key algorithm (when using a key with an algorithm other than RSA) need to be specified with the request using the -extraoption
options.
Parameter | Description |
---|---|
-filetype DPKG_SIG | Specify that a Debian Dpkg-sig signature is wanted. |
-extraoption KEY_ID= | Key ID (in hex format) for the PGP public key. |
-extraoption KEY_ALGORITHM= | Key algorithm, either in text format (RSA, DSA, or ECDSA) or numeric decimal format. |
-extraoption KEY_FINGERPRINT= | Key fingerprint (in hex format) for the PGP public key. This is included in the signed manifest. |
You can run without the required KEY_ options and the server will give an error message that includes the values that can be used.
Algorithm Support
Note that specifically for DSA signatures in client-side mode, other algorithms than SHA1 may not be supported due to the underlying implementation.
Sample Usages
The key ID is the hex-encoded representation, and the key algorithm can be specified as either a name (RSA, DSA, or ECDSA) or using one of the PGP constant values (for example 19 for ECDSA).
$ signclient signdocument -workername OpenPGPPlainSigner -clientside -digestalgorithm SHA-
256
-filetype DPKG_SIG\
-infile hello.deb -outfile hello-signed.deb -extraoption KEY_ID=4B821662F54A5923 -extraoption KEY_ALGORITHM=ECDSA\
-extraoption KEY_FINGERPRINT=23C0B776EEE6A30D6530ACD44B821662F54A5923
$ signclient signdocument -workername OpenPGPPlainSigner -clientside -digestalgorithm SHA-
256
-filetype DPKG_SIG\
-infile hello.deb -outfile hello-signed.deb -extraoption KEY_ID=4B821662F54A5923 -extraoption KEY_ALGORITHM=
19
\
-extraoption KEY_FINGERPRINT=23C0B776EEE6A30D6530ACD44B821662F54A5923
DNSSEC Signing
enterprise
When using the client-side hashing and construction for DNSSEC zone file signing using the ZoneHashSigner, the following -extraoption
options should be provided on the command line in addition to the required request metadata property specifying which keys to use.
Parameter | Description |
---|---|
-clientside | Required in order to use client-side hashing otherwise a normal request is sent. |
-digestalgorithm | The digest algorithm to use for the hash. |
-filetype ZONE_ZIP | Specify that a DNS zone file is provided in the ZIP file. The first file in the archive must be the new (unsigned) zone file. Optionally, the previously signed zone file can be provided after it and in which case valid signatures from it may be re-used. |
-metadata ZSK_SEQUENCE_NUMBER= | Sequence number of the ZSK key to use. The key with this sequence number will be used to sign the record and the next key in sequence will be included (a.k.a. pre-published). |
-extraoption ZONE_NAME= | Absolute domain name. Example: "example.com." Required |
-extraoption MIN_REMAINING_VALIDITY= | Minimum remaining time in seconds required to be able to re-use a previously signed record instead of resigning it. Required unless FORCE_RESIGN=TRUE |
-extraoption FORCE_RESIGN= | If set to "true", do not consider the minimum remaining validity and instead re-sign all records even if they are not expired. This should be done when a new key should be used for signing (i.e. when the ZSK_SEQUENCE_NUMBER is increased). Default: FALSE |
Sample Usages
$ signclient signdocument -workername ZoneHashSigner -clientside -digestalgorithm SHA-
256
-filetype ZONE_ZIP\
-infile example.com.zone.zip -outfile example.com-signed.zone -metadata ZSK_SEQUENCE_NUMBER=
1
-extraoption ZONE_NAME=example.com. -extraoption MIN_REMAINING_VALIDITY=
86400
APPX Signing
enterprise
When using the client-side hashing and construction for Appx file signing using the Appx CMS Signer, the following options are available.
Parameter | Description |
---|---|
-clientside | Required in order to use client-side hashing otherwise a normal request is sent. |
-digestalgorithm | The digest algorithm to use for the hash. |
-filetype APPX | Specify that an APPX file is provided for signing. |
Sample Usages
$ signclient signdocument -workername AppxCMSSigner -infile HelloAppx.appx -outfile HelloAppx-signed.appx -clientside -digestalgorithm SHA-
256
-filetype APPX
APK Signing
enterprise
When using the client-side hashing and construction for Android Package Kit (APK) file signing using the APK Hash Signer, the following -extraoptions can be used.
Parameter | Description |
---|---|
-clientside | Required in order to use client-side hashing otherwise a normal request is sent. |
-digestalgorithm | Any valid digest algorithm value (e.g. "SHA-256") needs to be supplied but is ignored as the algorithm to be used is determined based on key size and API level. |
-filetype APK | Specify that an APK file is provided for signing. |
-extraoption V1_SIGNATURE= | Enable (true) to Include APK version 1 signature. Valid values: true or false. If not set, will use default behavior depending on SDK version. |
-extraoption V2_SIGNATURE= | Enable (true) to Include APK version 2 signature. Valid values: true or false. If not set, will use default behavior depending on SDK version. |
-extraoption V3_SIGNATURE= | Enable (true) to Include APK version 3 signature. Valid values: true or false. If not set, will use default behavior depending on SDK version. |
-extraoption V1_SIGNATURE_NAME= | Minimum SDK version of APK supported to be signed. Valid values: a positive integer. |
-extraoption MIN_SDK_VERSION= | Minimum SDK version of APK supported to be signed. Valid values: a positive integer. |
-extraoption MAX_SDK_VERSION= | Maximum SDK version of APK supported to be signed. Valid values: a positive integer. Note, this parameter is not currently enforced, but reserved for future use. |
-extraoption DEBUGGABLE_APK_PERMITTED= | True if debuggable APK should be allowed to be signed. Valid values: true, or false. If not set, debuggable APKs will not be permitted. |
Algorithm Support
SignClient assumes the server-side signer is configured with either NONEwithECDSA, or NONEwithRSA as signature algorithm. SignClient currently supports SHA1, SHA-256, SHA-384, and SHA-512 with RSA and SHA1, SHA-224, SHA-256, SHA-384, and SHA-512 with ECDSA. DSA is not supported.
Sample Usages
$ signclient -workername ApkHashSigner -infile unsigned.apk -outfile signed.apk -clientside -digestalgorithm SHA-
256
-filetype APK
$ signclient -workername ApkHashSigner -infile unsigned.apk -outfile signed.apk -clientside -digestalgorithm SHA-
256
-filetype APK -extraoption V3_SIGNATURE=
false
$ signclient -workername ApkHashSigner -infile unsigned.apk -outfile signed.apk -clientside -digestalgorithm SHA-
256
-filetype APK -extraoption V1_SIGNATURE=
true