Skip to content

Commit

Permalink
GaraSign support
Browse files Browse the repository at this point in the history
  • Loading branch information
ebourg committed May 29, 2024
1 parent ab9ca93 commit ea3ead8
Show file tree
Hide file tree
Showing 18 changed files with 1,014 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Jsign is free to use and licensed under the [Apache License version 2.0](https:/
* [Azure Key Vault](https://azure.microsoft.com/services/key-vault/)
* [Azure Trusted Signing](https://learn.microsoft.com/en-us/azure/trusted-signing/)
* [DigiCert ONE](https://one.digicert.com)
* [GaraSign](https://garantir.io/garasign/)
* [Google Cloud KMS](https://cloud.google.com/security-key-management)
* [HashiCorp Vault](https://www.vaultproject.io/)
* [Oracle Cloud KMS](https://www.oracle.com/security/cloud-security/key-management/)
Expand All @@ -55,6 +56,7 @@ See https://ebourg.github.io/jsign for more information.

* The Azure Trusted Signing service has been integrated
* The Oracle Cloud signing service has been integrated
* The GaraSign signing service has been integrated
* Signing of NuGet packages has been implemented (contributed by Sebastian Stamm)
* The intermediate certificates are downloaded if missing from the keystore or the certificate chain file
* File list files prefixed with `@` are now supported with the command line tool to sign multiple files
Expand Down
31 changes: 31 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ <h3 id="features">Features</h3>
<li><a href="https://azure.microsoft.com/services/key-vault/">Azure Key Vault</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/trusted-signing/">Azure Trusted Signing</a></li>
<li><a href="https://one.digicert.com">DigiCert ONE</a></li>
<li><a href="https://garantir.io/garasign/">GaraSign</a></li>
<li><a href="https://cloud.google.com/security-key-management">Google Cloud KMS</a></li>
<li><a href="https://www.vaultproject.io">HashiCorp Vault</a></li>
<li><a href="https://www.oracle.com/security/cloud-security/key-management/">Oracle Cloud KMS</a></li>
Expand Down Expand Up @@ -198,6 +199,7 @@ <h4 id="attributes" class="mobile-only">Attributes</h4>
<li><code>AZUREKEYVAULT</code>: Azure Key Vault key management system</li>
<li><code>DIGICERTONE</code>: DigiCert ONE Secure Software Manager</li>
<li><code>ESIGNER</code>: SSL.com eSigner</li>
<li><code>GARASIGN</code>: Garantir Remote Signing</li>
<li><code>GOOGLECLOUD</code>: Google Cloud KMS</li>
<li><code>HASHICORPVAULT</code>: Google Cloud KMS via HashiCorp Vault</li>
<li><code>ORACLECLOUD</code>: Oracle Cloud Key Management Service</li>
Expand Down Expand Up @@ -505,6 +507,7 @@ <h3 id="cli">Command Line Tool</h3>
- AZUREKEYVAULT: Azure Key Vault key management system
- DIGICERTONE: DigiCert ONE Secure Software Manager
- ESIGNER: SSL.com eSigner
- GARASIGN: Garantir Remote Signing
- GOOGLECLOUD: Google Cloud KMS
- HASHICORPVAULT: Google Cloud KMS via HashiCorp Vault
- ORACLECLOUD: Oracle Cloud Key Management Service
Expand Down Expand Up @@ -737,6 +740,34 @@ <h4 id="example-sslcom-esigner">Signing with SSL.com eSigner</h4>
<p>SSL.com provides a sandbox environment, to use a test certificate simply add the parameter
<code>--keystore https://cs-try.ssl.com</code>.</p>

<h4 id="example-garasign">Signing with GaraSign</h4>

<p><a href="https://garantir.io/garasign/">GaraSign</a> is a remote signing service provided by Garantir.
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 is specified in the <code>keypass</code> parameter. The <code>keystore</code> parameter references
the URL of the GaraSign REST API (https://garasign.com:8443/CodeSigningRestService/ by default).</p>

<p>Authenticating with a username and a password:</p>

<pre>
jsign --storetype GARASIGN \
--storepass "&lt;username&gt;|&lt;password&gt;" \
--alias test \
application.exe
</pre>

<p>Authenticating with a TLS client certificate and a non-default endpoint:</p>

<pre>
jsign --storetype GARASIGN \
--keystore https://demo.garantir.io/CodeSigningRestService \
--storepass "/path/to/client-certificate.p12" \
--keypass &lt;client-certificate-password&gt; \
--alias test \
application.exe
</pre>

<h4 id="example-googlecloud">Signing with Google Cloud KMS</h4>

<p>Google Cloud KMS stores only the private key, the certificate must be provided separately. The keystore parameter
Expand Down
1 change: 1 addition & 0 deletions jsign-cli/src/main/java/net/jsign/JsignCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public static void main(String... args) {
+ "- AZUREKEYVAULT: Azure Key Vault key management system\n"
+ "- DIGICERTONE: DigiCert ONE Secure Software Manager\n"
+ "- ESIGNER: SSL.com eSigner\n"
+ "- GARASIGN: Garantir Remote Signing\n"
+ "- GOOGLECLOUD: Google Cloud KMS\n"
+ "- HASHICORPVAULT: Google Cloud KMS via HashiCorp Vault\n"
+ "- ORACLECLOUD: Oracle Cloud Key Management Service\n"
Expand Down
18 changes: 18 additions & 0 deletions jsign-core/src/main/java/net/jsign/KeyStoreBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,22 @@ public KeyStore build() throws KeyStoreException {
validate();
return storetype().getKeystore(this, provider());
}

/**
* Returns a java.security.KeyStore.Builder using the parameters of this builder.
*/
public KeyStore.Builder builder() {
return new KeyStore.Builder() {
@Override
public KeyStore getKeyStore() throws KeyStoreException {
return build();
}

@Override
public KeyStore.ProtectionParameter getProtectionParameter(String alias) {
String storepass = storepass();
return new KeyStore.PasswordProtection(storepass != null ? storepass.toCharArray() : null);
}
};
}
}
32 changes: 32 additions & 0 deletions jsign-core/src/main/java/net/jsign/KeyStoreType.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import net.jsign.jca.AzureTrustedSigningService;
import net.jsign.jca.DigiCertOneSigningService;
import net.jsign.jca.ESignerSigningService;
import net.jsign.jca.GaraSignCredentials;
import net.jsign.jca.GaraSignSigningService;
import net.jsign.jca.GoogleCloudSigningService;
import net.jsign.jca.HashiCorpVaultSigningService;
import net.jsign.jca.OpenPGPCardSigningService;
Expand Down Expand Up @@ -511,6 +513,36 @@ void validate(KeyStoreBuilder params) {
Provider getProvider(KeyStoreBuilder params) {
return new SigningServiceJcaProvider(new AzureTrustedSigningService(params.keystore(), params.storepass()));
}
},

GARASIGN(false, false, false) {
@Override
void validate(KeyStoreBuilder params) {
if (params.storepass() == null || params.storepass().split("\\|").length > 3) {
throw new IllegalArgumentException("storepass " + params.parameterName() + " must specify the GaraSign username/password and/or the path to the keystore containing the TLS client certificate: <username>|<password>, <certificate>, or <username>|<password>|<certificate>");
}
}

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

GaraSignCredentials credentials = new GaraSignCredentials(username, password, certificate, params.keypass());
return new SigningServiceJcaProvider(new GaraSignSigningService(params.keystore(), credentials));
}
};


Expand Down
88 changes: 88 additions & 0 deletions jsign-core/src/main/java/net/jsign/jca/GaraSignCredentials.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright 2024 Emmanuel Bourg
*
* 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 Garantir Remote Signing service.
*
* @since 6.1
*/
public class GaraSignCredentials {

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

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

public GaraSignCredentials(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)
.authentication((conn, data) -> {
if (conn instanceof HttpsURLConnection && keystore != null) {
try {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore.getKeyStore(), ((KeyStore.PasswordProtection) keystore.getProtectionParameter("")).getPassword());

SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, new SecureRandom());
((HttpsURLConnection) conn).setSSLSocketFactory(context.getSocketFactory());
} catch (GeneralSecurityException e) {
throw new RuntimeException("Unable to load the GaraSign client certificate", e);
}
}
});

Map<String, String> params = new LinkedHashMap<>();
params.put("api_version", "1.0");
if (username != null && password != null) {
params.put("username", username);
params.put("password", password);
}

Map<String, ?> response = client.post("/authenticate", params);
String status = (String) response.get("status");
if (!"SUCCESS".equals(status)) {
throw new IOException("Failed to authenticate with GaraSign: " + response.get("message"));
}
sessionToken = (String) response.get("sessionToken");
}

return sessionToken;
}
}
Loading

0 comments on commit ea3ead8

Please sign in to comment.