diff --git a/README.md b/README.md index 98028d1..7fd553a 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Once you add the AIS client library as a dependency to your project, you can con RestClientConfiguration restConfig = RestClientConfiguration.builder() .withServiceSignUrl("https://ais.swisscom.com/AIS-Server/rs/v1.0/sign") .withServicePendingUrl("https://ais.swisscom.com/AIS-Server/rs/v1.0/pending") + // the server certificate file is optional, in case it is omitted the CA must be a trusted one .withServerCertificateFile("/home/user/ais-server.crt") .withClientKeyFile("/home/user/ais-client.key") .withClientKeyPassword("secret") diff --git a/docs/configure-the-AIS-client.md b/docs/configure-the-AIS-client.md index 2d39455..dadff8f 100644 --- a/docs/configure-the-AIS-client.md +++ b/docs/configure-the-AIS-client.md @@ -13,7 +13,7 @@ license.file=${ITEXT_LICENSE_FILE_PATH} server.rest.signUrl=https://ais.swisscom.com/AIS-Server/rs/v1.0/sign # The AIS server REST URL for sending the Signature status poll requests (Pending requests) server.rest.pendingUrl=https://ais.swisscom.com/AIS-Server/rs/v1.0/pending -# The AIS server trusted CA certificate file +# The AIS server trusted CA certificate file. The configuration parameter is optional and in case it is omitted the CA must be a trusted one. server.cert.file=/home/user/ais-server.crt # -- # The client's private key file (corresponding to the public key attached to the client's certificate) @@ -192,7 +192,7 @@ swisscom: rest.signUrl: https://ais.swisscom.com/AIS-Server/rs/v1.0/sign # The AIS server REST URL for sending the Signature status poll requests (Pending requests) rest.pendingUrl: https://ais.swisscom.com/AIS-Server/rs/v1.0/pending - # The AIS server trusted CA certificate file + # The AIS server trusted CA certificate file. The configuration parameter is optional and in case it is omitted the CA must be a trusted one. cert.file: /home/user/ais-server.crt client: # The client's private key file (corresponding to the public key attached to the client's certificate) diff --git a/docs/use-the-AIS-client-programmatically.md b/docs/use-the-AIS-client-programmatically.md index e411234..c0d431d 100644 --- a/docs/use-the-AIS-client-programmatically.md +++ b/docs/use-the-AIS-client-programmatically.md @@ -66,6 +66,7 @@ Configure the REST client: RestClientConfiguration restConfig = RestClientConfiguration.builder() .withServiceSignUrl("https://ais.swisscom.com/AIS-Server/rs/v1.0/sign") .withServicePendingUrl("https://ais.swisscom.com/AIS-Server/rs/v1.0/pending") + // the server certificate file is optional, in case it is omitted the CA must be a trusted one .withServerCertificateFile("/home/user/ais-server.crt") .withClientKeyFile("/home/user/ais-client.key") .withClientKeyPassword("secret") diff --git a/src/main/java/com/swisscom/ais/itext7/TestFullyProgrammaticConfiguration.java b/src/main/java/com/swisscom/ais/itext7/TestFullyProgrammaticConfiguration.java index 12d5625..a221e99 100644 --- a/src/main/java/com/swisscom/ais/itext7/TestFullyProgrammaticConfiguration.java +++ b/src/main/java/com/swisscom/ais/itext7/TestFullyProgrammaticConfiguration.java @@ -43,6 +43,7 @@ public static void main(String[] args) throws IOException { RestClientConfiguration restConfig = RestClientConfiguration.builder() .withServiceSignUrl("https://ais.swisscom.com/AIS-Server/rs/v1.0/sign") .withServicePendingUrl("https://ais.swisscom.com/AIS-Server/rs/v1.0/pending") + // the server certificate file is optional, in case it is omitted the CA must be a trusted one .withServerCertificateFile("/home/user/ais-server.crt") .withClientKeyFile("/home/user/ais-client.key") .withClientKeyPassword("secret") diff --git a/src/main/java/com/swisscom/ais/itext7/client/common/PropertiesLoader.java b/src/main/java/com/swisscom/ais/itext7/client/common/PropertiesLoader.java index ad3fd65..addf496 100644 --- a/src/main/java/com/swisscom/ais/itext7/client/common/PropertiesLoader.java +++ b/src/main/java/com/swisscom/ais/itext7/client/common/PropertiesLoader.java @@ -22,10 +22,10 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; +import java.util.Optional; import java.util.Properties; import java.util.function.Function; -@SuppressWarnings("unused") public abstract class PropertiesLoader { private static final String ENV_VARIABLE_PREFIX = "${"; @@ -34,12 +34,6 @@ public abstract class PropertiesLoader { protected PropertiesLoader() { } - private static void validateProperty(String propertyName, String propertyValue) { - if (StringUtils.isBlank(propertyValue)) { - throw new IllegalStateException(String.format("Invalid configuration. The [%s] property is missing or is empty.", propertyName)); - } - } - protected abstract T fromConfigurationProvider(ConfigurationProvider provider); public T fromPropertiesClasspathFile(String fileName) { @@ -64,24 +58,18 @@ public Properties loadPropertiesFromClasspathFile(Class clazz, String filepat } } - public String extractStringProperty(ConfigurationProvider provider, String propertyName, boolean mandatory) { - return extractProperty(provider, propertyName, mandatory); + public String extractStringProperty(ConfigurationProvider provider, String propertyName, boolean isMandatory) { + return extractProperty(provider, propertyName, isMandatory).orElse(null); } - public Integer extractIntProperty(ConfigurationProvider provider, String propertyName, boolean mandatory) { - String property = extractProperty(provider, propertyName, mandatory); - if (property == null && !mandatory) { - return null; - } - return Integer.parseInt(property); + public Integer extractIntProperty(ConfigurationProvider provider, String propertyName, boolean isMandatory) { + Optional property = extractProperty(provider, propertyName, isMandatory); + return property.isPresent() ? Integer.parseInt(property.get()) : null; } - public String extractSecretProperty(ConfigurationProvider provider, String propertyName, boolean mandatory) { - String property = extractProperty(provider, propertyName, mandatory); - if (property == null && !mandatory) { - return null; - } - return shouldExtractFromEnvVariable(property) ? System.getenv(extractEnvPropertyName(property)) : property; + public String extractSecretProperty(ConfigurationProvider provider, String propertyName, boolean isMandatory) { + Optional property = extractProperty(provider, propertyName, isMandatory); + return property.isPresent() && shouldExtractFromEnvVariable(property.get()) ? System.getenv(extractEnvPropertyName(property.get())) : null; } private boolean shouldExtractFromEnvVariable(String property) { @@ -97,12 +85,14 @@ public E extractProperty(ConfigurationProvider provider, String propertyName return StringUtils.isNotBlank(propertyValue) ? mapperFunction.apply(propertyValue) : defaultValue; } - private String extractProperty(ConfigurationProvider provider, String propertyName, boolean mandatory) { + private Optional extractProperty(ConfigurationProvider provider, String propertyName, boolean isMandatory) { String propertyValue = provider.getProperty(propertyName); - if (StringUtils.isBlank(propertyValue) && !mandatory) { - return null; + if (StringUtils.isBlank(propertyValue) && !isMandatory) { + return Optional.empty(); + } + if (StringUtils.isBlank(propertyValue)) { + throw new IllegalStateException(String.format("Invalid configuration. The [%s] property is missing or is empty.", propertyName)); } - validateProperty(propertyName, propertyValue); - return propertyValue; + return Optional.of(propertyValue); } } diff --git a/src/main/java/com/swisscom/ais/itext7/client/rest/RestClientConfiguration.java b/src/main/java/com/swisscom/ais/itext7/client/rest/RestClientConfiguration.java index d75fc4a..2ad2979 100644 --- a/src/main/java/com/swisscom/ais/itext7/client/rest/RestClientConfiguration.java +++ b/src/main/java/com/swisscom/ais/itext7/client/rest/RestClientConfiguration.java @@ -110,7 +110,7 @@ protected RestClientConfiguration.Builder fromConfigurationProvider(Configuratio .withClientKeyFile(extractStringProperty(provider, "client.auth.keyFile", true)) .withClientKeyPassword(extractSecretProperty(provider, "client.auth.keyPassword", false)) .withClientCertificateFile(extractStringProperty(provider, "client.cert.file", true)) - .withServerCertificateFile(extractStringProperty(provider, "server.cert.file", true)) + .withServerCertificateFile(extractStringProperty(provider, "server.cert.file", false)) .withMaxTotalConnections(extractIntProperty(provider, "client.http.maxTotalConnections", true)) .withMaxConnectionsPerRoute(extractIntProperty(provider, "client.http.maxConnectionsPerRoute", true)) .withConnectionTimeoutInSec(extractIntProperty(provider, "client.http.connectionTimeoutInSeconds", true)) @@ -122,7 +122,6 @@ private void validate() { ValidationUtils.notBlank(servicePendingUrl, "The servicePendingUrl parameter of the REST client configuration must not be empty"); ValidationUtils.notBlank(clientKeyFile, "The clientKeyFile parameter of the REST client configuration must not be empty"); ValidationUtils.notBlank(clientCertificateFile, "The clientCertificateFile parameter of the REST client configuration must not be empty"); - ValidationUtils.notBlank(serverCertificateFile, "The serverCertificateFile parameter of the REST client configuration must not be empty"); ValidationUtils.isPositive(maxTotalConnections, "The maxTotalConnections parameter of the REST client configuration must not be empty"); ValidationUtils.isPositive(maxConnectionsPerRoute, "The maxConnectionsPerRoute parameter of the REST client configuration must not be empty"); ValidationUtils.isPositive(connectionTimeoutInSec, "The connectionTimeoutInSec parameter of the REST client configuration must not be empty"); diff --git a/src/main/java/com/swisscom/ais/itext7/client/rest/SignatureRestClientImpl.java b/src/main/java/com/swisscom/ais/itext7/client/rest/SignatureRestClientImpl.java index 1734903..0de953f 100644 --- a/src/main/java/com/swisscom/ais/itext7/client/rest/SignatureRestClientImpl.java +++ b/src/main/java/com/swisscom/ais/itext7/client/rest/SignatureRestClientImpl.java @@ -99,7 +99,9 @@ public SignatureRestClientImpl withConfiguration(RestClientConfiguration config) try { SSLContextBuilder sslContextBuilder = SSLContexts.custom() .loadKeyMaterial(produceTheKeyStore(config), keyToCharArray(config.getClientKeyPassword()), producePrivateKeyStrategy()); - sslContextBuilder.loadTrustMaterial(produceTheTrustStore(config), null); + if (StringUtils.isNotBlank(config.getServerCertificateFile())) { + sslContextBuilder.loadTrustMaterial(produceTheTrustStore(config.getServerCertificateFile()), null); + } sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build()); } catch (Exception e) { throw new AisClientException("Failed to configure the TLS/SSL connection factory for the AIS client", e); @@ -231,8 +233,8 @@ private KeyStore produceTheKeyStore(RestClientConfiguration config) { } } - private KeyStore produceTheTrustStore(RestClientConfiguration config) { - try (FileInputStream is = new FileInputStream(config.getServerCertificateFile())) { + private KeyStore produceTheTrustStore(String serverCertificateFile) { + try (FileInputStream is = new FileInputStream(serverCertificateFile)) { CertificateFactory fact = CertificateFactory.getInstance("X.509"); X509Certificate certificate = (X509Certificate) fact.generateCertificate(is); diff --git a/src/main/resources/cli/sign-pdf-help.properties b/src/main/resources/cli/sign-pdf-help.properties index b8b2fc2..91a2c94 100644 --- a/src/main/resources/cli/sign-pdf-help.properties +++ b/src/main/resources/cli/sign-pdf-help.properties @@ -6,7 +6,7 @@ license.file=${ITEXT_LICENSE_FILE_PATH} server.rest.signUrl=https://ais.swisscom.com/AIS-Server/rs/v1.0/sign # The AIS server REST URL for sending the Signature status poll requests (Pending requests) server.rest.pendingUrl=https://ais.swisscom.com/AIS-Server/rs/v1.0/pending -# The AIS server trusted CA certificate file +# The AIS server trusted CA certificate file. The configuration parameter is optional and in case it is omitted the CA must be a trusted one. server.cert.file=/home/user/ais-server.crt # -- # The client's private key file (corresponding to the public key attached to the client's certificate)