diff --git a/README.md b/README.md index 52ef76f7..92a1823d 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Jsign is free to use and licensed under the [Apache License version 2.0](https:/ * Cloud key management systems: * [AWS KMS](https://aws.amazon.com/kms/) * [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) * [Google Cloud KMS](https://cloud.google.com/security-key-management) * [HashiCorp Vault](https://www.vaultproject.io/) @@ -51,6 +52,7 @@ See https://ebourg.github.io/jsign for more information. #### Version 6.1 (in development) +* The Azure Trusted Signing service has been integrated * The Oracle Cloud signing service has been integrated * Signing of NuGet packages has been implemented (contributed by Sebastian Stamm) * Jsign now checks if the certificate subject matches the app manifest publisher before signing APPX/MSIX packages diff --git a/docs/index.html b/docs/index.html index 205df81e..083881c3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -70,6 +70,7 @@

Features

No, automatically detected for file based keystores. @@ -472,6 +474,7 @@

Command Line Tool

- GOOGLECLOUD: Google Cloud KMS - HASHICORPVAULT: Google Cloud KMS via HashiCorp Vault - ORACLECLOUD: Oracle Cloud Key Management Service + - TRUSTEDSIGNING: Azure Trusted Signing -a,--alias <NAME> The alias of the certificate used for signing in the keystore. --keypass <PASSWORD> The password of the private key. When using a keystore, this parameter can be omitted if the keystore shares the @@ -641,6 +644,35 @@

Signing with Azure Key Vault

The Azure account used must have the "Key Vault Crypto User" and "Key Vault Certificate User" roles.

+

Signing with Azure Trusted Signing

+ +

With the Azure Trusted Signing service +the keystore parameter specifies the endpoint URI, and the alias combines the account name and +the certificate profile. The Azure API access token is used as the keystore password.

+ +
+ jsign --storetype TRUSTEDSIGNING \
+       --keystore weu.codesigning.azure.net \
+       --storepass <api-access-token> \
+       --alias <account>/<profile> application.exe
+
+ +

The access token can be obtained with the Azure CLI:

+ +
+ az account get-access-token --resource https://codesigning.azure.net
+
+ +

The Azure account used must have the "Code Signing Certificate Profile Signer" role.

+ +

The certificates issued by Azure Trusted Signing have a lifetime of 3 days only, and timestamping is necessary to +ensure the long term validity of the signature. For this reason timestamping is automatically enabled when signing +with this service.

+ +

Implementation note: Jsign performs an extra call to the signing API to retrieve the current certificate chain before +signing. When signing multiple files it's recommended to invoke Jsign only once with the list of files to avoid doubling +the quota usage.

+

Signing with DigiCert ONE

Certificates and keys stored in the DigiCert ONE Secure Software Manager diff --git a/jsign-cli/src/main/java/net/jsign/JsignCLI.java b/jsign-cli/src/main/java/net/jsign/JsignCLI.java index f57b4f14..089329d5 100644 --- a/jsign-cli/src/main/java/net/jsign/JsignCLI.java +++ b/jsign-cli/src/main/java/net/jsign/JsignCLI.java @@ -78,7 +78,8 @@ public static void main(String... args) { + "- ESIGNER: SSL.com eSigner\n" + "- GOOGLECLOUD: Google Cloud KMS\n" + "- HASHICORPVAULT: Google Cloud KMS via HashiCorp Vault\n" - + "- ORACLECLOUD: Oracle Cloud Key Management Service\n").build()); + + "- ORACLECLOUD: Oracle Cloud Key Management Service\n" + + "- TRUSTEDSIGNING: Azure Trusted Signing\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()); diff --git a/jsign-core/src/main/java/net/jsign/KeyStoreType.java b/jsign-core/src/main/java/net/jsign/KeyStoreType.java index 36eab934..c30bf05c 100644 --- a/jsign-core/src/main/java/net/jsign/KeyStoreType.java +++ b/jsign-core/src/main/java/net/jsign/KeyStoreType.java @@ -37,6 +37,7 @@ import net.jsign.jca.AmazonCredentials; import net.jsign.jca.AmazonSigningService; import net.jsign.jca.AzureKeyVaultSigningService; +import net.jsign.jca.AzureTrustedSigningService; import net.jsign.jca.DigiCertOneSigningService; import net.jsign.jca.ESignerSigningService; import net.jsign.jca.GoogleCloudSigningService; @@ -469,6 +470,30 @@ Provider getProvider(KeyStoreBuilder params) { } return new SigningServiceJcaProvider(new OracleCloudSigningService(credentials, getCertificateStore(params))); } + }, + + /** + * Azure Trusted Signing Service. The keystore parameter specifies the API endpoint (for example + * weu.codesigning.azure.net). The Azure API access token is used as the keystore password, + * it can be obtained using the Azure CLI with: + * + *

  az account get-access-token --resource https://codesigning.azure.net
+ */ + TRUSTEDSIGNING(false, false, false) { + @Override + void validate(KeyStoreBuilder params) { + if (params.keystore() == null) { + throw new IllegalArgumentException("keystore " + params.parameterName() + " must specify the Azure endpoint (.codesigning.azure.net)"); + } + if (params.storepass() == null) { + throw new IllegalArgumentException("storepass " + params.parameterName() + " must specify the Azure API access token"); + } + } + + @Override + Provider getProvider(KeyStoreBuilder params) { + return new SigningServiceJcaProvider(new AzureTrustedSigningService(params.keystore(), params.storepass())); + } }; diff --git a/jsign-core/src/main/java/net/jsign/SignerHelper.java b/jsign-core/src/main/java/net/jsign/SignerHelper.java index 6e661e79..d4549700 100644 --- a/jsign-core/src/main/java/net/jsign/SignerHelper.java +++ b/jsign-core/src/main/java/net/jsign/SignerHelper.java @@ -384,6 +384,13 @@ private AuthenticodeSigner build() throws SignerException { } catch (Exception e) { throw new SignerException("Couldn't initialize proxy", e); } + + // enable timestamping with Azure Trusted Signing + if (tsaurl == null && storetype == KeyStoreType.TRUSTEDSIGNING) { + tsaurl = "http://timestamp.acs.microsoft.com/"; + tsmode = TimestampingMode.RFC3161.name(); + tsretries = 3; + } // configure the signer return new AuthenticodeSigner(chain, privateKey) diff --git a/jsign-core/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-core/src/main/java/net/jsign/jca/AzureTrustedSigningService.java new file mode 100644 index 00000000..6f247d56 --- /dev/null +++ b/jsign-core/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -0,0 +1,189 @@ +/** + * 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.ByteArrayInputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStoreException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.cedarsoftware.util.io.JsonWriter; + +import net.jsign.DigestAlgorithm; + +/** + * Signing service using the Azure Trusted Signing API. + * + * @since 6.1 + */ +public class AzureTrustedSigningService implements SigningService { + + /** Cache of certificate chains indexed by alias */ + private final Map certificates = new HashMap<>(); + + private final RESTClient client; + + /** Timeout in seconds for the signing operation */ + private long timeout = 60; + + /** + * Mapping between Java and Azure signing algorithms. + * @see Key Vault API - JonWebKeySignatureAlgorithm + */ + private final Map algorithmMapping = new HashMap<>(); + { + algorithmMapping.put("SHA256withRSA", "RS256"); + algorithmMapping.put("SHA384withRSA", "RS384"); + algorithmMapping.put("SHA512withRSA", "RS512"); + algorithmMapping.put("SHA256withECDSA", "ES256"); + algorithmMapping.put("SHA384withECDSA", "ES384"); + algorithmMapping.put("SHA512withECDSA", "ES512"); + algorithmMapping.put("SHA256withRSA/PSS", "PS256"); + algorithmMapping.put("SHA384withRSA/PSS", "PS384"); + algorithmMapping.put("SHA512withRSA/PSS", "PS512"); + } + + public AzureTrustedSigningService(String endpoint, String token) { + if (!endpoint.startsWith("http")) { + endpoint = "https://" + endpoint; + } + client = new RESTClient(endpoint, conn -> conn.setRequestProperty("Authorization", "Bearer " + token)); + } + + void setTimeout(int timeout) { + this.timeout = timeout; + } + + @Override + public String getName() { + return "TrustedSigning"; + } + + @Override + public List aliases() throws KeyStoreException { + return new ArrayList<>(); + } + + @Override + public Certificate[] getCertificateChain(String alias) throws KeyStoreException { + if (!certificates.containsKey(alias)) { + try { + String account = alias.substring(0, alias.indexOf('/')); + String profile = alias.substring(alias.indexOf('/') + 1); + SignStatus status = sign(account, profile, "RS256", new byte[32]); + certificates.put(alias, status.getCertificateChain().toArray(new Certificate[0])); + } catch (Exception e) { + throw new KeyStoreException("Unable to retrieve the certificate chain '" + alias + "'", e); + } + } + + return certificates.get(alias); + } + + @Override + public SigningServicePrivateKey getPrivateKey(String alias, char[] password) throws UnrecoverableKeyException { + return new SigningServicePrivateKey(alias, "RSA", this); + } + + @Override + public byte[] sign(SigningServicePrivateKey privateKey, String algorithm, byte[] data) throws GeneralSecurityException { + String alg = algorithmMapping.get(algorithm); + if (alg == null) { + throw new InvalidAlgorithmParameterException("Unsupported signing algorithm: " + algorithm); + } + + DigestAlgorithm digestAlgorithm = DigestAlgorithm.of(algorithm.substring(0, algorithm.toLowerCase().indexOf("with"))); + data = digestAlgorithm.getMessageDigest().digest(data); + + String alias = privateKey.getId(); + String account = alias.substring(0, alias.indexOf('/')); + String profile = alias.substring(alias.indexOf('/') + 1); + try { + SignStatus status = sign(account, profile, alg, data); + return status.signature; + } catch (IOException e) { + throw new GeneralSecurityException(e); + } + } + + private SignStatus sign(String account, String profile, String algorithm, byte[] data) throws IOException { + Map request = new HashMap<>(); + request.put("signatureAlgorithm", algorithm); + request.put("digest", Base64.getEncoder().encodeToString(data)); + + Map args = new HashMap<>(); + args.put(JsonWriter.TYPE, "false"); + + Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2022-06-15-preview", JsonWriter.objectToJson(request, args)); + + String operationId = (String) response.get("operationId"); + + // poll until the operation is completed + long startTime = System.currentTimeMillis(); + int i = 0; + while (System.currentTimeMillis() - startTime < timeout * 1000) { + try { + Thread.sleep(Math.min(1000, 50 + 10 * i++)); + } catch (InterruptedException e) { + break; + } + response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2022-06-15-preview"); + String status = (String) response.get("status"); + if ("InProgress".equals(status)) { + continue; + } + if ("Succeeded".equals(status)) { + break; + } + + throw new IOException("Signing operation " + operationId + " failed: " + status); + } + + if (!"Succeeded".equals(response.get("status"))) { + throw new IOException("Signing operation " + operationId + " timed out"); + } + + SignStatus status = new SignStatus(); + status.signature = Base64.getDecoder().decode((String) response.get("signature")); + status.signingCertificate = new String(Base64.getDecoder().decode((String) response.get("signingCertificate"))); + + return status; + } + + private static class SignStatus { + public byte[] signature; + public String signingCertificate; + + public Collection getCertificateChain() throws CertificateException { + byte[] cerbin = Base64.getMimeDecoder().decode(signingCertificate); + + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + return certificateFactory.generateCertificates(new ByteArrayInputStream(cerbin)); + } + } +} diff --git a/jsign-core/src/main/java/net/jsign/jca/RESTClient.java b/jsign-core/src/main/java/net/jsign/jca/RESTClient.java index d9314819..fb31367c 100644 --- a/jsign-core/src/main/java/net/jsign/jca/RESTClient.java +++ b/jsign-core/src/main/java/net/jsign/jca/RESTClient.java @@ -26,6 +26,7 @@ import java.util.function.Consumer; import com.cedarsoftware.util.io.JsonReader; +import com.cedarsoftware.util.io.JsonWriter; import org.apache.commons.io.IOUtils; class RESTClient { @@ -136,6 +137,10 @@ private String getErrorMessage(Map response) { String error = (String) response.get("__type"); String description = (String) response.get("message"); message.append(error).append(": ").append(description); + } else if (response.containsKey("title") && response.containsKey("errors")) { + // error from Azure Code Signing API + String errors = JsonWriter.objectToJson(response.get("errors")); + message.append(response.get("status")).append(" - ").append(response.get("title")).append(": ").append(errors); } else if (response.containsKey("code") && response.containsKey("message")) { // error from OCI API message.append(response.get("code")).append(": ").append(response.get("message")); diff --git a/jsign-core/src/test/java/net/jsign/KeyStoreBuilderTest.java b/jsign-core/src/test/java/net/jsign/KeyStoreBuilderTest.java index c79c148d..3a813134 100644 --- a/jsign-core/src/test/java/net/jsign/KeyStoreBuilderTest.java +++ b/jsign-core/src/test/java/net/jsign/KeyStoreBuilderTest.java @@ -329,6 +329,32 @@ public void testBuildOracleCloud() throws Exception { assertNotNull("keystore", keystore); } + @Test + public void testBuildTrustedSigning() throws Exception { + KeyStoreBuilder builder = new KeyStoreBuilder().storetype(TRUSTEDSIGNING); + + try { + builder.build(); + fail("Exception not thrown"); + } catch (IllegalArgumentException e) { + assertEquals("message", "keystore parameter must specify the Azure endpoint (.codesigning.azure.net)", e.getMessage()); + } + + builder.keystore("https://weu.codesigning.azure.net"); + + try { + builder.build(); + fail("Exception not thrown"); + } catch (IllegalArgumentException e) { + assertEquals("message", "storepass parameter must specify the Azure API access token", e.getMessage()); + } + + builder.storepass("0123456789ABCDEF"); + + KeyStore keystore = builder.build(); + assertNotNull("keystore", keystore); + } + @Test public void testBuildJKS() throws Exception { KeyStoreBuilder builder = new KeyStoreBuilder().storetype(JKS); diff --git a/jsign-core/src/test/java/net/jsign/SignerHelperTest.java b/jsign-core/src/test/java/net/jsign/SignerHelperTest.java index ebfdfddf..bf26beb5 100644 --- a/jsign-core/src/test/java/net/jsign/SignerHelperTest.java +++ b/jsign-core/src/test/java/net/jsign/SignerHelperTest.java @@ -311,6 +311,27 @@ public void testOracleCloud() throws Exception { SignatureAssert.assertSigned(new PEFile(targetFile), SHA256); } + @Test + public void testTrustedSigning() throws Exception { + File sourceFile = new File("target/test-classes/wineyes.exe"); + File targetFile = new File("target/test-classes/wineyes-signed-with-azure-trusted-signing.exe"); + + FileUtils.copyFile(sourceFile, targetFile); + + SignerHelper helper = new SignerHelper(new StdOutConsole(1), "option") + .storetype("TRUSTEDSIGNING") + .keystore("weu.codesigning.azure.net") + .storepass(Azure.getAccessToken("https://codesigning.azure.net")) + .alias("MyAccount/MyProfile") + .alg("SHA-256"); + + helper.sign(targetFile); + + Signable signable = Signable.of(targetFile); + SignatureAssert.assertSigned(signable, SHA256); + SignatureAssert.assertTimestamped("Invalid timestamp", signable.getSignatures().get(0)); + } + @Test public void testPIV() throws Exception { PIVCardTest.assumeCardPresent(); diff --git a/jsign-core/src/test/java/net/jsign/jca/Azure.java b/jsign-core/src/test/java/net/jsign/jca/Azure.java index 59a2ebf9..99fa71c4 100644 --- a/jsign-core/src/test/java/net/jsign/jca/Azure.java +++ b/jsign-core/src/test/java/net/jsign/jca/Azure.java @@ -29,9 +29,16 @@ public class Azure { * Generates an Azure access token using the CLI: az account get-access-token --resource "https://vault.azure.net" */ public static String getAccessToken() throws IOException, InterruptedException { + return getAccessToken("https://vault.azure.net"); + } + + /** + * Generates an Azure access token using the CLI: az account get-access-token --resource <resource> + */ + public static String getAccessToken(String resource) throws IOException, InterruptedException { Process process = null; try { - ProcessBuilder builder = new ProcessBuilder("az.cmd", "account", "get-access-token", "--resource", "https://vault.azure.net"); + ProcessBuilder builder = new ProcessBuilder("az.cmd", "account", "get-access-token", "--resource", resource); process = builder.start(); process.waitFor(); Assume.assumeTrue("Couldn't get Azure API token", process.exitValue() == 0); diff --git a/jsign-core/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java b/jsign-core/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java new file mode 100644 index 00000000..86108c3e --- /dev/null +++ b/jsign-core/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java @@ -0,0 +1,212 @@ +/** + * 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.FileInputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static net.jadler.Jadler.*; +import static org.junit.Assert.*; + +public class AzureTrustedSigningServiceTest { + + @Before + public void setUp() { + initJadler().withDefaultResponseStatus(404); + } + + @After + public void tearDown() { + closeJadler(); + } + + @Test + public void testGetAliases() throws Exception { + SigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + List aliases = service.aliases(); + + assertEquals("aliases", Collections.emptyList(), aliases); + } + + @Test + public void testGetCertificateChain() throws Exception { + onRequest() + .havingMethodEqualTo("POST") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(202) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); + onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(200) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") + .thenRespond() + .withStatus(200) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") + .thenRespond() + .withStatus(200) + .withBody(new FileInputStream("target/test-classes/services/trustedsigning-sign.json")); + + SigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + Certificate[] chain = service.getCertificateChain("MyAccount/MyProfile"); + assertNotNull("null chain", chain); + assertEquals("length", 4, chain.length); + assertEquals("subject 1", "CN=Emmanuel Bourg, O=Emmanuel Bourg, L=Paris, ST=Ile de France, C=FR", ((X509Certificate) chain[0]).getSubjectDN().getName()); + assertEquals("subject 2", "CN=Microsoft ID Verified CS EOC CA 01, O=Microsoft Corporation, C=US", ((X509Certificate) chain[1]).getSubjectDN().getName()); + assertEquals("subject 3", "CN=Microsoft ID Verified Code Signing PCA 2021, O=Microsoft Corporation, C=US", ((X509Certificate) chain[2]).getSubjectDN().getName()); + assertEquals("subject 4", "CN=Microsoft Identity Verification Root Certificate Authority 2020, O=Microsoft Corporation, C=US", ((X509Certificate) chain[3]).getSubjectDN().getName()); + } + + @Test + public void testGetCertificateChainWithError() { + onRequest() + .havingMethodEqualTo("POST") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(403); + + SigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + try { + service.getCertificateChain("MyAccount/MyProfile"); + fail("No exception thrown"); + } catch (KeyStoreException e) { + assertEquals("message", "Unable to retrieve the certificate chain 'MyAccount/MyProfile'", e.getMessage()); + } + } + + @Test + public void testGetPrivateKey() throws Exception { + SigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + SigningServicePrivateKey privateKey = service.getPrivateKey("MyAccount/MyProfile", null); + assertNotNull("null key", privateKey); + assertEquals("id", "MyAccount/MyProfile", privateKey.getId()); + assertEquals("algorithm", "RSA", privateKey.getAlgorithm()); + } + + @Test + public void testSign() throws Exception { + onRequest() + .havingMethodEqualTo("POST") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(202) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); + onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(200) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") + .thenRespond() + .withStatus(200) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") + .thenRespond() + .withStatus(200) + .withBody(new FileInputStream("target/test-classes/services/trustedsigning-sign.json")); + + AzureTrustedSigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + SigningServicePrivateKey privateKey = service.getPrivateKey("MyAccount/MyProfile", null); + + byte[] signature = service.sign(privateKey, "SHA256withRSA", "Hello".getBytes()); + + assertNotNull("null signature", signature); + assertEquals("length", 384, signature.length); + } + + @Test + public void testSignWithTimeout() throws Exception { + onRequest() + .havingMethodEqualTo("POST") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(202) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); + onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(200) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); + + AzureTrustedSigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + service.setTimeout(2); + SigningServicePrivateKey privateKey = service.getPrivateKey("MyAccount/MyProfile", null); + try { + service.sign(privateKey, "SHA256withRSA", "Hello".getBytes()); + fail("No exception thrown"); + } catch (GeneralSecurityException e) { + assertEquals("message", "java.io.IOException: Signing operation 1f234bd9-16cf-4283-9ee6-a460d31207bb timed out", e.getMessage()); + } + } + + @Test + public void testSignWithFailure() throws Exception { + onRequest() + .havingMethodEqualTo("POST") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(202) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); + onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb") + .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .respond() + .withStatus(200) + .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"Failed\",\"signature\":null,\"signingCertificate\":null}"); + + AzureTrustedSigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + SigningServicePrivateKey privateKey = service.getPrivateKey("MyAccount/MyProfile", null); + try { + service.sign(privateKey, "SHA256withRSA", "Hello".getBytes()); + fail("No exception thrown"); + } catch (GeneralSecurityException e) { + assertEquals("message", "java.io.IOException: Signing operation 1f234bd9-16cf-4283-9ee6-a460d31207bb failed: Failed", e.getMessage()); + } + } + + @Test + public void testSignWithInvalidAlgorithm() throws Exception { + SigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); + SigningServicePrivateKey privateKey = service.getPrivateKey("MyAccount/MyProfile", null); + try { + service.sign(privateKey, "SHA1withRSA", "Hello".getBytes()); + fail("No exception thrown"); + } catch (GeneralSecurityException e) { + assertEquals("message", "Unsupported signing algorithm: SHA1withRSA", e.getMessage()); + } + } +} diff --git a/jsign-core/src/test/java/net/jsign/jca/SigningServiceTest.java b/jsign-core/src/test/java/net/jsign/jca/SigningServiceTest.java index 4400e1f1..cdc407ab 100644 --- a/jsign-core/src/test/java/net/jsign/jca/SigningServiceTest.java +++ b/jsign-core/src/test/java/net/jsign/jca/SigningServiceTest.java @@ -200,4 +200,14 @@ public void testOracleCloudProvider() throws Exception { testCustomProvider(provider, keystore, "ocid1.key.oc1.eu-paris-1.h5tafwboaahxq.abrwiljrwkhgllb5zfqchmvdkmqnzutqeq5pz7yo6z7yhl2zyn2yncwzxiza", ""); } + + @Test + public void testTrustedSigningProvider() throws Exception { + String token = Azure.getAccessToken("https://codesigning.azure.net"); + Provider provider = new SigningServiceJcaProvider(new AzureTrustedSigningService("https://weu.codesigning.azure.net", token)); + KeyStore keystore = KeyStore.getInstance("TRUSTEDSIGNING", provider); + keystore.load(null, "".toCharArray()); + + testCustomProvider(provider, keystore, "MyAccount/MyProfile", ""); + } } diff --git a/jsign-core/src/test/resources/services/trustedsigning-sign.json b/jsign-core/src/test/resources/services/trustedsigning-sign.json new file mode 100644 index 00000000..1e6e22ad --- /dev/null +++ b/jsign-core/src/test/resources/services/trustedsigning-sign.json @@ -0,0 +1,6 @@ +{ + "operationId": "1f234bd9-16cf-4283-9ee6-a460d31207bb", + "status": "Succeeded", + "signature": "fyrDRW1Xf063cYW+PCI2Adf4gX4PxFzBzvGHOkUxWbaofwxhZe8qm5QrxMLb4mMHCi6OQ9/RYCBxKBD4IiYzbyDd+dSlXcZQlkxixcwC3755glSNrlkObuPLQCbwMzdmfA+KchJCjpgbGO7Z7txNx6qp7nvuKagomPtNWxrp6MA1VLryfcE750ek/4zS0ik5wDYjaIbsT7t3qkTzR4Q5o1D4aaF/RcXwTjeELLgyxkYQd9E/9g88uwtSmXSSSRdQVzh4jU8rWhAbJy010DwunKmobZZsp0d47pI1IQBbXjCa4cW/PcidQtDkPOV+Py06cGjXUvqeBRUyNGzF8pOpkEVjHnfsSGH03cYAYBrj4oCwOtpOUcYMotyKsQ02zGDA8FBTDsVLtvRflQHJeKdpAFYom8fOERNmqdNiSu2NFKQK8l5w6a1pjfmxXRCG8JbD+xWgZSG2coAVk1xiSblse31DYsa4l+chyui0JESxo6jwyRAQw6OIvBIicnrJbmA3", + "signingCertificate": "TUlJaTN3WUpLb1pJaHZjTkFRY0NvSUlpMERDQ0lzd0NBUUV4QURDQ0J3QUdDU3FHU0liM0RRRUhBYUNDQnZFRQ0KZ2didE1JSUc2VENDQk5HZ0F3SUJBZ0lUTXdBQVlNYTJ4bFFoSlQ4dDVnQUFBQUJneGpBTkJna3Foa2lHOXcwQg0KQVF3RkFEQmFNUXN3Q1FZRFZRUUdFd0pWVXpFZU1Cd0dBMVVFQ2hNVlRXbGpjbTl6YjJaMElFTnZjbkJ2Y21GMA0KYVc5dU1Tc3dLUVlEVlFRREV5Sk5hV055YjNOdlpuUWdTVVFnVm1WeWFXWnBaV1FnUTFNZ1JVOURJRU5CSURBeA0KTUI0WERUSTBNRFF3TVRJeU5UY3lNRm9YRFRJME1EUXdOREl5TlRjeU1Gb3daekVMTUFrR0ExVUVCaE1DUmxJeA0KRmpBVUJnTlZCQWdURFVsc1pTQmtaU0JHY21GdVkyVXhEakFNQmdOVkJBY1RCVkJoY21sek1SY3dGUVlEVlFRSw0KRXc1RmJXMWhiblZsYkNCQ2IzVnlaekVYTUJVR0ExVUVBeE1PUlcxdFlXNTFaV3dnUW05MWNtY3dnZ0dpTUEwRw0KQ1NxR1NJYjNEUUVCQVFVQUE0SUJqd0F3Z2dHS0FvSUJnUUNlQ3JsN2pzdG1Kc0hiZHUzaGY2dk5WMVlVVFIwaQ0KQ3ZabEU0R2RtbW1kRHpvZDFxbGhYVjB1NDA2QU1CbmU5UkFWdTNDRWsrMkZ1SXVpRDVCQlNZQW1scTFDdFp5OA0KVWtUNUlybUV5NndlcDZITytXalAwWmRiSlozWGdPZVFlSktGVDNTZjh2SzN0Q3RVYlU2Q2tyb3NRM3grRFd2Rg0KcmtqcVkzb1dXa2s3c215cmszTWxNK3dwTzBnS2JTelFvZG1IaVdWVXphNkQzVWxPc2FFY0Z3NGxDY3FVeG92TQ0KSjAxSVFQQ2Nmbk4xeUpkQWttY2VESXR5dFZqUGlCZ2wzbzR6YmJZdXlRMExhRVpydmhtT1JsenVhVDBxWXVoSQ0KR1BwY3hFVnZNTzFIRC9yUy9JR2doMEhQMi94aTZmaDgxQUlzd3JrSEQ4Z3RrSTRXaXhRc3JjWGd1WFJhRThtdw0KcDNGbHdENi9Tb2xkSnRxOUJwbTEwSEZQTm5oRStwc0ZLVDZveWhuWnNuVjdxQlYyeW9nQWpOOHoxVitBYU9pYw0KYlowL2FvNG80ampSK2pHaW9mTU1RUFdiMU9PNkpXUS9ab3cxZHltUFYxVU5UTThBZC9PbW5nbUc2ZFVOWWlvMw0KNEtXZDYrVFNDK2JwTy9JSWhmMS91QkNxQnJrZGh6YjdMaXNDQXdFQUFhT0NBaGt3Z2dJVk1Bd0dBMVVkRXdFQg0KL3dRQ01BQXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1Ed0dBMVVkSlFRMU1ETUdDaXNHQVFRQmdqZGhBUUFHQ0NzRw0KQVFVRkJ3TURCaHNyQmdFRUFZSTNZWUdNL3ZaTWd1SENpMStDOGJIV0dkZnNsbXN3SFFZRFZSME9CQllFRkxIVQ0KOE9oY1VvSy9BQzJKZ3VWRlFxQldQRlVzTUI4R0ExVWRJd1FZTUJhQUZIYWNOblFUMFpCOVlWK3pBdXVBOUpsTA0KcFQ2Rk1HY0dBMVVkSHdSZ01GNHdYS0Jhb0ZpR1ZtaDBkSEE2THk5M2QzY3ViV2xqY205emIyWjBMbU52YlM5dw0KYTJsdmNITXZZM0pzTDAxcFkzSnZjMjltZENVeU1FbEVKVEl3Vm1WeWFXWnBaV1FsTWpCRFV5VXlNRVZQUXlVeQ0KTUVOQkpUSXdNREV1WTNKc01JR2xCZ2dyQmdFRkJRY0JBUVNCbURDQmxUQmtCZ2dyQmdFRkJRY3dBb1pZYUhSMA0KY0RvdkwzZDNkeTV0YVdOeWIzTnZablF1WTI5dEwzQnJhVzl3Y3k5alpYSjBjeTlOYVdOeWIzTnZablFsTWpCSg0KUkNVeU1GWmxjbWxtYVdWa0pUSXdRMU1sTWpCRlQwTWxNakJEUVNVeU1EQXhMbU55ZERBdEJnZ3JCZ0VGQlFjdw0KQVlZaGFIUjBjRG92TDI5dVpXOWpjM0F1YldsamNtOXpiMlowTG1OdmJTOXZZM053TUdZR0ExVWRJQVJmTUYwdw0KVVFZTUt3WUJCQUdDTjB5RGZRRUJNRUV3UHdZSUt3WUJCUVVIQWdFV00yaDBkSEE2THk5M2QzY3ViV2xqY205eg0KYjJaMExtTnZiUzl3YTJsdmNITXZSRzlqY3k5U1pYQnZjMmwwYjNKNUxtaDBiVEFJQmdabmdRd0JCQUV3RFFZSg0KS29aSWh2Y05BUUVNQlFBRGdnSUJBSDVOYWRKLzB1ZEpHUllsNFg1Y1FuOWpTR0gyVktzMWVibGVOZWltanFNZQ0Ka29vOTF1aUcvSjE2N3Jkc1cxZmVVeGJPUG5OQUxWNzJmYnR6QmQyRVQ2OGNoQWJXNTFuU2d4bjZtL0RTQmtrag0KUEh1WFlYblpHdlliQ3lvL0VZMDdmdUNkNWdOSXZqU05rYk5Qdk1OMU5ta1ptQzdKOFVoZ01YMFZkaHZVN3ozSQ0KOW4rM0dLTUdza0o2aWErYldkWDNnOGNnZXJCeEkzQlNyekhnNVJzcVpEK1IrVG1OTUpOYkxGek1hZjZCQ0RScA0KOElxalZ1c2xUc3lob1YxOS8yUDBmMUxzU2orQzZrc2s3MHNIMXA3a1piREpORCtMYis4NE1EUVVFZ3pHQnptTQ0KZlVpZHlnbyt2NFF4aldHcXRXc1daL3ZEblB3dElPMFR6ZkQzY2x6Z0V3UHUwT0FXT25vSk52dU5TclhjZzU0RA0KbnFqOTJKdytjMHlrMnRucU0vUHhzK0JhSmV5THZCLzBhUldIQ0tHK0JOVEFZbnNsNXB2NkxXL21hYk02UjQwYQ0KanpDRW5vcDRteXA3R3BtQnRNV0pEdXplWlZkQ0NtaUQwaGtiR0w5cnkwdUM0S2JmeU1wU0lVdFBNMXN6WmszZQ0KM3hjTXNiNXpEV05PS3JjTzhMT29kZ2RzSHhKN2R3M2dIWXBhTE9xUTlCZkUzaG9GWDZkdXdHR1pSKythN3B4cQ0KY2dqdXYraE1Rd2xGNEZ4VUpWcEpheXd6Sitva3ZzNGE4OEQrOTFabDJWcnpOOWU1V2dCbUwrdU9PSWphVDNjYw0Kak84WEt5VkJGU3JaejM5QTV6ekpCRDNPcC9xN1VCUWFDbC9TaW9uYjJ5LzRrNjVlVTA0cmJXOHg3NUF3ckUrWA0Kb0lJYnZUQ0NCdWt3Z2dUUm9BTUNBUUlDRXpNQUFHREd0c1pVSVNVL0xlWUFBQUFBWU1Zd0RRWUpLb1pJaHZjTg0KQVFFTUJRQXdXakVMTUFrR0ExVUVCaE1DVlZNeEhqQWNCZ05WQkFvVEZVMXBZM0p2YzI5bWRDQkRiM0p3YjNKaA0KZEdsdmJqRXJNQ2tHQTFVRUF4TWlUV2xqY205emIyWjBJRWxFSUZabGNtbG1hV1ZrSUVOVElFVlBReUJEUVNBdw0KTVRBZUZ3MHlOREEwTURFeU1qVTNNakJhRncweU5EQTBNRFF5TWpVM01qQmFNR2N4Q3pBSkJnTlZCQVlUQWtaUw0KTVJZd0ZBWURWUVFJRXcxSmJHVWdaR1VnUm5KaGJtTmxNUTR3REFZRFZRUUhFd1ZRWVhKcGN6RVhNQlVHQTFVRQ0KQ2hNT1JXMXRZVzUxWld3Z1FtOTFjbWN4RnpBVkJnTlZCQU1URGtWdGJXRnVkV1ZzSUVKdmRYSm5NSUlCb2pBTg0KQmdrcWhraUc5dzBCQVFFRkFBT0NBWThBTUlJQmlnS0NBWUVBbmdxNWU0N0xaaWJCMjNidDRYK3J6VmRXRkUwZA0KSWdyMlpST0JuWnBwblE4NkhkYXBZVjFkTHVOT2dEQVozdlVRRmJ0d2hKUHRoYmlMb2crUVFVbUFKcGF0UXJXYw0KdkZKRStTSzVoTXVzSHFlaHp2bG96OUdYV3lXZDE0RG5rSGlTaFU5MG4vTHl0N1FyVkcxT2dwSzZMRU44Zmcxcg0KeGE1STZtTjZGbHBKTzdKc3E1TnpKVFBzS1R0SUNtMHMwS0haaDRsbFZNMnVnOTFKVHJHaEhCY09KUW5LbE1hTA0KekNkTlNFRHduSDV6ZGNpWFFKSm5IZ3lMY3JWWXo0Z1lKZDZPTTIyMkxza05DMmhHYTc0WmprWmM3bWs5S21Mbw0KU0JqNlhNUkZiekR0UncvNjB2eUJvSWRCejl2OFl1bjRmTlFDTE1LNUJ3L0lMWkNPRm9zVUxLM0Y0TGwwV2hQSg0Kc0tkeFpjQSt2MHFKWFNiYXZRYVp0ZEJ4VHpaNFJQcWJCU2srcU1vWjJiSjFlNmdWZHNxSUFJemZNOVZmZ0dqbw0KbkcyZFAycU9LT0k0MGZveG9xSHpERUQxbTlUanVpVmtQMmFNTlhjcGoxZFZEVXpQQUhmenBwNEpodW5WRFdJcQ0KTitDbG5ldmswZ3ZtNlR2eUNJWDlmN2dRcWdhNUhZYzIreTRyQWdNQkFBR2pnZ0laTUlJQ0ZUQU1CZ05WSFJNQg0KQWY4RUFqQUFNQTRHQTFVZER3RUIvd1FFQXdJSGdEQThCZ05WSFNVRU5UQXpCZ29yQmdFRUFZSTNZUUVBQmdncg0KQmdFRkJRY0RBd1liS3dZQkJBR0NOMkdCalA3MlRJTGh3b3RmZ3ZHeDFoblg3SlpyTUIwR0ExVWREZ1FXQkJTeA0KMVBEb1hGS0N2d0F0aVlMbFJVS2dWanhWTERBZkJnTlZIU01FR0RBV2dCUjJuRFowRTlHUWZXRmZzd0xyZ1BTWg0KUzZVK2hUQm5CZ05WSFI4RVlEQmVNRnlnV3FCWWhsWm9kSFJ3T2k4dmQzZDNMbTFwWTNKdmMyOW1kQzVqYjIwdg0KY0d0cGIzQnpMMk55YkM5TmFXTnliM052Wm5RbE1qQkpSQ1V5TUZabGNtbG1hV1ZrSlRJd1ExTWxNakJGVDBNbA0KTWpCRFFTVXlNREF4TG1OeWJEQ0JwUVlJS3dZQkJRVUhBUUVFZ1pnd2daVXdaQVlJS3dZQkJRVUhNQUtHV0doMA0KZEhBNkx5OTNkM2N1YldsamNtOXpiMlowTG1OdmJTOXdhMmx2Y0hNdlkyVnlkSE12VFdsamNtOXpiMlowSlRJdw0KU1VRbE1qQldaWEpwWm1sbFpDVXlNRU5USlRJd1JVOURKVEl3UTBFbE1qQXdNUzVqY25Rd0xRWUlLd1lCQlFVSA0KTUFHR0lXaDBkSEE2THk5dmJtVnZZM053TG0xcFkzSnZjMjltZEM1amIyMHZiMk56Y0RCbUJnTlZIU0FFWHpCZA0KTUZFR0RDc0dBUVFCZ2pkTWczMEJBVEJCTUQ4R0NDc0dBUVVGQndJQkZqTm9kSFJ3T2k4dmQzZDNMbTFwWTNKdg0KYzI5bWRDNWpiMjB2Y0d0cGIzQnpMMFJ2WTNNdlVtVndiM05wZEc5eWVTNW9kRzB3Q0FZR1o0RU1BUVFCTUEwRw0KQ1NxR1NJYjNEUUVCREFVQUE0SUNBUUIrVFduU2Y5TG5TUmtXSmVGK1hFSi9ZMGhoOWxTck5YbTVYalhvcG82ag0KSHBLS1BkYm9odnlkZXU2M2JGdFgzbE1Xemo1elFDMWU5bjI3Y3dYZGhFK3ZISVFHMXVkWjBvTVorcHZ3MGdaSg0KSXp4N2wyRjUyUnIyR3dzcVB4R05PMzdnbmVZRFNMNDBqWkd6VDd6RGRUWnBHWmd1eWZGSVlERjlGWFliMU84OQ0KeVBaL3R4aWpCckpDZW9tdm0xblY5NFBISUhxd2NTTndVcTh4NE9VYkttUS9rZms1alRDVFd5eGN6R24rZ1FnMA0KYWZDS28xYnJKVTdNb2FGZGZmOWo5SDlTN0VvL2d1cExKTzlMQjlhZTVHV3d5VFEvaTIvdk9EQTBGQklNeGdjNQ0KakgxSW5jb0tQcitFTVkxaHFyVnJGbWY3dzV6OExTRHRFODN3OTNKYzRCTUQ3dERnRmpwNkNUYjdqVXExM0lPZQ0KQTU2by9kaWNQbk5NcE5yWjZqUHo4YlBnV2lYc2k3d2Y5R2tWaHdpaHZnVFV3R0o3SmVhYitpMXY1bW16T2tlTg0KR284d2hKNktlSnNxZXhxWmdiVEZpUTdzM21WWFFncG9nOUlaR3hpL2E4dExndUNtMzhqS1VpRkxUek5iTTJaTg0KM3Q4WERMRytjdzFqVGlxM0R2Q3pxSFlIYkI4U2UzY040QjJLV2l6cWtQUVh4TjRhQlYrbmJzQmhtVWZ2bXU2Yw0KYW5JSTdyL29URU1KUmVCY1ZDVmFTV3NzTXlmcUpMN09HdlBBL3ZkV1pkbGE4emZYdVZvQVppL3JqamlJMms5Mw0KSEl6dkZ5c2xRUlVxMmM5L1FPYzh5UVE5enFmNnUxQVVHZ3BmMG9xSjI5c3YrSk91WGxOT0syMXZNZStRTUt4UA0KbHpDQ0Ixb3dnZ1ZDb0FNQ0FRSUNFek1BQUFBR1NocjZ6d1ZoYW5RQUFBQUFBQVl3RFFZSktvWklodmNOQVFFTQ0KQlFBd1l6RUxNQWtHQTFVRUJoTUNWVk14SGpBY0JnTlZCQW9URlUxcFkzSnZjMjltZENCRGIzSndiM0poZEdsdg0KYmpFME1ESUdBMVVFQXhNclRXbGpjbTl6YjJaMElFbEVJRlpsY21sbWFXVmtJRU52WkdVZ1UybG5ibWx1WnlCUQ0KUTBFZ01qQXlNVEFlRncweU1UQTBNVE14TnpNeE5UUmFGdzB5TmpBME1UTXhOek14TlRSYU1Gb3hDekFKQmdOVg0KQkFZVEFsVlRNUjR3SEFZRFZRUUtFeFZOYVdOeWIzTnZablFnUTI5eWNHOXlZWFJwYjI0eEt6QXBCZ05WQkFNVA0KSWsxcFkzSnZjMjltZENCSlJDQldaWEpwWm1sbFpDQkRVeUJGVDBNZ1EwRWdNREV3Z2dJaU1BMEdDU3FHU0liMw0KRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDQVFESDQ4Zy85Q0hkeGhuQXU4WExxNjRuaDlPbmVXZnNhcXp1enlWTg0KWEorQTRsWS9Wb0FIQ1RiK2pGMVdOOUlkU3JneE05ZUtVdm51cUw5OGZ0aWQwUXJncWQzZTdseDUwWEN2Wm9kSg0KT25xK1g4OHZWMEF2MngrZ084MmwwYlEzOUh6Z0NGZzJrRkJPR2s3ajhHckdZS0NYZUloRitHSGFnVlU2NkpPSQ0KTlZhOWNHRHZwdHlPY2VjUVMxZk84QmJBbTdSc0ZUdWhGR3BCNTNoVmNtMGdKVzM1bWdwUktPcGpuQlNXRUIzQQ0KZUg3ZlVHZWtFOExNVzBwV0l1bnJNUzFISTdGRjZCcUFWVDdJdUJlKytaM1RzZ00zUkxaTXRpNkptTlBENlJ4Zw0KNjJnMkFxdnVUUUxvVDFaL2NmaU1kcStUWXpHb1dtMkI4dlNBdjdOdEp2NVVFMHFKVlBTYXJOY2tnbVphYXJEUQ0KcjRQY3dwK1lKNnZkN2NKdXMvNFhsRzBKdlJkb1RTNUZ3azlrbU5iQnlJTUhFRWh1UTBYZ1l2WGFHWG0vSjJBVQ0KeWJOQncyNmgwckpmLy9lVXNuV3JiYXVnZFZMVnlDMnd1Q21OWmhtVUdXRUpOeGNsNW5mRzVvbTlka0gydHdzSg0KZlhrNkJjdmJXMVJUQWtJc1RidFhrQVpuR1E3ZUxuaWFCSUt6QzA2WlpUZ0FwMzhIOTdjcTFlL3BjRlJFcTRDMQ0KNTdQVVNtQ1docG5CQjZQMlhsMDMxU0h4YlgwRm1EMGlVdVg3RWRGZmk4T0l4WUJSLy9zQTE3Z3loTDN3WGptdg0Kdm9nWW5TRUxUWVF5NHhuRUFTdkJtUFNXZlJvdm5jVE9VeHJra0tKRTV0dlJTZ3NkOFpKMDBtd3lEUzZQY01CQQ0KTjFWWk1RSURBUUFCbzRJQ0RqQ0NBZ293RGdZRFZSMFBBUUgvQkFRREFnR0dNQkFHQ1NzR0FRUUJnamNWQVFRRA0KQWdFQU1CMEdBMVVkRGdRV0JCUjJuRFowRTlHUWZXRmZzd0xyZ1BTWlM2VStoVEJVQmdOVkhTQUVUVEJMTUVrRw0KQkZVZElBQXdRVEEvQmdnckJnRUZCUWNDQVJZemFIUjBjRG92TDNkM2R5NXRhV055YjNOdlpuUXVZMjl0TDNCcg0KYVc5d2N5OUViMk56TDFKbGNHOXphWFJ2Y25rdWFIUnRNQmtHQ1NzR0FRUUJnamNVQWdRTUhnb0FVd0IxQUdJQQ0KUXdCQk1CSUdBMVVkRXdFQi93UUlNQVlCQWY4Q0FRQXdId1lEVlIwakJCZ3dGb0FVMlVFcHNBOFBZMnp2YWRmMQ0KelNtZXBFaHFNT1l3Y0FZRFZSMGZCR2t3WnpCbG9HT2dZWVpmYUhSMGNEb3ZMM2QzZHk1dGFXTnliM052Wm5RdQ0KWTI5dEwzQnJhVzl3Y3k5amNtd3ZUV2xqY205emIyWjBKVEl3U1VRbE1qQldaWEpwWm1sbFpDVXlNRU52WkdVbA0KTWpCVGFXZHVhVzVuSlRJd1VFTkJKVEl3TWpBeU1TNWpjbXd3Z2E0R0NDc0dBUVVGQndFQkJJR2hNSUdlTUcwRw0KQ0NzR0FRVUZCekFDaG1Gb2RIUndPaTh2ZDNkM0xtMXBZM0p2YzI5bWRDNWpiMjB2Y0d0cGIzQnpMMk5sY25Seg0KTDAxcFkzSnZjMjltZENVeU1FbEVKVEl3Vm1WeWFXWnBaV1FsTWpCRGIyUmxKVEl3VTJsbmJtbHVaeVV5TUZCRA0KUVNVeU1ESXdNakV1WTNKME1DMEdDQ3NHQVFVRkJ6QUJoaUZvZEhSd09pOHZiMjVsYjJOemNDNXRhV055YjNOdg0KWm5RdVkyOXRMMjlqYzNBd0RRWUpLb1pJaHZjTkFRRU1CUUFEZ2dJQkFHb3ZDWi9Zc0hWQ05TQnJRYnZNV1JzWg0KM3cwRkFzYy9RbzRVRlkwa1ZtSk8wcCtrN1J0blpaK3pxL20rb2dxTVRmWkRvenowYmhtUlZ5OWE0UUFENTIrTQ0KdE9GTEx6MWpULytiOVpOSXJCaTJKSFVUQ2Z2SFdURDhXRDNmQkNtellMVlpTUDdUVC9xNDJzWDUzZ3hVbkZYVQ0KZWdFZ1A3M2xraGJRcVNwbWltYzREakRtOC9oUGx3R210bEFDVS8rOHdiSUhRZjM2a2MyalNOUDFEeUI4b2szTQ0KZEwyTFVPQUdhYTU4WjFiMU1ISzZlandZQ0xNVXlFdVVpelR4dm1XS1VpUVRuUGNVd0JRQ3Y1ZUFnalVVMW1kdg0KamM0anBCM2JNNktOdU5oKzZ1eGRRSTBjTDVGTEFrYWJsUXZNL0taaUNDY242U0VrNnJ1aEtXbzhhbHV2dlNFWQ0KRjQvRDhudithWktxbnVGT0MzU1krS1JMV0xocW56SDQvZko2WmhLR2NXdUJYWHZuWk1qNEN6cjB0K0F1MkdRaA0KTzkvdHNVY0h5K1lpRnAxa0k1TFM5TUxIY0g3ODVWd1F3czA3WnNuUTcyS1J6VW1wSFFXK3JIdWNEQXhGS0hjVg0KV3FpeURNRnRhZFdSQW1ydWhZWEF4VjhVaGlmb3M5Rmt5M2p5N3FJeFFJVUZJOTEydzhEL3FUem1ZUy83VHhUbA0KWUpEdkoyUFVwVlhaTWV0Ny95WXNlSjZiM0IvOExPaUdwR2UzRXpZVC9INDBmTHBNRXlkSTlCR3FHRTErNDZCUQ0KTUJZUmlhVXo5a2NabzhodnZFNjk5WEl0RC91WHBoK2lCUGQ2bTNDbmdZNFpHTWZuUDZBYjJTa0VqSHhDdEdYbw0KNktXZVhGRVRHaVNZeCtVdnVYWFpNSUlIbmpDQ0JZYWdBd0lCQWdJVE13QUFBQWVIb3pTamU2V09IQUFBQUFBQQ0KQnpBTkJna3Foa2lHOXcwQkFRd0ZBREIzTVFzd0NRWURWUVFHRXdKVlV6RWVNQndHQTFVRUNoTVZUV2xqY205eg0KYjJaMElFTnZjbkJ2Y21GMGFXOXVNVWd3UmdZRFZRUURFejlOYVdOeWIzTnZablFnU1dSbGJuUnBkSGtnVm1WeQ0KYVdacFkyRjBhVzl1SUZKdmIzUWdRMlZ5ZEdsbWFXTmhkR1VnUVhWMGFHOXlhWFI1SURJd01qQXdIaGNOTWpFdw0KTkRBeE1qQXdOVEl3V2hjTk16WXdOREF4TWpBeE5USXdXakJqTVFzd0NRWURWUVFHRXdKVlV6RWVNQndHQTFVRQ0KQ2hNVlRXbGpjbTl6YjJaMElFTnZjbkJ2Y21GMGFXOXVNVFF3TWdZRFZRUURFeXROYVdOeWIzTnZablFnU1VRZw0KVm1WeWFXWnBaV1FnUTI5a1pTQlRhV2R1YVc1bklGQkRRU0F5TURJeE1JSUNJakFOQmdrcWhraUc5dzBCQVFFRg0KQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFzdkRBcnhtSUtPTGRWSHBNU1d4cENGVUp0RkwvZWtyNHdlc2xLUGRuRjNjcA0KVGV1Vjh2ZXF0bUtWZ29rMnJPMEQwNUJweXZVRENnMXdkc29FdHV4QUNFR2NnSGZqUEYvblpzT2tnN2MwbVY4aA0KcE1UL0d2QjR1aER2V1hNSWVRUHNEZ0N6VUd6VHZvaTc2WURweERPeGhnZjhKdVhXSnpCRG9Mcm10VGhYMDFDRQ0KMVRDQ3ZIMnNaRC8rSHozUkR3bDJNc3ZEU2RYNXJKRFlWdVIzYmphajJRZnpaRm13ZmNjVEtxTUFIbHJ6NEI3YQ0KYzhnOXp5eGxUcGtUdUpHdEZuTEJHYXNvT25uNU55WWxmMHhGOS9ialZSbzRHemcyWWM3S1I3eWhUVk5pdVRHSA0KNWg0ZUI5YWptMU9DU2hJeWhyS3FnT2tjNHNtejZvYnhPK0h4S2VKOWJZbVBmNktMWFZOTHo4VWFlQVJvMEJhdA0Kdko4MnNMcjJncWxGQmRqMXNZZnFPZjAwUW0vM0I0WEdGUERLL0gwNGt0ZVpFWnNCUmMzVlQyZC9pVmQ3T1RMcA0KU0g5eUNPUlYzb0laUUIvUXI0bkQ0WVQvbFdraFZ0dzJ2MnMwVG5SSnViTC9oRk1JUWE4NnJjYUdNaE5zSnJoeQ0Kc0xOTk1lQmhpTWV6VTFzNXpwdXNmNTRxbFl1MnY1c1o1ekwwS3ZCRExIdEw4RjlnbjZqT3kzdjdKbTBiYkJIag0Kclc1eVFXN1MzNkFMQXQwM1FEcHdXMUpHMUh4dS9GVVhKYkJPMkF3d1ZHNEZyZStaUTVPZDhvdXd0NTlGcEJ4Vg0KT0JHZk40dk4ybTNmWngxZ3FuNTJHdmFpQno2b3pvcmdJRWpuK1BoVVhJTGhBVjVRL1pnQ0owdTIrbGRGR2pjQw0KQXdFQUFhT0NBalV3Z2dJeE1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUUJna3JCZ0VFQVlJM0ZRRUVBd0lCQURBZA0KQmdOVkhRNEVGZ1FVMlVFcHNBOFBZMnp2YWRmMXpTbWVwRWhxTU9Zd1ZBWURWUjBnQkUwd1N6QkpCZ1JWSFNBQQ0KTUVFd1B3WUlLd1lCQlFVSEFnRVdNMmgwZEhBNkx5OTNkM2N1YldsamNtOXpiMlowTG1OdmJTOXdhMmx2Y0hNdg0KUkc5amN5OVNaWEJ2YzJsMGIzSjVMbWgwYlRBWkJna3JCZ0VFQVlJM0ZBSUVEQjRLQUZNQWRRQmlBRU1BUVRBUA0KQmdOVkhSTUJBZjhFQlRBREFRSC9NQjhHQTFVZEl3UVlNQmFBRk1oKzBtcUZLaHZLR1pnRUJ5ZlBVQkJQYUtpaQ0KTUlHRUJnTlZIUjhFZlRCN01IbWdkNkIxaG5Ob2RIUndPaTh2ZDNkM0xtMXBZM0p2YzI5bWRDNWpiMjB2Y0d0cA0KYjNCekwyTnliQzlOYVdOeWIzTnZablFsTWpCSlpHVnVkR2wwZVNVeU1GWmxjbWxtYVdOaGRHbHZiaVV5TUZKdg0KYjNRbE1qQkRaWEowYVdacFkyRjBaU1V5TUVGMWRHaHZjbWwwZVNVeU1ESXdNakF1WTNKc01JSERCZ2dyQmdFRg0KQlFjQkFRU0J0akNCc3pDQmdRWUlLd1lCQlFVSE1BS0dkV2gwZEhBNkx5OTNkM2N1YldsamNtOXpiMlowTG1Odg0KYlM5d2EybHZjSE12WTJWeWRITXZUV2xqY205emIyWjBKVEl3U1dSbGJuUnBkSGtsTWpCV1pYSnBabWxqWVhScA0KYjI0bE1qQlNiMjkwSlRJd1EyVnlkR2xtYVdOaGRHVWxNakJCZFhSb2IzSnBkSGtsTWpBeU1ESXdMbU55ZERBdA0KQmdnckJnRUZCUWN3QVlZaGFIUjBjRG92TDI5dVpXOWpjM0F1YldsamNtOXpiMlowTG1OdmJTOXZZM053TUEwRw0KQ1NxR1NJYjNEUUVCREFVQUE0SUNBUUIvSlNxZS90U3I2dDFtQ3R0WEkweTZYbXlRNDF1R1d6bDl4dytXWWh2Tw0KTDQ3QlYwOURnZm5tL3RVNGllZVo3TkFSNWJndW9yVENOcjU4SE9jQTF0Y3NIUXF0MHdKc2RDbHN1OGJwUUQ5ZQ0KL2FsK2xVZ1RVSkVWODBYaGNvN3hkZ1JyZWhieWhVZjRwa2VBaEJFakFCdklVcEQyTEtQaG81WjREUENUNS8wVA0KbEswMm5sUHdVYnY5VVJSRWhWWUN0c0RNKzMxT0ZVM2ZEVjhCbVFYdjVoVDJSdXJWc0pIWmdQNHkyNmRKRFZGKw0KM3BjYnR2aDdSNk5FRHVZSFlpaGZtRTJIZFFScTVqUnZMRTFFYjU5UFl3SVNGQ1gyRGFMWit6cFU0YlgwSTE2bg0KdEtxNHBvR09GYWFLdGpJQTF2UkVsSXRhT0tjd3RjMDRDQnJYU2Z5TDJPcDZtdk5JeFRrNE9hc3dJa1RYYkZMOA0KMVpLR0QrMjR1TUN3by9wTE5objdWSExmbnhsTVZ6SFFWTCtiSGE5S2hUeXp3ZEcvTDZ1ZGVySlFuMGNHcExRTQ0KU3RVdU5EQXJ4VzJ3RjE2UUdaMU50QldnS0E4S3F2NDhNOEhmRnFOaWZONit6dDZKMEd3enZVOGcwcllHZ1RaUg0KOHpERUlKZmVaeHdXREhwU3hCNUZKMVZWVTFMSUF0QjdvOVBYYmpYekdpZmFJTVlUelU0WUt0NHZNTnd3Qm1ldA0KUURIaGRBdFRQcGxPWHJuSTlTSTZIZVR0akREM2lVTi83eWdiYWhtWU9IazdWQjdmd1Q0emUrRXJDYk1oNmdIVg0KMVV1WFBpTGNpbG9OeEg2SzRhTWZaTjFvTFZrNllGZUlKRW9rdVBnTlBhNkVuVGlPTDYwY1BxZm55K0ZxOFVpdQ0KWnpDQ0Jjd3dnZ08wb0FNQ0FRSUNFRlNZMHRIVVd4bVZTQk41eUJIQWg1a3dEUVlKS29aSWh2Y05BUUVNQlFBdw0KZHpFTE1Ba0dBMVVFQmhNQ1ZWTXhIakFjQmdOVkJBb1RGVTFwWTNKdmMyOW1kQ0JEYjNKd2IzSmhkR2x2YmpGSQ0KTUVZR0ExVUVBeE0vVFdsamNtOXpiMlowSUVsa1pXNTBhWFI1SUZabGNtbG1hV05oZEdsdmJpQlNiMjkwSUVObA0KY25ScFptbGpZWFJsSUVGMWRHaHZjbWwwZVNBeU1ESXdNQjRYRFRJd01EUXhOakU0TXpZeE5sb1hEVFExTURReA0KTmpFNE5EUTBNRm93ZHpFTE1Ba0dBMVVFQmhNQ1ZWTXhIakFjQmdOVkJBb1RGVTFwWTNKdmMyOW1kQ0JEYjNKdw0KYjNKaGRHbHZiakZJTUVZR0ExVUVBeE0vVFdsamNtOXpiMlowSUVsa1pXNTBhWFI1SUZabGNtbG1hV05oZEdsdg0KYmlCU2IyOTBJRU5sY25ScFptbGpZWFJsSUVGMWRHaHZjbWwwZVNBeU1ESXdNSUlDSWpBTkJna3Foa2lHOXcwQg0KQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBczVFcUI0TUdaLzJlbmVESHdMZWs1a0lFZncrbTIxLzcxVnJYUmFENw0KZHd2d2dQT21iVnBOZVZQWW9JYUVWMFVneDZKVSs4ZWl2NHJIYmpYem9oWEVMMDdqU29XV1NRMy92cG5ZRlBhOA0KSndmdVFwc3I5UXVTQnVUOWFSTmxxSkZ5OHBpRTY0TTlEdVRYY1JKSUljc043ZlpIU2JlYitjbkhGN2FFVC8rNA0KckpyWGMyZEpoZU9HdlRkQTBDV0cxTjYxd20xaWF0V3BlTHd0YjBuNTVXd1VGUDBVeDlObEZqZmV5MjY4WGltTg0KL1dLYkZTeldCZWE1aVRJem8yTEgxOVpTWndqRUx2UldLNTRMaDh6c3A3U21xdXNGelJsWHBUb0xCQ2Nja1dlZQ0KTFdJdEx4Nisyc0FneXdRWnlqUDdpYjZZNG5LZ2NqVytlZUdjZzIva2JSZHZrUE05QUlaMU9JN1E0RW1hdTl2VA0KK0RESzFWZUlhRTF5MDc5dGYzSFkvYjBOcnBKa1NMZGJiM2ttdGMyYmxTR0UwZThQTWoxN1Y0enpSUWRNZk9CZQ0KR0E0MWRvdHRuc3MyZEtzRitPQnpYVEpXbEdlWEpRckdOVDJVbCtmQlJJdUEvY0g0OUhRWjVURDJCdnNoVno0Rw0KSEl0ckZZWW5TWHVDazhwWjZIVkg2RDg0OU1kVGVhQzJ0T0pjVWUrOVh6akJFK1o0REpWYUxzVkFXU2pNRHlUQQ0KN0xvSmR5T1pPS2EySE5ySHVpQzIxemZZZnpldkNPTTdjZHR1Y3h0OW1YS3c1SVl6V1hTMUZnQjdVRzNHaGhQYQ0KL2NRNWdqMGtBSnBnMnJxVXdBVlJMRFNzVUprVGg3dXpCWUN5VFRBQ1hMZ21nMTIwWTNQdnJpT1ZUMkFvdmpmVg0KVzZVQ0F3RUFBYU5VTUZJd0RnWURWUjBQQVFIL0JBUURBZ0dHTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRA0KVlIwT0JCWUVGTWgrMG1xRktodktHWmdFQnlmUFVCQlBhS2lpTUJBR0NTc0dBUVFCZ2pjVkFRUURBZ0VBTUEwRw0KQ1NxR1NJYjNEUUVCREFVQUE0SUNBUUN2YXQzbUdlY3RsRU1aVHN2cFVKVmtwUU9SQW92aU5vQTdGYUpTd2hZWg0KdG1wYVhYUkRNUFNiLzJCMENiRWhIcEFXYmNVa2oxeG1pR1AwVDh4OThoSk1RQkNMQVovYXFjaXU4cFViejUwRg0KNjBrK2RLQm9XK1ZXTEdVY2duNVQybGJaUmhkNWtrWEVFRFlJVWlrWHl5K204bjdVYVNTS0hvK3djdzNNSEVxcg0Kc3FydHA1RmpBV1FpcURLNGZqSW9zMmR6TFpHMDNERUJDL2RIQ3FieDEwcnRWbURFTEFpamUwQ3d2SFFuVW9mVw0Kdm9qZE40cUpibWVJSGZYSlhhRCt0cXM2Z05jYWx6d1hOaUpCSHF4TjFZUG1QRGk5VHpEcFZLblR0Z1RETW5aaA0KdTdBWXhTc1lzOENBMWJlVnNGNVJUU0w4N0ZpcTZOaVV0S1V1N1pMZTV4aDhJVmZkVldQM3YyM05IOUttZHlodw0KeCtKYk9sc0kwbHRPeUFDV3MrR0ROcStHQ21WY2RQYnE3SHBxZEtEd1MrN3ZsS09zVVBLSDdkYzZNSVBKKzMxWA0KdnVYaitFSEs1V1N1czZQc1dPeUZtc3p2dWVyelZoaTVYSE9hcjhWM0Y0TloyemNhR0hKVXBVSFN0aU4xbzBPYQ0KNVhkOGxubTNRWTIvN055QW9KL1JkM1ZZWHpVVDRDVWFad3Q5emlYNkJ3cmtZU0hZMUJ6bEI4WTJtZlNXME1ZVg0KL2s3TjE2NkxuZHNXL1FUR2tyM1VpT2FwbzZxNzkyUTRPMS9NRE5BMXZuUVpBNmJGcWt5aVlUYUNQaDN6Szd5WA0KWGR0TGVEc3Q5VHZ2WUNQbzlld0xJemFWcjVobXYxUFRlN2hwU2lxV1ptbkVsTWIwWDI2c21IaUlnQVpjb3JMdA0Kb2pFQQ0K" +} diff --git a/jsign-maven-plugin/src/main/java/net/jsign/JsignMojo.java b/jsign-maven-plugin/src/main/java/net/jsign/JsignMojo.java index ecb1b2bd..7ed36115 100644 --- a/jsign-maven-plugin/src/main/java/net/jsign/JsignMojo.java +++ b/jsign-maven-plugin/src/main/java/net/jsign/JsignMojo.java @@ -87,7 +87,7 @@ public class JsignMojo extends AbstractMojo { /** * The type of the keystore (JKS, JCEKS, PKCS12, PKCS11, ETOKEN, NITROKEY, OPENPGP, OPENSC, PIV, YUBIKEY, AWS, - * AZUREKEYVAULT, DIGICERTONE, ESIGNER, GOOGLECLOUD, HASHICORPVAULT or ORACLECLOUD). + * AZUREKEYVAULT, DIGICERTONE, ESIGNER, GOOGLECLOUD, HASHICORPVAULT, ORACLECLOUD or TRUSTEDSIGNING). */ @Parameter( property = "jsign.storetype" ) private String storetype; diff --git a/jsign/src/deb/data/usr/share/bash-completion/completions/jsign b/jsign/src/deb/data/usr/share/bash-completion/completions/jsign index 9e90a6a1..e158fa2b 100644 --- a/jsign/src/deb/data/usr/share/bash-completion/completions/jsign +++ b/jsign/src/deb/data/usr/share/bash-completion/completions/jsign @@ -22,7 +22,7 @@ _jsign() return 0 ;; --storetype) - COMPREPLY=( $( compgen -W 'JKS JCEKS PKCS12 PKCS11 AWS AZUREKEYVAULT DIGICERTONE ESIGNER ETOKEN GOOGLECLOUD HASHICORPVAULT ORACLECLOUD YUBIKEY NITROKEY OPENPGP OPENSC PIV' -- "$cur" ) ) + COMPREPLY=( $( compgen -W 'JKS JCEKS PKCS12 PKCS11 AWS AZUREKEYVAULT DIGICERTONE ESIGNER ETOKEN GOOGLECLOUD HASHICORPVAULT ORACLECLOUD TRUSTEDSIGNING YUBIKEY NITROKEY OPENPGP OPENSC PIV' -- "$cur" ) ) return 0 ;; --storepass|-a|--alias|--keypass|-t|--tsaurl|-r|--tsretries|-w|--tsretrywait|-n|--name|-u|--url|-e|--encoding) diff --git a/jsign/src/deb/data/usr/share/man/man1/jsign.1 b/jsign/src/deb/data/usr/share/man/man1/jsign.1 index faa6a8bd..c78b69ac 100644 --- a/jsign/src/deb/data/usr/share/man/man1/jsign.1 +++ b/jsign/src/deb/data/usr/share/man/man1/jsign.1 @@ -75,6 +75,8 @@ The type of the keystore: .br - ORACLECLOUD : Oracle Cloud Key Management Service .br +- TRUSTEDSIGNING: Azure Trusted Signing +.br This option is not required for file based keystores (JKS, JCEKS and PKCS12). .TP @@ -315,6 +317,32 @@ The Azure account used must have the "Key Vault Crypto User" and "Key Vault Cert .TP +Signing with Azure Trusted Signing + +With the Azure Trusted Signing service the keystore parameter specifies the endpoint URI, and the alias combines +the account name and the certificate profile. The Azure API access token is used as the keystore password. + +jsign --storetype TRUSTEDSIGNING \ + --keystore weu.codesigning.azure.net \ + --storepass \ + --alias / application.exe + +The access token can be obtained with the Azure CLI: + +az account get-access-token --resource https://codesigning.azure.net + +The Azure account used must have the "Code Signing Certificate Profile Signer" role. + +The certificates issued by Azure Trusted Signing have a lifetime of 3 days only, and timestamping is necessary to +ensure the long term validity of the signature. For this reason timestamping is automatically enabled when signing +with this service. + +Implementation note: Jsign performs an extra call to the signing API to retrieve the current certificate chain before +signing. When signing multiple files it's recommended to invoke Jsign only once with the list of files to avoid doubling +the quota usage. + +.TP + Signing with DigiCert ONE: Certificates and keys stored in the DigiCert ONE Secure Software Manager can be used directly without installing