Skip to content

Add support for Venafi CodeSign Protect #286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ <h4 id="attributes" class="mobile-only">Attributes</h4>
<li><code>SIGNPATH</code>: SignPath</li>
<li><code>SIGNSERVER</code>: Keyfactor SignServer</li>
<li><code>TRUSTEDSIGNING</code>: Azure Trusted Signing</li>
<li><code>VENAFI</code>: Venafi CodeSign Protect</li>
</ul>
</td>
<td class="required">No, automatically detected for file based keystores.</td>
Expand Down Expand Up @@ -558,6 +559,7 @@ <h3 id="cli">Command Line Tool</h3>
- SIGNPATH: SignPath
- SIGNSERVER: Keyfactor SignServer
- TRUSTEDSIGNING: Azure Trusted Signing
- VENAFI: Venafi CodeSign Protect
-a,--alias &lt;NAME> The alias of the certificate used for signing in the keystore
--keypass &lt;PASSWORD> The password of the private key. When using a keystore,
this parameter can be omitted if the keystore shares the
Expand Down Expand Up @@ -998,6 +1000,22 @@ <h4 id="example-signpath">Signing with SignPath</h4>
application.exe
</pre>

<h4 id="example-venafi">Signing with Venafi CodeSign Protect</h4>

<p>Signing with <a href="https://venafi.com/codesign-protect/">Venafi CodeSign Protect</a> requires version 23.1 or later
and requires a minimum scope of <code>codesignclient</code> as part of the default <code>VenafiCodeSignClient</code> API integration within the Trust Protection Platform.
The <code>keystore</code> parameter references the URL of the Venafi CodeSign Protect server. The <code>storepass</code> parameter represents
the Key User credential assigned to the CodeSign Protect project. The <code>alias</code> parameter is the certificate label which can be obtained from
the <code>pkcs11config list</code> command.</p>

<pre>
jsign --storetype VENAFI \
--keystore https://example.tpp.local \
--storepass "&lt;username&gt;|&lt;password&gt;" \
--alias my-certificate-label \
application.exe
</pre>


<br>

Expand Down
3 changes: 2 additions & 1 deletion jsign-cli/src/main/java/net/jsign/JsignCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ private Map<String, Options> getOptions() {
+ "- ORACLECLOUD: Oracle Cloud Key Management Service\n"
+ "- SIGNPATH: SignPath\n"
+ "- SIGNSERVER: Keyfactor SignServer\n"
+ "- TRUSTEDSIGNING: Azure Trusted Signing\n").build());
+ "- TRUSTEDSIGNING: Azure Trusted Signing\n"
+ "- VENAFI: Venafi CodeSign Protect\n").build());
options.addOption(Option.builder("a").hasArg().longOpt(PARAM_ALIAS).argName("NAME").desc("The alias of the certificate used for signing in the keystore").build());
options.addOption(Option.builder().hasArg().longOpt(PARAM_KEYPASS).argName("PASSWORD").desc("The password of the private key. When using a keystore, this parameter can be omitted if the keystore shares the same password").build());
options.addOption(Option.builder().hasArg().longOpt(PARAM_KEYFILE).argName("FILE").desc("The file containing the private key. PEM and PVK files are supported").type(File.class).build());
Expand Down
32 changes: 32 additions & 0 deletions jsign-crypto/src/main/java/net/jsign/KeyStoreType.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
import net.jsign.jca.SignServerCredentials;
import net.jsign.jca.SignServerSigningService;
import net.jsign.jca.SigningServiceJcaProvider;
import net.jsign.jca.VenafiSigningService;
import net.jsign.jca.VenafiCredentials;

/**
* Type of a keystore.
Expand Down Expand Up @@ -561,6 +563,36 @@ Provider getProvider(KeyStoreBuilder params) {
}
},


VENAFI(false, false) {
@Override
void validate(KeyStoreBuilder params) {
if (params.storepass() == null || params.storepass().split("\\|").length > 2) {
throw new IllegalArgumentException("storepass " + params.parameterName() + " must specify the Venafi CodeSign Protect username/password: <username>|<password>");
}
}

@Override
Provider getProvider(KeyStoreBuilder params) {
String[] elements = params.storepass().split("\\|");
String username = null;
String password = null;
if (elements.length == 2) {
username = elements[0];
password = elements[1];
}

try {
VenafiCredentials credentials = new VenafiCredentials(username, password, null, params.keypass());
return new SigningServiceJcaProvider(new VenafiSigningService(params.keystore(), credentials));
} catch (IOException e) {
throw new IllegalStateException("Authentication failed with Venafi", e);

}
}
},


/**
* 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
Expand Down
72 changes: 72 additions & 0 deletions jsign-crypto/src/main/java/net/jsign/jca/VenafiCredentials.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2025 Ivan Wallis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.jsign.jca;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;

import net.jsign.KeyStoreBuilder;

/**
* Credentials for the Venafi CodeSign Protect.
*
* @since 7.2
*/
public class VenafiCredentials {

public String username;
public String password;
public KeyStore.Builder keystore;
public String sessionToken;

public VenafiCredentials(String username, String password, String keystore, String storepass) {
this(username, password, new KeyStoreBuilder().keystore(keystore).storepass(storepass).builder());
}

public VenafiCredentials(String username, String password, KeyStore.Builder keystore) {
this.username = username;
this.password = password;
this.keystore = keystore;
}

public String getSessionToken(String endpoint) throws IOException {
if (sessionToken == null) {
RESTClient client = new RESTClient(endpoint)
.errorHandler(response -> response.get("error") + ": " + response.get("error_description"));

Map<String, Object> request = new LinkedHashMap<>();
request.put("client_id", "VenafiCodeSignClient");
request.put("scope", "codesignclient");
if (username != null && password != null) {
request.put("username", username);
request.put("password", password);
}

Map<String, ?> response = client.post("/vedauth/authorize/oauth", JsonWriter.format(request));
sessionToken = (String) response.get("access_token");
}

return sessionToken;
}
}
Loading