KeyStoreBuilderTest.java
/*
* 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;
import java.io.File;
import java.nio.file.Files;
import java.security.KeyStore;
import java.security.ProviderException;
import org.apache.commons.io.FileUtils;
import org.junit.Assume;
import org.junit.Test;
import net.jsign.jca.OpenPGPCardTest;
import net.jsign.jca.PIVCardTest;
import static net.jsign.KeyStoreType.*;
import static org.junit.Assert.*;
public class KeyStoreBuilderTest {
@Test
public void testCreateFile() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder();
File file = builder.createFile(null);
assertNull(file);
// relative path, default base directory
file = builder.createFile("keystore.p12");
assertEquals(new File(".").getCanonicalFile(), file.getCanonicalFile().getParentFile());
assertEquals("keystore.p12", file.getName());
// relative path, custom base directory
builder.setBaseDir(new File("target/test-classes"));
file = builder.createFile("keystores/keystore.p12");
assertEquals(new File("target/test-classes/keystores").getCanonicalFile(), file.getCanonicalFile().getParentFile());
assertEquals("keystore.p12", file.getName());
// absolute path
builder.setBaseDir(new File("target/test-classes/keystores"));
file = builder.createFile(new File("keystore.p12").getAbsolutePath());
assertEquals(new File(".").getCanonicalFile(), file.getCanonicalFile().getParentFile());
assertEquals("keystore.p12", file.getName());
}
@Test
public void testReadPasswordFromEnvironment() {
Assume.assumeTrue("STOREPASS environment variable not defined", System.getenv().containsKey("STOREPASS"));
KeyStoreBuilder builder = new KeyStoreBuilder().storepass("env:STOREPASS");
assertEquals("password", builder.storepass());
}
@Test
public void testReadPasswordFromEnvironmentFailed() {
Assume.assumeFalse(System.getenv().containsKey("MISSING_VAR"));
KeyStoreBuilder builder = new KeyStoreBuilder().storepass("env:MISSING_VAR");
Exception e = assertThrows(IllegalArgumentException.class, builder::storepass);
assertEquals("message", "Failed to read the storepass parameter, the 'MISSING_VAR' environment variable is not defined", e.getMessage());
}
@Test
public void testReadPasswordFromFile() throws Exception {
Files.write(new File("target/test-classes/storepass.txt").toPath(), "password".getBytes());
KeyStoreBuilder builder = new KeyStoreBuilder().storepass("file:target/test-classes/storepass.txt");
assertEquals("password", builder.storepass());
}
@Test
public void testReadPasswordFromFileFailed() {
KeyStoreBuilder builder = new KeyStoreBuilder().storepass("file:/path/to/missing/file");
Exception e = assertThrows(IllegalArgumentException.class, builder::storepass);
assertEquals("message", "Failed to read the storepass parameter from the file '/path/to/missing/file'", e.getMessage());
}
@Test
public void testBuildAWS() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(AWS);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the AWS region", e.getMessage());
builder.keystore("eu-west-1");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "certfile parameter must be set", e.getMessage());
builder.certfile("keystores/jsign-test-certificate.pem");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertTrue("message", e.getMessage().matches(
"storepass parameter must specify the AWS credentials\\: \\<accessKey\\>\\|\\<secretKey\\>\\[\\|\\<sessionToken\\>\\], when not running from ECS or an EC2 instance"));
builder.storepass("<accessKey>|<secretKey>|<sessionToken>");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildAzureKeyVault() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(AZUREKEYVAULT);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the Azure vault name", e.getMessage());
builder.keystore("jsigntestkeyvault");
e = assertThrows(IllegalArgumentException.class, builder::build);
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 testBuildDigiCertONE() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(DIGICERTONE);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the DigiCert ONE API key and the client certificate: <apikey>|<keystore>|<password>", e.getMessage());
builder.storepass("0123456789ABCDEF");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the DigiCert ONE API key and the client certificate: <apikey>|<keystore>|<password>", e.getMessage());
builder.storepass("APIKEY|keystore.p12|password");
e = assertThrows(RuntimeException.class, builder::build);
assertEquals("message", "Failed to load the client certificate for DigiCert ONE", e.getMessage());
builder.keystore("https://clientauth.demo.one.digicert.com");
builder.storepass("APIKEY|target/test-classes/keystores/keystore.p12|password");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildESigner() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(ESIGNER);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the SSL.com username and password: <username>|<password>", e.getMessage());
builder.storepass("password");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the SSL.com username and password: <username>|<password>", e.getMessage());
builder.storepass("esigner_test|esignerTest#1");
e = assertThrows(IllegalStateException.class, builder::build);
assertEquals("message", "Authentication failed with SSL.com", e.getMessage());
builder.storepass("esigner_demo|esignerDemo#1");
builder.keystore("https://cs-try.ssl.com");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildGoogleCloud() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(GOOGLECLOUD);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the Google Cloud keyring", e.getMessage());
builder.keystore("projects/first-rain-123/locations/global/keyRings/mykeyring/cryptoKeys/jsign");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the path of the keyring (projects/{projectName}/locations/{location}/keyRings/{keyringName})", e.getMessage());
builder.keystore("projects/first-rain-123/locations/global/keyRings/mykeyring");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the Google Cloud API access token", e.getMessage());
builder.storepass("0123456789ABCDEF");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "certfile parameter must be set", e.getMessage());
builder.certfile("keystores/jsign-test-certificate.pem");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildHashiCorpVault() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(HASHICORPVAULT);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the HashiCorp Vault secrets engine URL", e.getMessage());
builder.keystore("https://vault.example.com:8200/v1/gcpkms/");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the HashiCorp Vault token", e.getMessage());
builder.storepass("0123456789ABCDEF");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "certfile parameter must be set", e.getMessage());
builder.certfile("keystores/jsign-test-certificate.pem");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildOracleCloud() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(ORACLECLOUD);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "certfile parameter must be set", e.getMessage());
builder.certfile("keystores/jsign-test-certificate.pem");
File config = File.createTempFile("ociconfig", null);
config.deleteOnExit();
FileUtils.writeStringToFile(config, "[DEFAULT]\n", "UTF-8");
builder.storepass(config.getAbsolutePath() + "|DEFAULT");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildTrustedSigning() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(TRUSTEDSIGNING);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the Azure endpoint (<region>.codesigning.azure.net)", e.getMessage());
builder.keystore("https://weu.codesigning.azure.net");
e = assertThrows(IllegalArgumentException.class, builder::build);
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 testBuildGaraSign() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(GARASIGN);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the GaraSign username/password and/or the path to the keystore containing the TLS client certificate: <username>|<password>, <certificate>, or <username>|<password>|<certificate>", e.getMessage());
builder.storepass("username|password|keystore.p12|storepass");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the GaraSign username/password and/or the path to the keystore containing the TLS client certificate: <username>|<password>, <certificate>, or <username>|<password>|<certificate>", e.getMessage());
builder.storepass("username|password");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
builder = new KeyStoreBuilder().storetype(GARASIGN).keystore("https://api.garantir.io");
builder.storepass("keystore.p12");
keystore = builder.build();
assertNotNull("keystore", keystore);
builder = new KeyStoreBuilder().storetype(GARASIGN).keystore("https://api.garantir.io");
builder.storepass("keystore.p12");
builder.storepass("username|password|keystore.p12");
builder.keypass("keypass");
keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildSignPath() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(SIGNPATH);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the SignPath organization id (e.g. eacd4b78-6038-4450-9eec-4acd1c7ba6f1)", e.getMessage());
builder.keystore("eacd4b78-6038-4450-9eec-4acd1c7ba6f1");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the SignPath API access token", e.getMessage());
builder.storepass("AIk/65sl23lA1nVV/pgSqk96SvHFsSw3xitmp5Qhr+F/");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildSignServer() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(SIGNSERVER);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must specify the SignServer API endpoint (e.g. https://example.com/signserver/)", e.getMessage());
builder.keystore("https://example.com/signserver");
builder.storepass("username|password|certificate.p12");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the SignServer username/password or the path to the keystore containing the TLS client certificate: <username>|<password> or <certificate>", e.getMessage());
builder.storepass("username|password");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
builder.storepass(null);
keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildJKS() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(JKS);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must be set", e.getMessage());
builder.keystore("target/test-classes/keystores/missing.jks");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "The keystore target/test-classes/keystores/missing.jks couldn't be found", e.getMessage());
builder.keystore("target/test-classes/keystores/keystore.jks");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildJCEKS() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(JCEKS);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must be set", e.getMessage());
builder.keystore("target/test-classes/keystores/missing.jceks");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "The keystore target/test-classes/keystores/missing.jceks couldn't be found", e.getMessage());
builder.keystore("target/test-classes/keystores/keystore.jceks");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildPKCS12() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(PKCS12);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must be set", e.getMessage());
builder.keystore("target/test-classes/keystores/missing.p12");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "The keystore target/test-classes/keystores/missing.p12 couldn't be found", e.getMessage());
builder.keystore("target/test-classes/keystores/keystore.p12");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildWithoutStoreType() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder();
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "Either keystore, or keyfile and certfile, or storetype parameters must be set", e.getMessage());
builder.keystore("target/test-classes/keystores/keystore.error");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "Keystore file 'target/test-classes/keystores/keystore.error' not found", e.getMessage());
builder.keystore("target/test-classes/keystores/keystore.p12");
assertEquals("storetype", PKCS12, builder.storetype());
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildPKCS11() {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(PKCS11);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter must be set", e.getMessage());
builder.keystore("target/test-classes/pkcs11-missing.cfg");
e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "keystore parameter should either refer to the SunPKCS11 configuration file or to the name of the provider configured in jre/lib/security/java.security", e.getMessage());
builder.keystore("target/test-classes/keystores/keystore.p12");
e = assertThrows(ProviderException.class, builder::build);
assertEquals("message", "Failed to create a SunPKCS11 provider from the configuration target/test-classes/keystores/keystore.p12", e.getMessage());
}
@Test
public void testBuildOpenPGP() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(OPENPGP);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the PIN", e.getMessage());
OpenPGPCardTest.assumeCardPresent();
builder.storepass("123456");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testBuildPIV() throws Exception {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype(PIV);
Exception e = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("message", "storepass parameter must specify the PIN", e.getMessage());
PIVCardTest.assumeCardPresent();
builder.storepass("123456");
KeyStore keystore = builder.build();
assertNotNull("keystore", keystore);
}
@Test
public void testLowerCaseStoreType() {
KeyStoreBuilder builder = new KeyStoreBuilder().storetype("pkcs12");
assertEquals("storetype", PKCS12, builder.storetype());
}
}