Skip to content

Commit

Permalink
Support server-side hashing for SignServer (#260)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vampire authored and ebourg committed Jan 16, 2025
1 parent bd2a60c commit 545be87
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 19 deletions.
19 changes: 16 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -890,9 +890,12 @@ <h4 id="example-signserver">Signing with Keyfactor SignServer</h4>
<p><a href="https://www.signserver.org">SignServer</a> is a cloud/on-premises open source signing service developed by
Keyfactor. SignServer supports various signing operations handled by signer workers. Jsign requires a
<a href="https://doc.primekey.com/signserver/signserver-reference/signserver-workers/signserver-signers/plain-signer">Plain Signer</a>
worker configured with the <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> properties
set to <code>true</code>, and the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or
<code>NONEwithECDSA</code>.</p>
worker, preferably configured with the <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code>
properties set to <code>true</code>, and the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or
<code>NONEwithECDSA</code>. The worker may be configured with server-side hashing (i.e. with <code>CLIENTSIDEHASHING</code>
and <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set to <code>false</code>, and a proper
<code>SIGNATUREALGORITHM</code> set), in this case the worker name or id in the alias has to be suffixed with
<code>|serverside</code>.</p>

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

<p>Using server-side hashing, the digest algorithm must match the one configured for the worker:</p>

<pre>
jsign --storetype SIGNSERVER \
--keystore https://example.com/signserver \
--alias "test|serverside" \
--alg SHA-512 \
application.exe
</pre>


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

Expand Down
9 changes: 6 additions & 3 deletions jsign-crypto/src/main/java/net/jsign/KeyStoreType.java
Original file line number Diff line number Diff line change
Expand Up @@ -561,9 +561,12 @@ Provider getProvider(KeyStoreBuilder params) {
},

/**
* Keyfactor SignServer. This keystore requires a Plain Signer worker configured to allow client-side hashing (with
* the properties <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set to true), and
* the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or <code>NONEwithECDSA</code>.
* Keyfactor SignServer. This keystore requires a Plain Signer worker, preferably configured to allow client-side
* hashing (with the properties <code>CLIENTSIDEHASHING</code> or <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set
* to true), and the <code>SIGNATUREALGORITHM</code> property set to <code>NONEwithRSA</code> or <code>NONEwithECDSA</code>.
* The worker may be configured with server-side hashing (i.e. with <code>CLIENTSIDEHASHING</code> and
* <code>ALLOW_CLIENTSIDEHASHING_OVERRIDE</code> set to <code>false</code>, and a proper <code>SIGNATUREALGORITHM</code>
* set), in this case the worker name or id in the alias has to be suffixed with <code>|serverside</code>.
*
* <p>If necessary the authentication is performed by specifying the username/password or the TLS client certificate
* in the storepass parameter. If the TLS client certificate is stored in a password protected keystore, the password
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,29 @@ public List<String> aliases() throws KeyStoreException {
public Certificate[] getCertificateChain(String alias) throws KeyStoreException {
if (!certificates.containsKey(alias)) {
try {
Map<String, ?> response = client.post("/rest/v1/workers/" + alias + "/process", "{\"data\":\"\"}");
String worker = alias;
boolean serverside = false;
if (worker.endsWith("|serverside")) {
worker = worker.substring(0, worker.length() - 11);
serverside = true;
}

Map<String, Object> request = new HashMap<>();
if (serverside) {
request.put("data", "");
Map<String, String> metadata = new HashMap<>();
metadata.put("USING_CLIENTSUPPLIED_HASH", "false");
request.put("metaData", metadata);
} else {
request.put("data", "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
request.put("encoding", "BASE64");
Map<String, String> metadata = new HashMap<>();
metadata.put("USING_CLIENTSUPPLIED_HASH", "true");
metadata.put("CLIENTSIDE_HASHDIGESTALGORITHM", "SHA-256");
request.put("metaData", metadata);
}

Map<String, ?> response = client.post("/rest/v1/workers/" + worker + "/process", JsonWriter.format(request));
String encodedCertificate = response.get("signerCertificate").toString();
byte[] certificateBytes = Base64.getDecoder().decode(encodedCertificate);
Certificate certificate = CertificateFactory.getInstance("X.509")
Expand All @@ -120,19 +142,32 @@ public SigningServicePrivateKey getPrivateKey(String alias, char[] password) thr

@Override
public byte[] sign(SigningServicePrivateKey privateKey, String algorithm, byte[] data) throws GeneralSecurityException {
DigestAlgorithm digestAlgorithm = DigestAlgorithm.of(algorithm.substring(0, algorithm.toLowerCase().indexOf("with")));
data = digestAlgorithm.getMessageDigest().digest(data);
String worker = privateKey.getId();
boolean serverside = false;
if (worker.endsWith("|serverside")) {
worker = worker.substring(0, worker.length() - 11);
serverside = true;
}

Map<String, Object> request = new HashMap<>();
request.put("data", Base64.getEncoder().encodeToString(data));
if (serverside) {
request.put("data", Base64.getEncoder().encodeToString(data));
Map<String, String> metadata = new HashMap<>();
metadata.put("USING_CLIENTSUPPLIED_HASH", "false");
request.put("metaData", metadata);
} else {
DigestAlgorithm digestAlgorithm = DigestAlgorithm.of(algorithm.substring(0, algorithm.toLowerCase().indexOf("with")));
data = digestAlgorithm.getMessageDigest().digest(data);
request.put("data", Base64.getEncoder().encodeToString(data));
Map<String, String> metadata = new HashMap<>();
metadata.put("USING_CLIENTSUPPLIED_HASH", "true");
metadata.put("CLIENTSIDE_HASHDIGESTALGORITHM", digestAlgorithm.id);
request.put("metaData", metadata);
}
request.put("encoding", "BASE64");
Map<String, String> metadata = new HashMap<>();
metadata.put("USING_CLIENTSUPPLIED_HASH", "true");
metadata.put("CLIENTSIDE_HASHDIGESTALGORITHM", digestAlgorithm.id);
request.put("metaData", metadata);

try {
Map<String, ?> response = client.post("/rest/v1/workers/" + privateKey.getId() + "/process", JsonWriter.format(request));
Map<String, ?> response = client.post("/rest/v1/workers/" + worker + "/process", JsonWriter.format(request));
return Base64.getDecoder().decode((String) response.get("data"));
} catch (IOException e) {
throw new GeneralSecurityException(e);
Expand Down
19 changes: 15 additions & 4 deletions jsign/src/deb/data/usr/share/man/man1/jsign.1
Original file line number Diff line number Diff line change
Expand Up @@ -512,13 +512,14 @@ jsign --storetype HASHICORPVAULT \\

.TP


Signing with Keyfactor SignServer:

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

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

Using server-side hashing, the digest algorithm must match the one configured for the worker:

jsign --storetype SIGNSERVER \
--keystore https://example.com/signserver \
--alias 'test|serverside' \
--alg SHA-512 \
application.exe


.TP

Signing with Oracle Cloud Key Management Service

Expand Down

0 comments on commit 545be87

Please sign in to comment.