Skip to content

Commit

Permalink
Unit tests for AzureKeyVaultSigningService
Browse files Browse the repository at this point in the history
  • Loading branch information
ebourg committed Jun 16, 2023
1 parent ad92e96 commit 9690216
Show file tree
Hide file tree
Showing 3 changed files with 356 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/**
* Copyright 2023 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.InvalidAlgorithmParameterException;
import java.security.KeyStoreException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import net.jsign.DigestAlgorithm;

import static net.jadler.Jadler.*;
import static org.junit.Assert.*;

public class AzureKeyVaultSigningServiceTest {

@Before
public void setUp() {
initJadler().withDefaultResponseStatus(404);
}

@After
public void tearDown() {
closeJadler();
}

@Test
public void testGetAliases() throws Exception {
onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificates.json"));

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
List<String> aliases = service.aliases();

assertEquals("aliases", Arrays.asList("test1", "test2", "test3"), aliases);
}

@Test
public void testGetAliasesError() {
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
try {
service.aliases();
fail("Exception not thrown");
} catch (KeyStoreException e) {
assertEquals("message", "Unable to retrieve Azure Key Vault certificate aliases", e.getMessage());
}
}

@Test
public void testGetCertificateChain() throws Exception {
onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates/test1")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
Certificate[] chain = service.getCertificateChain("test1");
assertNotNull("chain", chain);
assertEquals("number of certificates", 1, chain.length);
assertEquals("subject name", "CN=Jsign Test Certificate", ((X509Certificate) chain[0]).getSubjectDN().getName());

// check if the certificate is cached
Certificate[] chain2 = service.getCertificateChain("test1");
assertEquals("certificate", chain[0], chain2[0]);
}

@Test
public void testGetCertificateChainError() throws Exception {
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
try {
service.getCertificateChain("test1");
fail("Exception not thrown");
} catch (KeyStoreException e) {
assertEquals("message", "Unable to retrieve Azure Key Vault certificate 'test1'", e.getMessage());
}
}

@Test
public void testGetPrivateKey() throws Exception {
onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates/test1")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
assertNotNull("privateKey", privateKey);
assertEquals("algorithm", "https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055", privateKey.getId());
assertEquals("algorithm", "RSA", privateKey.getAlgorithm());
}

@Test
public void testGetPrivateKeyError() {
SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
try {
service.getPrivateKey("test1", null);
fail("Exception not thrown");
} catch (UnrecoverableKeyException e) {
assertEquals("message", "Unable to fetch Azure Key Vault private key for the certificate 'test1'", e.getMessage());
}
}

@Test
public void testSign() throws Exception {
byte[] data = "0123456789ABCDEF0123456789ABCDEF".getBytes();
byte[] digest = DigestAlgorithm.SHA256.getMessageDigest().digest(data);

onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates/test1")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));

onRequest()
.havingMethodEqualTo("POST")
.havingPathEqualTo("/keys/test1/38ca3e3560b94086ac604c5dd21aa055/sign")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.havingBodyEqualTo("{\"alg\":\"RS256\",\"value\":\"" + Base64.getEncoder().encodeToString(digest) + "\"}")
.respond()
.withStatus(200)
.withBody("{\"kid\":\"https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055\",\"value\":\"" + Base64.getEncoder().encodeToString(new byte[32]) + "\"}");

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
String keyId = privateKey.getId().replace("https://jsigntestkeyvault.vault.azure.net", "http://localhost:" + port());
privateKey = new SigningServicePrivateKey(keyId, privateKey.getAlgorithm());

byte[] signature = service.sign(privateKey, "SHA256withRSA", data);
assertNotNull("signature", signature);
assertArrayEquals("signature", new byte[32], signature);
}

@Test
public void testSignWithRSNULL() throws Exception {
byte[] data = "0123456789ABCDEF0123456789ABCDEF".getBytes();

onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates/test1")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));

onRequest()
.havingMethodEqualTo("POST")
.havingPathEqualTo("/keys/test1/38ca3e3560b94086ac604c5dd21aa055/sign")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.havingBodyEqualTo("{\"alg\":\"RSNULL\",\"value\":\"MCEwCQYFKw4DAhoFAAQUTYV9JAiwDD3RfwxP/PFbl/EEmGc=\"}")
.respond()
.withStatus(200)
.withBody("{\"kid\":\"https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055\",\"value\":\"" + Base64.getEncoder().encodeToString(new byte[32]) + "\"}");

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
String keyId = privateKey.getId().replace("https://jsigntestkeyvault.vault.azure.net", "http://localhost:" + port());
privateKey = new SigningServicePrivateKey(keyId, privateKey.getAlgorithm());

byte[] signature = service.sign(privateKey, "SHA1withRSA", data);
assertNotNull("signature", signature);
assertArrayEquals("signature", new byte[32], signature);
}

@Test
public void testSignWithUnsupportedAlgorithm() throws Exception {
onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates/test1")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);

try {
service.sign(privateKey, "MD5withRSA", new byte[0]);
fail("Exception not thrown");
} catch (InvalidAlgorithmParameterException e) {
assertEquals("message", "Unsupported signing algorithm: MD5withRSA", e.getMessage());
}
}

@Test(expected = GeneralSecurityException.class)
public void testSignError() throws Exception {
onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/certificates/test1")
.havingQueryStringEqualTo("api-version=7.2")
.havingHeaderEqualTo("Authorization", "Bearer token")
.respond()
.withStatus(200)
.withBody(new FileInputStream("target/test-classes/services/azure-certificate.json"));

SigningService service = new AzureKeyVaultSigningService("http://localhost:" + port(), "token");
SigningServicePrivateKey privateKey = service.getPrivateKey("test1", null);
String keyId = privateKey.getId().replace("https://jsigntestkeyvault.vault.azure.net", "http://localhost:" + port());
privateKey = new SigningServicePrivateKey(keyId, privateKey.getAlgorithm());

service.sign(privateKey, "SHA256withRSA", new byte[0]);
}
}
67 changes: 67 additions & 0 deletions jsign-core/src/test/resources/services/azure-certificate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1/38ca3e3560b94086ac604c5dd21aa055",
"kid": "https://jsigntestkeyvault.vault.azure.net/keys/test1/38ca3e3560b94086ac604c5dd21aa055",
"sid": "https://jsigntestkeyvault.vault.azure.net/secrets/test1/38ca3e3560b94086ac604c5dd21aa055",
"x5t": "v0sWC5lKV5G4k4UdqPWBo1PhBqs",
"cer": "MIIDSDCCAjCgAwIBAgIQI+1K7AuPQaOHuA2AcfOx1zANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDExZKc2lnbiBUZXN0IENlcnRpZmljYXRlMB4XDTIxMDYxMjA4MjcwOVoXDTIyMDYxMjA4MzcwOVowITEfMB0GA1UEAxMWSnNpZ24gVGVzdCBDZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+hb3oSz3Dmpsua/hdchodcxPVoC0Edc3yyYeXPyU15eZJggxmKFSbtuhhuMSCnyy/U5IdkwGL1Itb9Z0JkNbCuz0Qoj2US3lO1zG6BWLYATQzLI0P8gcC77MRFTolkCs8Db4zF/fm7887XjlnBIDKxxSaSFxXKMCRnnYkS71IxrmhcEI8UO9jaP6c5aux61nPEqn/eO64WEYp5FkjHrsmKz9T98MijLorMSCKnClniGBpJDOMy2koLNuWjYhjU5dwBcP0EaydZWm8ithVxQNgxFQTeaNf4q4kwZggULSlaIst9zLlXz1DDQSPrTJNIHs3TataFehpVpaezb+b4bPECAwEAAaN8MHowDgYDVR0PAQH/BAQDAgWgMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFPdrIYZz5JtpoVDDU9rGMSNHwjkEMB0GA1UdDgQWBBT3ayGGc+SbaaFQw1PaxjEjR8I5BDANBgkqhkiG9w0BAQsFAAOCAQEAHDS/9U5nYwnDDEZ5V0wSNnGSfO5MuOINeD/5qiQx7v4K+6mvx+tX9cKRaNsf7zHv6lEcuuwZgGS61dyl3RItKURrGQxpAk07oEEuNgj+8EcShfwZ7Flufk1DGaawYklBCY7JNvUctUqOGeZ56Vy94r42rleh2w9FPWcoA7ZypnYq1Z7aM6AWQwQ7AJuPxrp49ATBQFUTG17QrVh8EX8b3gG6D/IV3WhrFr9BX3DOcACdgKT82oLyTfFQOZRxM4hZzhLlBQWu5oBe1ZcGGhhz3GwIFSScuxxCJkA9FiiU31COaW/4Zmj86JM/sHzO9ntn9UKqpbB7bI964v8EKPo/Pg==",
"attributes": {
"enabled": true,
"nbf": 1623486429,
"exp": 1655023029,
"created": 1623487029,
"updated": 1623487029,
"recoveryLevel": "Recoverable+Purgeable",
"recoverableDays": 90
},
"policy": {
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1/policy",
"key_props": {
"exportable": false,
"kty": "RSA",
"key_size": 2048,
"reuse_key": false
},
"secret_props": {
"contentType": "application/x-pkcs12"
},
"x509_props": {
"subject": "CN=Jsign Test Certificate",
"sans": {
"dns_names": []
},
"ekus": [
"1.3.6.1.5.5.7.3.1",
"1.3.6.1.5.5.7.3.2"
],
"key_usage": [
"digitalSignature",
"keyEncipherment"
],
"validity_months": 12,
"basic_constraints": {
"ca": false
}
},
"lifetime_actions": [
{
"trigger": {
"lifetime_percentage": 80
},
"action": {
"action_type": "AutoRenew"
}
}
],
"issuer": {
"name": "Self"
},
"attributes": {
"enabled": true,
"created": 1623486458,
"updated": 1623486458
}
},
"pending": {
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1/pending"
}
}
41 changes: 41 additions & 0 deletions jsign-core/src/test/resources/services/azure-certificates.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"value": [
{
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test1",
"x5t": "v0sWC5lKV5G4k4UdqPWBo1PhBqs",
"attributes": {
"enabled": true,
"nbf": 1623486429,
"exp": 1655023029,
"created": 1623487029,
"updated": 1623487029
},
"subject": ""
},
{
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test2",
"x5t": "u9_e8Jr50AmAGj-4TBPcxLxeFfU",
"attributes": {
"enabled": true,
"nbf": 1623486175,
"exp": 1655022775,
"created": 1623486775,
"updated": 1623486775
},
"subject": ""
},
{
"id": "https://jsigntestkeyvault.vault.azure.net/certificates/test3",
"x5t": "KDt72FfYbTV8Wi4ZPmGtUx5rrHg",
"attributes": {
"enabled": true,
"nbf": 1623486380,
"exp": 1655022980,
"created": 1623486981,
"updated": 1623486981
},
"subject": ""
}
],
"nextLink": null
}

0 comments on commit 9690216

Please sign in to comment.