Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 545be87

Browse files
Vampireebourg
authored andcommittedJan 16, 2025·
Support server-side hashing for SignServer (#260)
1 parent bd2a60c commit 545be87

File tree

4 files changed

+81
-19
lines changed

4 files changed

+81
-19
lines changed
 

‎docs/index.html

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -890,9 +890,12 @@ <h4 id="example-signserver">Signing with Keyfactor SignServer</h4>
890890
<p><a href="https://www.signserver.org">SignServer</a> is a cloud/on-premises open source signing service developed by
891891
Keyfactor. SignServer supports various signing operations handled by signer workers. Jsign requires a
892892
<a href="https://doc.primekey.com/signserver/signserver-reference/signserver-workers/signserver-signers/plain-signer">Plain Signer</a>
893-
worker configured with the <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> properties
894-
set to <code>true</code>, and the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or
895-
<code>NONEwithECDSA</code>.</p>
893+
worker, preferably configured with the <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code>
894+
properties set to <code>true</code>, and the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or
895+
<code>NONEwithECDSA</code>. The worker may be configured with server-side hashing (i.e. with <code>CLIENTSIDEHASHING</code>
896+
and <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set to <code>false</code>, and a proper
897+
<code>SIGNATUREALGORITHM</code> set), in this case the worker name or id in the alias has to be suffixed with
898+
<code>|serverside</code>.</p>
896899

897900
<p>If necessary the authentication is performed by specifying the username/password or the TLS client certificate in the
898901
<code>storepass</code> parameter. If the TLS client certificate is stored in a password protected keystore, the password
@@ -920,6 +923,16 @@ <h4 id="example-signserver">Signing with Keyfactor SignServer</h4>
920923
application.exe
921924
</pre>
922925

926+
<p>Using server-side hashing, the digest algorithm must match the one configured for the worker:</p>
927+
928+
<pre>
929+
jsign --storetype SIGNSERVER \
930+
--keystore https://example.com/signserver \
931+
--alias "test|serverside" \
932+
--alg SHA-512 \
933+
application.exe
934+
</pre>
935+
923936

924937
<h4 id="example-oraclecloud">Signing with Oracle Cloud Key Management Service</h4>
925938

‎jsign-crypto/src/main/java/net/jsign/KeyStoreType.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,9 +561,12 @@ Provider getProvider(KeyStoreBuilder params) {
561561
},
562562

563563
/**
564-
* Keyfactor SignServer. This keystore requires a Plain Signer worker configured to allow client-side hashing (with
565-
* the properties <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set to true), and
566-
* the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or <code>NONEwithECDSA</code>.
564+
* Keyfactor SignServer. This keystore requires a Plain Signer worker, preferably configured to allow client-side
565+
* hashing (with the properties <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set
566+
* to true), and the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or <code>NONEwithECDSA</code>.
567+
* The worker may be configured with server-side hashing (i.e. with <code>CLIENTSIDEHASHING</code> and
568+
* <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set to <code>false</code>, and a proper <code>SIGNATUREALGORITHM</code>
569+
* set), in this case the worker name or id in the alias has to be suffixed with <code>|serverside</code>.
567570
*
568571
* <p>If necessary the authentication is performed by specifying the username/password or the TLS client certificate
569572
* in the storepass parameter. If the TLS client certificate is stored in a password protected keystore, the password

‎jsign-crypto/src/main/java/net/jsign/jca/SignServerSigningService.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,29 @@ public List<String> aliases() throws KeyStoreException {
9494
public Certificate[] getCertificateChain(String alias) throws KeyStoreException {
9595
if (!certificates.containsKey(alias)) {
9696
try {
97-
Map<String, ?> response = client.post("/rest/v1/workers/" + alias + "/process", "{\"data\":\"\"}");
97+
String worker = alias;
98+
boolean serverside = false;
99+
if (worker.endsWith("|serverside")) {
100+
worker = worker.substring(0, worker.length() - 11);
101+
serverside = true;
102+
}
103+
104+
Map<String, Object> request = new HashMap<>();
105+
if (serverside) {
106+
request.put("data", "");
107+
Map<String, String> metadata = new HashMap<>();
108+
metadata.put("USING_CLIENTSUPPLIED_HASH", "false");
109+
request.put("metaData", metadata);
110+
} else {
111+
request.put("data", "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
112+
request.put("encoding", "BASE64");
113+
Map<String, String> metadata = new HashMap<>();
114+
metadata.put("USING_CLIENTSUPPLIED_HASH", "true");
115+
metadata.put("CLIENTSIDE_HASHDIGESTALGORITHM", "SHA-256");
116+
request.put("metaData", metadata);
117+
}
118+
119+
Map<String, ?> response = client.post("/rest/v1/workers/" + worker + "/process", JsonWriter.format(request));
98120
String encodedCertificate = response.get("signerCertificate").toString();
99121
byte[] certificateBytes = Base64.getDecoder().decode(encodedCertificate);
100122
Certificate certificate = CertificateFactory.getInstance("X.509")
@@ -120,19 +142,32 @@ public SigningServicePrivateKey getPrivateKey(String alias, char[] password) thr
120142

121143
@Override
122144
public byte[] sign(SigningServicePrivateKey privateKey, String algorithm, byte[] data) throws GeneralSecurityException {
123-
DigestAlgorithm digestAlgorithm = DigestAlgorithm.of(algorithm.substring(0, algorithm.toLowerCase().indexOf("with")));
124-
data = digestAlgorithm.getMessageDigest().digest(data);
145+
String worker = privateKey.getId();
146+
boolean serverside = false;
147+
if (worker.endsWith("|serverside")) {
148+
worker = worker.substring(0, worker.length() - 11);
149+
serverside = true;
150+
}
125151

126152
Map<String, Object> request = new HashMap<>();
127-
request.put("data", Base64.getEncoder().encodeToString(data));
153+
if (serverside) {
154+
request.put("data", Base64.getEncoder().encodeToString(data));
155+
Map<String, String> metadata = new HashMap<>();
156+
metadata.put("USING_CLIENTSUPPLIED_HASH", "false");
157+
request.put("metaData", metadata);
158+
} else {
159+
DigestAlgorithm digestAlgorithm = DigestAlgorithm.of(algorithm.substring(0, algorithm.toLowerCase().indexOf("with")));
160+
data = digestAlgorithm.getMessageDigest().digest(data);
161+
request.put("data", Base64.getEncoder().encodeToString(data));
162+
Map<String, String> metadata = new HashMap<>();
163+
metadata.put("USING_CLIENTSUPPLIED_HASH", "true");
164+
metadata.put("CLIENTSIDE_HASHDIGESTALGORITHM", digestAlgorithm.id);
165+
request.put("metaData", metadata);
166+
}
128167
request.put("encoding", "BASE64");
129-
Map<String, String> metadata = new HashMap<>();
130-
metadata.put("USING_CLIENTSUPPLIED_HASH", "true");
131-
metadata.put("CLIENTSIDE_HASHDIGESTALGORITHM", digestAlgorithm.id);
132-
request.put("metaData", metadata);
133168

134169
try {
135-
Map<String, ?> response = client.post("/rest/v1/workers/" + privateKey.getId() + "/process", JsonWriter.format(request));
170+
Map<String, ?> response = client.post("/rest/v1/workers/" + worker + "/process", JsonWriter.format(request));
136171
return Base64.getDecoder().decode((String) response.get("data"));
137172
} catch (IOException e) {
138173
throw new GeneralSecurityException(e);

‎jsign/src/deb/data/usr/share/man/man1/jsign.1

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -512,13 +512,14 @@ jsign --storetype HASHICORPVAULT \\
512512

513513
.TP
514514

515-
516515
Signing with Keyfactor SignServer:
517516

518517
SignServer is a cloud/on-premises open source signing service developed by Keyfactor. SignServer supports various
519-
signing operations handled by signer workers. Jsign requires a Plain Signer worker configured with the CLIENTSIDEHASHING
520-
or ALLOW_CLIENTSIDEHASHING_OVERRIDE properties set to true, and the SIGNATUREALGORITHM property set to NONEwithRSA or
521-
NONEwithECDSA.
518+
signing operations handled by signer workers. Jsign requires a Plain Signer worker, preferably configured with the
519+
CLIENTSIDEHASHING or ALLOW_CLIENTSIDEHASHING_OVERRIDE properties set to true, and the SIGNATUREALGORITHM property
520+
set to NONEwithRSA or NONEwithECDSA. The worker may be configured with server-side hashing (i.e. with CLIENTSIDEHASHING
521+
and ALLOW_CLIENTSIDEHASHING_OVERRIDE set to false, and a proper SIGNATUREALGORITHM set), in this case the worker name
522+
or id in the alias has to be suffixed with '|serverside'.
522523

523524
If necessary the authentication is performed by specifying the username/password or the TLS client certificate in the
524525
storepass parameter. If the TLS client certificate is stored in a password protected keystore, the password
@@ -542,6 +543,16 @@ jsign --storetype SIGNSERVER \
542543
--alias test \
543544
application.exe
544545

546+
Using server-side hashing, the digest algorithm must match the one configured for the worker:
547+
548+
jsign --storetype SIGNSERVER \
549+
--keystore https://example.com/signserver \
550+
--alias 'test|serverside' \
551+
--alg SHA-512 \
552+
application.exe
553+
554+
555+
.TP
545556

546557
Signing with Oracle Cloud Key Management Service
547558

0 commit comments

Comments
 (0)
Please sign in to comment.