From 4e9afe65dbf080455c8ea4c9b7f15034ace12429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Fri, 21 Nov 2025 15:49:46 +0100 Subject: [PATCH 1/8] Change API endpoint to 2024-09-30-preview --- .../jsign/jca/AzureTrustedSigningService.java | 4 ++-- .../jca/AzureTrustedSigningServiceTest.java | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index ca7c46ee..b81267b0 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -144,7 +144,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] request.put("signatureAlgorithm", algorithm); request.put("digest", Base64.getEncoder().encodeToString(data)); - Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2022-06-15-preview", JsonWriter.format(request)); + Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2024-09-30-preview", JsonWriter.format(request)); String operationId = (String) response.get("operationId"); @@ -157,7 +157,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] } catch (InterruptedException e) { break; } - response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2022-06-15-preview"); + response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2024-09-30-preview"); String status = (String) response.get("status"); if ("InProgress".equals(status)) { continue; diff --git a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java index 676150ed..48b416f3 100644 --- a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java +++ b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java @@ -56,14 +56,14 @@ public void testGetCertificateChain() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-09-30-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") + .havingQueryStringEqualTo("api-version=2024-09-30-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -89,7 +89,7 @@ public void testGetCertificateChainWithError() { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-09-30-preview") .respond() .withStatus(403); @@ -113,14 +113,14 @@ public void testSign() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-09-30-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") + .havingQueryStringEqualTo("api-version=2024-09-30-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -145,14 +145,14 @@ public void testSignWithTimeout() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-09-30-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") + .havingQueryStringEqualTo("api-version=2024-09-30-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); @@ -170,14 +170,14 @@ public void testSignWithFailure() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-09-30-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") + .havingQueryStringEqualTo("api-version=2024-09-30-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"Failed\",\"signature\":null,\"signingCertificate\":null}"); @@ -203,7 +203,7 @@ public void testSignWithAuthorizationError() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2022-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-09-30-preview") .respond() .withStatus(404) .withContentType("application/json") From f4ca5528d8cf67f839fc2863ec8f75720d60df4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Fri, 21 Nov 2025 15:52:43 +0100 Subject: [PATCH 2/8] Add dispatch --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 246edfd8..f440e457 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,7 @@ on: branches: [ '*' ] pull_request: branches: [ master ] + workflow_dispatch: env: STOREPASS: password From 58e93e1b169279d155bd808759b30216998c18bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Mon, 24 Nov 2025 10:04:29 +0100 Subject: [PATCH 3/8] Switch to the latest supported API ATS version --- .../jsign/jca/AzureTrustedSigningService.java | 4 ++-- .../jca/AzureTrustedSigningServiceTest.java | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index b81267b0..5226bd71 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -144,7 +144,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] request.put("signatureAlgorithm", algorithm); request.put("digest", Base64.getEncoder().encodeToString(data)); - Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2024-09-30-preview", JsonWriter.format(request)); + Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2024-06-15", JsonWriter.format(request)); String operationId = (String) response.get("operationId"); @@ -157,7 +157,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] } catch (InterruptedException e) { break; } - response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2024-09-30-preview"); + response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2024-06-15"); String status = (String) response.get("status"); if ("InProgress".equals(status)) { continue; diff --git a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java index 48b416f3..52b18d58 100644 --- a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java +++ b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java @@ -56,14 +56,14 @@ public void testGetCertificateChain() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .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=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -89,7 +89,7 @@ public void testGetCertificateChainWithError() { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .respond() .withStatus(403); @@ -113,14 +113,14 @@ public void testSign() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .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=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -145,14 +145,14 @@ public void testSignWithTimeout() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .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=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); @@ -170,14 +170,14 @@ public void testSignWithFailure() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .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=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"Failed\",\"signature\":null,\"signingCertificate\":null}"); @@ -203,7 +203,7 @@ public void testSignWithAuthorizationError() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-09-30-preview") + .havingQueryStringEqualTo("api-version=2024-06-15") .respond() .withStatus(404) .withContentType("application/json") From 6360708d75f145187cf84918239fe5108be9ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Mon, 24 Nov 2025 10:29:04 +0100 Subject: [PATCH 4/8] Revert to older version that is still with -preview suffix but documented --- .../jsign/jca/AzureTrustedSigningService.java | 4 ++-- .../jca/AzureTrustedSigningServiceTest.java | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index 5226bd71..899659d4 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -144,7 +144,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] request.put("signatureAlgorithm", algorithm); request.put("digest", Base64.getEncoder().encodeToString(data)); - Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2024-06-15", JsonWriter.format(request)); + Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2023-06-15-preview", JsonWriter.format(request)); String operationId = (String) response.get("operationId"); @@ -157,7 +157,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] } catch (InterruptedException e) { break; } - response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2024-06-15"); + response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2023-06-15-preview"); String status = (String) response.get("status"); if ("InProgress".equals(status)) { continue; diff --git a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java index 52b18d58..cd851c74 100644 --- a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java +++ b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java @@ -56,14 +56,14 @@ public void testGetCertificateChain() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-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=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -89,7 +89,7 @@ public void testGetCertificateChainWithError() { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(403); @@ -113,14 +113,14 @@ public void testSign() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-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=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -145,14 +145,14 @@ public void testSignWithTimeout() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-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=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); @@ -170,14 +170,14 @@ public void testSignWithFailure() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-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=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"Failed\",\"signature\":null,\"signingCertificate\":null}"); @@ -203,7 +203,7 @@ public void testSignWithAuthorizationError() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-06-15") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(404) .withContentType("application/json") From 2dfc7f1d606332f8ab90d1e75bc086fdd6d05cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Mon, 24 Nov 2025 11:06:26 +0100 Subject: [PATCH 5/8] Try undocumented version --- .../jsign/jca/AzureTrustedSigningService.java | 4 ++-- .../jca/AzureTrustedSigningServiceTest.java | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index 899659d4..bfb1a5a9 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -144,7 +144,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] request.put("signatureAlgorithm", algorithm); request.put("digest", Base64.getEncoder().encodeToString(data)); - Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2023-06-15-preview", JsonWriter.format(request)); + Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2024-02-05-preview", JsonWriter.format(request)); String operationId = (String) response.get("operationId"); @@ -157,7 +157,7 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] } catch (InterruptedException e) { break; } - response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2023-06-15-preview"); + response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2024-02-05-preview"); String status = (String) response.get("status"); if ("InProgress".equals(status)) { continue; diff --git a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java index cd851c74..9a9ad34b 100644 --- a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java +++ b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java @@ -56,14 +56,14 @@ public void testGetCertificateChain() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-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=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -89,7 +89,7 @@ public void testGetCertificateChainWithError() { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-preview") .respond() .withStatus(403); @@ -113,14 +113,14 @@ public void testSign() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-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=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -145,14 +145,14 @@ public void testSignWithTimeout() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-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=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); @@ -170,14 +170,14 @@ public void testSignWithFailure() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-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=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"Failed\",\"signature\":null,\"signingCertificate\":null}"); @@ -203,7 +203,7 @@ public void testSignWithAuthorizationError() throws Exception { onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2023-06-15-preview") + .havingQueryStringEqualTo("api-version=2024-02-05-preview") .respond() .withStatus(404) .withContentType("application/json") From 72501cbd9814464fbd741b3a24c67902900746b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Mon, 24 Nov 2025 13:25:13 +0100 Subject: [PATCH 6/8] Update AzureTrustedSigningService.java Try to use new API --- .../jsign/jca/AzureTrustedSigningService.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index bfb1a5a9..2df2a2cc 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -49,6 +49,9 @@ public class AzureTrustedSigningService implements SigningService { /** Timeout in seconds for the signing operation */ private long timeout = 60; + /** API version to use for Azure Trusted Signing */ + private String apiVersion = "2023-06-15-preview"; + /** * Mapping between Java and Azure signing algorithms. * @see Key Vault API - JonWebKeySignatureAlgorithm @@ -144,9 +147,9 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] request.put("signatureAlgorithm", algorithm); request.put("digest", Base64.getEncoder().encodeToString(data)); - Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign?api-version=2024-02-05-preview", JsonWriter.format(request)); + Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + ":sign?api-version=" + apiVersion, JsonWriter.format(request)); - String operationId = (String) response.get("operationId"); + String operationId = (String) response.get("id"); // poll until the operation is completed long startTime = System.currentTimeMillis(); @@ -157,9 +160,9 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] } catch (InterruptedException e) { break; } - response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=2024-02-05-preview"); + response = client.get("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + ":sign/" + operationId + "?api-version=" + apiVersion); String status = (String) response.get("status"); - if ("InProgress".equals(status)) { + if ("NotStarted".equals(status) || "Running".equals(status)) { continue; } if ("Succeeded".equals(status)) { @@ -174,8 +177,15 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] } SignStatus status = new SignStatus(); - status.signature = Base64.getDecoder().decode((String) response.get("signature")); - status.signingCertificate = new String(Base64.getDecoder().decode((String) response.get("signingCertificate"))); + Map result = (Map) response.get("result"); + if (result != null) { + status.signature = Base64.getDecoder().decode((String) result.get("signature")); + status.signingCertificate = new String(Base64.getDecoder().decode((String) result.get("signingCertificate"))); + } else { + // Fallback for older API versions + status.signature = Base64.getDecoder().decode((String) response.get("signature")); + status.signingCertificate = new String(Base64.getDecoder().decode((String) response.get("signingCertificate"))); + } return status; } From 1231441184d9a504afdf51dd2ea6da94856f702c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Mon, 24 Nov 2025 14:54:18 +0100 Subject: [PATCH 7/8] Implement a new API changes: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added header-aware RESTResponse return path in RESTClient so callers can access operation metadata (status code, headers, parsed body, raw bytes) without reimplementing HTTP plumbing. - Updated AzureTrustedSigningService to use the new RESTResponse, read Azure’s operation-location header, and poll the returned /sign/{operationId} URL until completion. - Adjusted the Azure Trusted Signing tests to simulate the :sign POST plus header-driven polling flow, ensuring certificate-chain fetch, signing success/failure, and timeout cases all match the real API --- .../jsign/jca/AzureTrustedSigningService.java | 155 ++++++++++++++---- .../main/java/net/jsign/jca/RESTClient.java | 83 +++++++++- .../jca/AzureTrustedSigningServiceTest.java | 62 ++++--- 3 files changed, 240 insertions(+), 60 deletions(-) diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index 2df2a2cc..8fa7a033 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -30,6 +30,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import net.jsign.DigestAlgorithm; @@ -41,6 +42,8 @@ */ public class AzureTrustedSigningService implements SigningService { + private static final String API_VERSION = "2023-06-15-preview"; + /** Cache of certificate chains indexed by alias */ private final Map certificates = new HashMap<>(); @@ -49,9 +52,6 @@ public class AzureTrustedSigningService implements SigningService { /** Timeout in seconds for the signing operation */ private long timeout = 60; - /** API version to use for Azure Trusted Signing */ - private String apiVersion = "2023-06-15-preview"; - /** * Mapping between Java and Azure signing algorithms. * @see Key Vault API - JonWebKeySignatureAlgorithm @@ -147,49 +147,140 @@ private SignStatus sign(String account, String profile, String algorithm, byte[] request.put("signatureAlgorithm", algorithm); request.put("digest", Base64.getEncoder().encodeToString(data)); - Map response = client.post("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + ":sign?api-version=" + apiVersion, JsonWriter.format(request)); + RESTClient.RESTResponse initialResponse = client.postResponse("/codesigningaccounts/" + account + "/certificateprofiles/" + profile + ":sign?api-version=" + API_VERSION, JsonWriter.format(request)); - String operationId = (String) response.get("id"); + Map statusResponse = initialResponse.getBody(); + String operationLocation = initialResponse.getHeader("operation-location"); + String operationId = readOperationId(statusResponse); + if (operationId == null) { + operationId = extractOperationId(operationLocation); + } - // 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=" + apiVersion); - String status = (String) response.get("status"); - if ("NotStarted".equals(status) || "Running".equals(status)) { - continue; + String status = statusResponse != null ? (String) statusResponse.get("status") : null; + + if (!isSucceeded(status)) { + String statusResource = resolveStatusResource(account, profile, operationId, operationLocation); + + 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; + } + statusResponse = client.get(statusResource); + status = statusResponse != null ? (String) statusResponse.get("status") : null; + if (operationId == null) { + operationId = readOperationId(statusResponse); + } + if (isPending(status)) { + continue; + } + if (isSucceeded(status)) { + break; + } + + throw new IOException("Signing operation " + describeOperation(operationId) + " failed: " + status); } - if ("Succeeded".equals(status)) { - break; + + if (!isSucceeded(status)) { + throw new IOException("Signing operation " + describeOperation(operationId) + " timed out"); } + } - throw new IOException("Signing operation " + operationId + " failed: " + status); + SignStatus signStatus = buildSignStatus(statusResponse); + if (signStatus == null) { + throw new IOException("Signing operation " + describeOperation(operationId) + " returned no result"); } - if (!"Succeeded".equals(response.get("status"))) { - throw new IOException("Signing operation " + operationId + " timed out"); + return signStatus; + } + + private SignStatus buildSignStatus(Map response) { + if (response == null) { + return null; } - SignStatus status = new SignStatus(); - Map result = (Map) response.get("result"); - if (result != null) { - status.signature = Base64.getDecoder().decode((String) result.get("signature")); - status.signingCertificate = new String(Base64.getDecoder().decode((String) result.get("signingCertificate"))); - } else { - // Fallback for older API versions - status.signature = Base64.getDecoder().decode((String) response.get("signature")); - status.signingCertificate = new String(Base64.getDecoder().decode((String) response.get("signingCertificate"))); + Map result = response; + Object resultNode = response.get("result"); + if (resultNode instanceof Map) { + result = (Map) resultNode; + } + + Object signatureValue = result.get("signature"); + Object certificateValue = result.get("signingCertificate"); + if (!(signatureValue instanceof String) || !(certificateValue instanceof String)) { + return null; } + SignStatus status = new SignStatus(); + status.signature = Base64.getDecoder().decode((String) signatureValue); + status.signingCertificate = new String(Base64.getDecoder().decode((String) certificateValue)); return status; } + private String resolveStatusResource(String account, String profile, String operationId, String operationLocation) throws IOException { + if (operationLocation != null && !operationLocation.isEmpty()) { + return ensureApiVersion(operationLocation.trim()); + } + if (operationId != null && !operationId.isEmpty()) { + return "/codesigningaccounts/" + account + "/certificateprofiles/" + profile + "/sign/" + operationId + "?api-version=" + API_VERSION; + } + throw new IOException("Signing operation identifier not returned by Azure Trusted Signing"); + } + + private String ensureApiVersion(String resource) { + if (resource == null || resource.isEmpty() || resource.contains("api-version=")) { + return resource; + } + return resource + (resource.contains("?") ? "&" : "?") + "api-version=" + API_VERSION; + } + + private String extractOperationId(String operationLocation) { + if (operationLocation == null || operationLocation.isEmpty()) { + return null; + } + String value = operationLocation; + int queryIndex = value.indexOf('?'); + if (queryIndex >= 0) { + value = value.substring(0, queryIndex); + } + int slashIndex = value.lastIndexOf('/'); + if (slashIndex >= 0 && slashIndex < value.length() - 1) { + return value.substring(slashIndex + 1); + } + return null; + } + + private String readOperationId(Map response) { + if (response == null) { + return null; + } + Object value = response.get("operationId"); + if (value == null) { + value = response.get("id"); + } + return value instanceof String ? (String) value : null; + } + + private boolean isPending(String status) { + String normalized = normalizeStatus(status); + return normalized == null || "INPROGRESS".equals(normalized) || "RUNNING".equals(normalized) || "NOTSTARTED".equals(normalized); + } + + private boolean isSucceeded(String status) { + return "SUCCEEDED".equals(normalizeStatus(status)); + } + + private String normalizeStatus(String status) { + return status == null ? null : status.replaceAll("\\s+", "").toUpperCase(Locale.ROOT); + } + + private String describeOperation(String operationId) { + return operationId != null ? operationId : "unknown"; + } + private static class SignStatus { public byte[] signature; public String signingCertificate; diff --git a/jsign-crypto/src/main/java/net/jsign/jca/RESTClient.java b/jsign-crypto/src/main/java/net/jsign/jca/RESTClient.java index a3d5cb8f..39d59f2d 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/RESTClient.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/RESTClient.java @@ -17,10 +17,12 @@ package net.jsign.jca; import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -70,6 +72,10 @@ public RESTClient errorHandler(Function, String> errorHandler) { return query("GET", resource, null, null); } + public RESTResponse getResponse(String resource) throws IOException { + return request("GET", resource, null, null); + } + public Map post(String resource, String body) throws IOException { return query("POST", resource, body, null); } @@ -78,6 +84,14 @@ public RESTClient errorHandler(Function, String> errorHandler) { return query("POST", resource, body, headers); } + public RESTResponse postResponse(String resource, String body) throws IOException { + return request("POST", resource, body, null); + } + + public RESTResponse postResponse(String resource, String body, Map headers) throws IOException { + return request("POST", resource, body, headers); + } + public Map post(String resource, Map params) throws IOException { return post(resource, params, false); } @@ -124,6 +138,10 @@ public RESTClient errorHandler(Function, String> errorHandler) { } private Map query(String method, String resource, String body, Map headers) throws IOException { + return request(method, resource, body, headers).getBody(); + } + + private RESTResponse request(String method, String resource, String body, Map headers) throws IOException { URL url = new URL(resource.startsWith("http") ? resource : endpoint + resource); log.finest(method + " " + url); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); @@ -167,20 +185,26 @@ public RESTClient errorHandler(Function, String> errorHandler) { log.finest("Content-Type: " + contentType); if (responseCode < 400) { - byte[] binaryResponse = IOUtils.toByteArray(conn.getInputStream()); + InputStream input = conn.getInputStream(); + byte[] binaryResponse = input != null ? IOUtils.toByteArray(input) : new byte[0]; String response = new String(binaryResponse, StandardCharsets.UTF_8); log.finest("Content-Length: " + binaryResponse.length); log.finest("Content:\n" + response); log.finest(""); - Object value = JsonReader.jsonToJava(response); - if (value instanceof Map) { - return (Map) value; - } else { - Map map = new HashMap<>(); - map.put("result", value); - return map; + Map responseBody = Collections.emptyMap(); + if (!response.trim().isEmpty()) { + Object value = JsonReader.jsonToJava(response); + if (value instanceof Map) { + responseBody = (Map) value; + } else { + Map map = new HashMap<>(); + map.put("result", value); + responseBody = map; + } } + + return new RESTResponse(responseCode, conn.getHeaderFields(), responseBody, binaryResponse); } else { String error = conn.getErrorStream() != null ? IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8) : ""; if (conn.getErrorStream() != null) { @@ -193,4 +217,47 @@ public RESTClient errorHandler(Function, String> errorHandler) { } } } + + static class RESTResponse { + private final int statusCode; + private final Map> headers; + private final Map body; + private final byte[] rawBody; + + RESTResponse(int statusCode, Map> headers, Map body, byte[] rawBody) { + this.statusCode = statusCode; + this.headers = headers != null ? headers : Collections.emptyMap(); + this.body = body != null ? body : Collections.emptyMap(); + this.rawBody = rawBody != null ? rawBody : new byte[0]; + } + + public int getStatusCode() { + return statusCode; + } + + public Map> getHeaders() { + return headers; + } + + public Map getBody() { + return body; + } + + public byte[] getRawBody() { + return rawBody; + } + + public String getHeader(String name) { + if (name == null) { + return null; + } + for (Map.Entry> entry : headers.entrySet()) { + if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(name)) { + List values = entry.getValue(); + return values != null && !values.isEmpty() ? values.get(0) : null; + } + } + return null; + } + } } diff --git a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java index 9a9ad34b..1a5d29a6 100644 --- a/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java +++ b/jsign-crypto/src/test/java/net/jsign/jca/AzureTrustedSigningServiceTest.java @@ -16,14 +16,19 @@ package net.jsign.jca; -import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; 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.LinkedHashMap; import java.util.List; +import java.util.Map; +import com.cedarsoftware.util.io.JsonReader; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -55,15 +60,16 @@ public void testGetAliases() throws Exception { public void testGetCertificateChain() throws Exception { onRequest() .havingMethodEqualTo("POST") - .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-02-05-preview") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile:sign") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(202) - .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); + .withHeader("operation-location", "http://localhost:" + port() + "/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb?api-version=2023-06-15-preview") + .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=2024-02-05-preview") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -72,7 +78,7 @@ public void testGetCertificateChain() throws Exception { .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") .thenRespond() .withStatus(200) - .withBody(new FileReader("target/test-classes/services/trustedsigning-sign.json")); + .withBody(loadSignSuccessResponse()); SigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); Certificate[] chain = service.getCertificateChain("MyAccount/MyProfile"); @@ -88,8 +94,8 @@ public void testGetCertificateChain() throws Exception { public void testGetCertificateChainWithError() { onRequest() .havingMethodEqualTo("POST") - .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-02-05-preview") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile:sign") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(403); @@ -112,15 +118,16 @@ public void testGetPrivateKey() throws Exception { public void testSign() throws Exception { onRequest() .havingMethodEqualTo("POST") - .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-02-05-preview") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile:sign") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(202) + .withHeader("operation-location", "http://localhost:" + port() + "/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb?api-version=2023-06-15-preview") .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=2024-02-05-preview") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") @@ -129,7 +136,7 @@ public void testSign() throws Exception { .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}") .thenRespond() .withStatus(200) - .withBody(new FileReader("target/test-classes/services/trustedsigning-sign.json")); + .withBody(loadSignSuccessResponse()); AzureTrustedSigningService service = new AzureTrustedSigningService("http://localhost:" + port(), "token"); SigningServicePrivateKey privateKey = service.getPrivateKey("MyAccount/MyProfile", null); @@ -144,15 +151,16 @@ public void testSign() throws Exception { public void testSignWithTimeout() throws Exception { onRequest() .havingMethodEqualTo("POST") - .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-02-05-preview") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile:sign") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(202) + .withHeader("operation-location", "http://localhost:" + port() + "/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb?api-version=2023-06-15-preview") .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=2024-02-05-preview") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"InProgress\",\"signature\":null,\"signingCertificate\":null}"); @@ -169,15 +177,16 @@ public void testSignWithTimeout() throws Exception { public void testSignWithFailure() throws Exception { onRequest() .havingMethodEqualTo("POST") - .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-02-05-preview") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile:sign") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(202) + .withHeader("operation-location", "http://localhost:" + port() + "/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign/1f234bd9-16cf-4283-9ee6-a460d31207bb?api-version=2023-06-15-preview") .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=2024-02-05-preview") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(200) .withBody("{\"operationId\":\"1f234bd9-16cf-4283-9ee6-a460d31207bb\",\"status\":\"Failed\",\"signature\":null,\"signingCertificate\":null}"); @@ -202,8 +211,8 @@ public void testSignWithInvalidAlgorithm() throws Exception { public void testSignWithAuthorizationError() throws Exception { onRequest() .havingMethodEqualTo("POST") - .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile/sign") - .havingQueryStringEqualTo("api-version=2024-02-05-preview") + .havingPathEqualTo("/codesigningaccounts/MyAccount/certificateprofiles/MyProfile:sign") + .havingQueryStringEqualTo("api-version=2023-06-15-preview") .respond() .withStatus(404) .withContentType("application/json") @@ -215,4 +224,17 @@ public void testSignWithAuthorizationError() throws Exception { Exception e = assertThrows(GeneralSecurityException.class, () -> service.sign(privateKey, "SHA256withRSA", "Hello".getBytes())); assertEquals("message", "InternalError - Response status code does not indicate success: 403 (Forbidden).", e.getCause().getMessage()); } + + private String loadSignSuccessResponse() throws Exception { + String json = new String(Files.readAllBytes(Paths.get("target/test-classes/services/trustedsigning-sign.json")), StandardCharsets.UTF_8); + Map response = (Map) JsonReader.jsonToJava(json); + Map payload = new LinkedHashMap<>(); + payload.put("operationId", response.get("operationId")); + payload.put("status", response.get("status")); + Map result = new LinkedHashMap<>(); + result.put("signature", response.get("signature")); + result.put("signingCertificate", response.get("signingCertificate")); + payload.put("result", result); + return JsonWriter.format(payload); + } } From 30b005e75439294a03aa5a9da2035d87e301ede5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0abr=C5=88=C3=A1k?= Date: Mon, 24 Nov 2025 18:17:48 +0100 Subject: [PATCH 8/8] Provide link to the documentation for future API changes in ATS --- .github/workflows/build.yml | 1 - .../src/main/java/net/jsign/jca/AzureTrustedSigningService.java | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f440e457..246edfd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ on: branches: [ '*' ] pull_request: branches: [ master ] - workflow_dispatch: env: STOREPASS: password diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java index 8fa7a033..18549132 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AzureTrustedSigningService.java @@ -42,6 +42,8 @@ */ public class AzureTrustedSigningService implements SigningService { + // Dataplane documentation for 2023-06-15-preview: + // https://github.com/Azure/azure-rest-api-specs/blob/main/specification/trustedsigning/data-plane/TrustedSigning/preview/2023-06-15-preview/azure.developer.trustedsigning.json private static final String API_VERSION = "2023-06-15-preview"; /** Cache of certificate chains indexed by alias */