Skip to content

Commit a7fb5a0

Browse files
committed
Unit tests for AzureKeyVaultSigningService
1 parent b9b0d3c commit a7fb5a0

File tree

3 files changed

+356
-0
lines changed

3 files changed

+356
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/**
2+
* Copyright 2023 Emmanuel Bourg
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package net.jsign.jca;
18+
19+
import java.io.FileInputStream;
20+
import java.security.GeneralSecurityException;
21+
import java.security.InvalidAlgorithmParameterException;
22+
import java.security.KeyStoreException;
23+
import java.security.UnrecoverableKeyException;
24+
import java.security.cert.Certificate;
25+
import java.security.cert.X509Certificate;
26+
import java.util.Arrays;
27+
import java.util.Base64;
28+
import java.util.List;
29+
30+
import org.junit.After;
31+
import org.junit.Before;
32+
import org.junit.Test;
33+
34+
import net.jsign.DigestAlgorithm;
35+
36+
import static net.jadler.Jadler.*;
37+
import static org.junit.Assert.*;
38+
39+
public class AzureKeyVaultSigningServiceTest {
40+
41+
@Before
42+
public void setUp() {
43+
initJadler().withDefaultResponseStatus(404);
44+
}
45+
46+
@After
47+
public void tearDown() {
48+
closeJadler();
49+
}
50+
51+
@Test
52+
public void testGetAliases() throws Exception {
53+
onRequest()
54+
.havingMethodEqualTo("GET")
55+
.havingPathEqualTo("/certificates")
56+
.havingQueryStringEqualTo("api-version=7.2")
57+
.havingHeaderEqualTo("Authorization", "Bearer token")
58+
.respond()
59+
.withStatus(200)
60+
.withBody(new FileInputStream("target/test-classes/services/azure-certificates.json"));
61+
62+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
63+
List<String> aliases = service.aliases();
64+
65+
assertEquals("aliases", Arrays.asList("test1", "test2", "test3"), aliases);
66+
}
67+
68+
@Test
69+
public void testGetAliasesError() {
70+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
71+
try {
72+
service.aliases();
73+
fail("Exception not thrown");
74+
} catch (KeyStoreException e) {
75+
assertEquals("message", "Unable to retrieve Azure Key Vault certificate aliases", e.getMessage());
76+
}
77+
}
78+
79+
@Test
80+
public void testGetCertificateChain() throws Exception {
81+
onRequest()
82+
.havingMethodEqualTo("GET")
83+
.havingPathEqualTo("/certificates/test1")
84+
.havingQueryStringEqualTo("api-version=7.2")
85+
.havingHeaderEqualTo("Authorization", "Bearer token")
86+
.respond()
87+
.withStatus(200)
88+
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));
89+
90+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
91+
Certificate[] chain = service.getCertificateChain("test1");
92+
assertNotNull("chain", chain);
93+
assertEquals("number of certificates", 1, chain.length);
94+
assertEquals("subject name", "CN=Jsign Test Certificate", ((X509Certificate) chain[0]).getSubjectDN().getName());
95+
96+
// check if the certificate is cached
97+
Certificate[] chain2 = service.getCertificateChain("test1");
98+
assertEquals("certificate", chain[0], chain2[0]);
99+
}
100+
101+
@Test
102+
public void testGetCertificateChainError() throws Exception {
103+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
104+
try {
105+
service.getCertificateChain("test1");
106+
fail("Exception not thrown");
107+
} catch (KeyStoreException e) {
108+
assertEquals("message", "Unable to retrieve Azure Key Vault certificate 'test1'", e.getMessage());
109+
}
110+
}
111+
112+
@Test
113+
public void testGetPrivateKey() throws Exception {
114+
onRequest()
115+
.havingMethodEqualTo("GET")
116+
.havingPathEqualTo("/certificates/test1")
117+
.havingQueryStringEqualTo("api-version=7.2")
118+
.havingHeaderEqualTo("Authorization", "Bearer token")
119+
.respond()
120+
.withStatus(200)
121+
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));
122+
123+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
124+
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
125+
assertNotNull("privateKey", privateKey);
126+
assertEquals("algorithm", "https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055", privateKey.getId());
127+
assertEquals("algorithm", "RSA", privateKey.getAlgorithm());
128+
}
129+
130+
@Test
131+
public void testGetPrivateKeyError() {
132+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
133+
try {
134+
service.getPrivateKey("test1", null);
135+
fail("Exception not thrown");
136+
} catch (UnrecoverableKeyException e) {
137+
assertEquals("message", "Unable to fetch Azure Key Vault private key for the certificate 'test1'", e.getMessage());
138+
}
139+
}
140+
141+
@Test
142+
public void testSign() throws Exception {
143+
byte[] data = "0123456789ABCDEF0123456789ABCDEF".getBytes();
144+
byte[] digest = DigestAlgorithm.SHA256.getMessageDigest().digest(data);
145+
146+
onRequest()
147+
.havingMethodEqualTo("GET")
148+
.havingPathEqualTo("/certificates/test1")
149+
.havingQueryStringEqualTo("api-version=7.2")
150+
.havingHeaderEqualTo("Authorization", "Bearer token")
151+
.respond()
152+
.withStatus(200)
153+
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));
154+
155+
onRequest()
156+
.havingMethodEqualTo("POST")
157+
.havingPathEqualTo("/keys/test1/38ca3e3560b94086ac604c5dd21aa055/sign")
158+
.havingQueryStringEqualTo("api-version=7.2")
159+
.havingHeaderEqualTo("Authorization", "Bearer token")
160+
.havingBodyEqualTo("{\"alg\":\"RS256\",\"value\":\"" + Base64.getEncoder().encodeToString(digest) + "\"}")
161+
.respond()
162+
.withStatus(200)
163+
.withBody("{\"kid\":\"https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055\",\"value\":\"" + Base64.getEncoder().encodeToString(new byte[32]) + "\"}");
164+
165+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
166+
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
167+
String keyId = privateKey.getId().replace("https://jsigntestkeyvault.vault.azure.net", "http://localhost:" + port());
168+
privateKey = new SigningServicePrivateKey(keyId, privateKey.getAlgorithm());
169+
170+
byte[] signature = service.sign(privateKey, "SHA256withRSA", data);
171+
assertNotNull("signature", signature);
172+
assertArrayEquals("signature", new byte[32], signature);
173+
}
174+
175+
@Test
176+
public void testSignWithRSNULL() throws Exception {
177+
byte[] data = "0123456789ABCDEF0123456789ABCDEF".getBytes();
178+
179+
onRequest()
180+
.havingMethodEqualTo("GET")
181+
.havingPathEqualTo("/certificates/test1")
182+
.havingQueryStringEqualTo("api-version=7.2")
183+
.havingHeaderEqualTo("Authorization", "Bearer token")
184+
.respond()
185+
.withStatus(200)
186+
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));
187+
188+
onRequest()
189+
.havingMethodEqualTo("POST")
190+
.havingPathEqualTo("/keys/test1/38ca3e3560b94086ac604c5dd21aa055/sign")
191+
.havingQueryStringEqualTo("api-version=7.2")
192+
.havingHeaderEqualTo("Authorization", "Bearer token")
193+
.havingBodyEqualTo("{\"alg\":\"RSNULL\",\"value\":\"MCEwCQYFKw4DAhoFAAQUTYV9JAiwDD3RfwxP/PFbl/EEmGc=\"}")
194+
.respond()
195+
.withStatus(200)
196+
.withBody("{\"kid\":\"https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055\",\"value\":\"" + Base64.getEncoder().encodeToString(new byte[32]) + "\"}");
197+
198+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
199+
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
200+
String keyId = privateKey.getId().replace("https://jsigntestkeyvault.vault.azure.net", "http://localhost:" + port());
201+
privateKey = new SigningServicePrivateKey(keyId, privateKey.getAlgorithm());
202+
203+
byte[] signature = service.sign(privateKey, "SHA1withRSA", data);
204+
assertNotNull("signature", signature);
205+
assertArrayEquals("signature", new byte[32], signature);
206+
}
207+
208+
@Test
209+
public void testSignWithUnsupportedAlgorithm() throws Exception {
210+
onRequest()
211+
.havingMethodEqualTo("GET")
212+
.havingPathEqualTo("/certificates/test1")
213+
.havingQueryStringEqualTo("api-version=7.2")
214+
.havingHeaderEqualTo("Authorization", "Bearer token")
215+
.respond()
216+
.withStatus(200)
217+
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));
218+
219+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
220+
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
221+
222+
try {
223+
service.sign(privateKey, "MD5withRSA", new byte[0]);
224+
fail("Exception not thrown");
225+
} catch (InvalidAlgorithmParameterException e) {
226+
assertEquals("message", "Unsupported signing algorithm: MD5withRSA", e.getMessage());
227+
}
228+
}
229+
230+
@Test(expected = GeneralSecurityException.class)
231+
public void testSignError() throws Exception {
232+
onRequest()
233+
.havingMethodEqualTo("GET")
234+
.havingPathEqualTo("/certificates/test1")
235+
.havingQueryStringEqualTo("api-version=7.2")
236+
.havingHeaderEqualTo("Authorization", "Bearer token")
237+
.respond()
238+
.withStatus(200)
239+
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));
240+
241+
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
242+
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
243+
String keyId = privateKey.getId().replace("https://jsigntestkeyvault.vault.azure.net", "http://localhost:" + port());
244+
privateKey = new SigningServicePrivateKey(keyId, privateKey.getAlgorithm());
245+
246+
service.sign(privateKey, "SHA256withRSA", new byte[0]);
247+
}
248+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1/38ca3e3560b94086ac604c5dd21aa055",
3+
"kid": "https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055",
4+
"sid": "https://jsigntestkeyvault.vault.azure.net/secrets/test1/38ca3e3560b94086ac604c5dd21aa055",
5+
"x5t": "v0sWC5lKV5G4k4UdqPWBo1PhBqs",
6+
"cer": "MIIDSDCCAjCgAwIBAgIQI+1K7AuPQaOHuA2AcfOx1zANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDExZKc2lnbiBUZXN0IENlcnRpZmljYXRlMB4XDTIxMDYxMjA4MjcwOVoXDTIyMDYxMjA4MzcwOVowITEfMB0GA1UEAxMWSnNpZ24gVGVzdCBDZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+hb3oSz3Dmpsua/hdchodcxPVoC0Edc3yyYeXPyU15eZJggxmKFSbtuhhuMSCnyy/U5IdkwGL1Itb9Z0JkNbCuz0Qoj2US3lO1zG6BWLYATQzLI0P8gcC77MRFTolkCs8Db4zF/fm7887XjlnBIDKxxSaSFxXKMCRnnYkS71IxrmhcEI8UO9jaP6c5aux61nPEqn/eO64WEYp5FkjHrsmKz9T98MijLorMSCKnClniGBpJDOMy2koLNuWjYhjU5dwBcP0EaydZWm8ithVxQNgxFQTeaNf4q4kwZggULSlaIst9zLlXz1DDQSPrTJNIHs3TataFehpVpaezb+b4bPECAwEAAaN8MHowDgYDVR0PAQH/BAQDAgWgMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFPdrIYZz5JtpoVDDU9rGMSNHwjkEMB0GA1UdDgQWBBT3ayGGc+SbaaFQw1PaxjEjR8I5BDANBgkqhkiG9w0BAQsFAAOCAQEAHDS/9U5nYwnDDEZ5V0wSNnGSfO5MuOINeD/5qiQx7v4K+6mvx+tX9cKRaNsf7zHv6lEcuuwZgGS61dyl3RItKURrGQxpAk07oEEuNgj+8EcShfwZ7Flufk1DGaawYklBCY7JNvUctUqOGeZ56Vy94r42rleh2w9FPWcoA7ZypnYq1Z7aM6AWQwQ7AJuPxrp49ATBQFUTG17QrVh8EX8b3gG6D/IV3WhrFr9BX3DOcACdgKT82oLyTfFQOZRxM4hZzhLlBQWu5oBe1ZcGGhhz3GwIFSScuxxCJkA9FiiU31COaW/4Zmj86JM/sHzO9ntn9UKqpbB7bI964v8EKPo/Pg==",
7+
"attributes": {
8+
"enabled": true,
9+
"nbf": 1623486429,
10+
"exp": 1655023029,
11+
"created": 1623487029,
12+
"updated": 1623487029,
13+
"recoveryLevel": "Recoverable+Purgeable",
14+
"recoverableDays": 90
15+
},
16+
"policy": {
17+
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1/policy",
18+
"key_props": {
19+
"exportable": false,
20+
"kty": "RSA",
21+
"key_size": 2048,
22+
"reuse_key": false
23+
},
24+
"secret_props": {
25+
"contentType": "application/x-pkcs12"
26+
},
27+
"x509_props": {
28+
"subject": "CN=Jsign Test Certificate",
29+
"sans": {
30+
"dns_names": []
31+
},
32+
"ekus": [
33+
"1.3.6.1.5.5.7.3.1",
34+
"1.3.6.1.5.5.7.3.2"
35+
],
36+
"key_usage": [
37+
"digitalSignature",
38+
"keyEncipherment"
39+
],
40+
"validity_months": 12,
41+
"basic_constraints": {
42+
"ca": false
43+
}
44+
},
45+
"lifetime_actions": [
46+
{
47+
"trigger": {
48+
"lifetime_percentage": 80
49+
},
50+
"action": {
51+
"action_type": "AutoRenew"
52+
}
53+
}
54+
],
55+
"issuer": {
56+
"name": "Self"
57+
},
58+
"attributes": {
59+
"enabled": true,
60+
"created": 1623486458,
61+
"updated": 1623486458
62+
}
63+
},
64+
"pending": {
65+
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1/pending"
66+
}
67+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"value": [
3+
{
4+
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1",
5+
"x5t": "v0sWC5lKV5G4k4UdqPWBo1PhBqs",
6+
"attributes": {
7+
"enabled": true,
8+
"nbf": 1623486429,
9+
"exp": 1655023029,
10+
"created": 1623487029,
11+
"updated": 1623487029
12+
},
13+
"subject": ""
14+
},
15+
{
16+
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test2",
17+
"x5t": "u9_e8Jr50AmAGj-4TBPcxLxeFfU",
18+
"attributes": {
19+
"enabled": true,
20+
"nbf": 1623486175,
21+
"exp": 1655022775,
22+
"created": 1623486775,
23+
"updated": 1623486775
24+
},
25+
"subject": ""
26+
},
27+
{
28+
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test3",
29+
"x5t": "KDt72FfYbTV8Wi4ZPmGtUx5rrHg",
30+
"attributes": {
31+
"enabled": true,
32+
"nbf": 1623486380,
33+
"exp": 1655022980,
34+
"created": 1623486981,
35+
"updated": 1623486981
36+
},
37+
"subject": ""
38+
}
39+
],
40+
"nextLink": null
41+
}

0 commit comments

Comments
 (0)