![JAR search and dependency download from the Maven repository](/logo.png)
es.ree.eemws.kit.cmd.trustserver.Main Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eemws-kit Show documentation
Show all versions of eemws-kit Show documentation
Client implementation of IEC 62325-504 technical specification. eemws-kit includes command line utilities to invoke the eem web services, as well as several GUI applications (browser, editor, ...)
The newest version!
/*
* Copyright 2024 Redeia.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3 of the license.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTIBIILTY or FITNESS FOR A PARTICULAR PURPOSE. See GNU Lesser General
* Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* http://www.gnu.org/licenses/.
*
* Any redistribution and/or modification of this program has to make
* reference to Redeia as the copyright owner of the program.
*/
package es.ree.eemws.kit.cmd.trustserver;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import es.ree.eemws.client.get.GetMessage;
import es.ree.eemws.client.list.ListMessages;
import es.ree.eemws.core.utils.config.ConfigException;
import es.ree.eemws.core.utils.operations.get.GetOperationException;
import es.ree.eemws.core.utils.operations.list.ListOperationException;
import es.ree.eemws.core.utils.security.X509Util;
import es.ree.eemws.kit.cmd.MessageCatalog;
import es.ree.eemws.kit.cmd.ParentMain;
import jakarta.xml.ws.WebServiceException;
/**
* Adds the given server to the list of trusted servers.
*
* @author Redeia.
* @version 2.1 01/01/2024
*/
public final class Main extends ParentMain {
/** Sets text for parameter url
. */
private static final String PARAMETER_URL = MessageCatalog.PARAMETER_URL.getMessage();
/** Log messages. */
private static final Logger LOGGER = Logger.getLogger("trustserver"); //$NON-NLS-1$
/** Default https port. */
private static final int DEFAULT_HTTPS_PORT = 443;
/** Https protocol string. */
private static final String HTTPS_PROTOCOL = "https"; //$NON-NLS-1$
/** TSL protocol string. */
private static final String TLS_PROTOCOL = "TLS"; //$NON-NLS-1$
/** SO_TIMEOUT value. */
private static final int SO_TIMEOUT = 10000;
/** Java system property to get the trust store password. */
private static final String LOCAL_TRUST_STORE_PASSWORD_KEY = "javax.net.ssl.trustStorePassword"; //$NON-NLS-1$
/** Java system property to get the trust store file. */
private static final String LOCAL_TRUST_STORE_FILE_KEY = "javax.net.ssl.trustStore"; //$NON-NLS-1$
/** Local trust store. */
private static KeyStore localTrustStore;
/**
* A list of added certificates avoid trying to add twice the same certificate.
*/
private static List addedCertificates = new ArrayList<>();
/**
* Main. Opens a connection to the remote server and adds its certificates to
* the local trust store.
*
* @param args command line arguments.
*/
public static void main(final String[] args) {
var urlEndPoint = ""; //$NON-NLS-1$
try {
/* Reads command line parameters, store its values. */
List arguments = new ArrayList<>(Arrays.asList(args));
/* If the list has duplicates must stop the execution. */
var dup = findDuplicates(arguments, PARAMETER_URL);
if (dup != null) {
throw new IllegalArgumentException(MessageCatalog.PARAMETER_REPEATED.getMessage(dup));
}
urlEndPoint = readParameter(arguments, PARAMETER_URL);
if (!arguments.isEmpty()) {
throw new IllegalArgumentException(MessageCatalog.UNKNOWN_PARAMETERS.getMessage(arguments.toString()));
}
urlEndPoint = setConfig(urlEndPoint);
var url = new URL(urlEndPoint);
if (!HTTPS_PROTOCOL.equals(url.getProtocol())) {
throw new IllegalArgumentException(TrustServerMessageCatalog.TRUSTSERVER_ONLY_HTTPS.getMessage());
}
var localTrustFile = System.getProperty(LOCAL_TRUST_STORE_FILE_KEY);
if (localTrustFile == null) {
throw new IllegalArgumentException(TrustServerMessageCatalog.TRUSTSERVER_NO_TRUST_STORE.getMessage());
}
var passwd = System.getProperty(LOCAL_TRUST_STORE_PASSWORD_KEY);
if (passwd == null) {
passwd = ""; //$NON-NLS-1$
}
loadLocalTrustStore(localTrustFile, passwd.toCharArray());
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_TRUST_SIZE.getMessage(localTrustStore.size()));
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_GETTING_SERVER_CERTICATES.getMessage());
}
var certAdded = addServerCerts(urlEndPoint);
if (certAdded) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_RERUN_COMMAND.getMessage());
}
} else {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_GETTING_SIGNATURE_CERTICATES.getMessage());
}
addSignatureCert(urlEndPoint);
}
storeLocalTrustStore(localTrustFile, passwd.toCharArray());
} catch (MalformedURLException e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe(MessageCatalog.INVALID_URL.getMessage(urlEndPoint));
}
} catch (IOException e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe(e.getMessage());
}
} catch (ConfigException e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe(MessageCatalog.INVALID_CONFIGURATION.getMessage(e.getMessage()));
}
/* Shows stack trace only for debug. Don't bother the user with this details. */
LOGGER.log(Level.FINE, MessageCatalog.INVALID_CONFIGURATION.getMessage(e.getMessage()), e);
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, TrustServerMessageCatalog.TRUSTSERVER_BAD_KEYSTORE.getMessage(e.getMessage()));
}
} catch (IllegalArgumentException e) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(e.getMessage());
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_USAGE.getMessage(PARAMETER_URL));
}
}
}
/**
* Loads the local trust store.
*
* @param filePath Full trust store file path.
* @param passwd Trust store password.
* @throws IOException if it's no possible to open the configured trust store.
*/
private static void loadLocalTrustStore(final String filePath, final char[] passwd) throws IOException {
var localCacert = new File(filePath);
try (InputStream in = new FileInputStream(localCacert)) {
localTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
localTrustStore.load(in, passwd);
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
throw new IOException(TrustServerMessageCatalog.TRUSTSERVER_UNABLE_TO_LOAD.getMessage(), e);
}
}
/**
* Saves (stores) the local trust store.
*
* @param filePath Full trust store file path.
* @param passwd Trust store password.
* @throws IOException if it's no possible to open the configured trust store.
*/
private static void storeLocalTrustStore(final String filePath, final char[] passwd) throws IOException {
var localCacert = new File(filePath);
try (OutputStream out = new FileOutputStream(localCacert)) {
localTrustStore.store(out, passwd);
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException ex) {
throw new IOException(TrustServerMessageCatalog.TRUSTSERVER_UNABLE_TO_SAVE.getMessage(), ex);
}
}
/**
* Adds the certificate used for signature to the current trust store. This
* method perform a list + get process to get a signed message.
*
* @param urlEndPoint Web service url.
* @throws KeyStoreException If the method is unable to add the retrieved
* certificate.
* @throws MalformedURLException If the current url is incorrecto (impossible at
* this point)
*/
private static void addSignatureCert(final String urlEndPoint) throws KeyStoreException, MalformedURLException {
var url = new URL(urlEndPoint);
GetMessage get = null;
try {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_GETTING_MSG_LIST.getMessage());
}
var list = new ListMessages();
list.setEndPoint(url);
var listMsg = list.list(0L);
if (listMsg.isEmpty()) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.warning(TrustServerMessageCatalog.TRUSTSERVER_NO_MESSAGES_TO_LIST.getMessage());
}
} else {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_MSG_LIST.getMessage(listMsg.size()));
}
get = new GetMessage();
get.setEndPoint(url);
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_MSG_GET
.getMessage(String.valueOf(listMsg.get(0).getCode().longValue())));
}
get.get(listMsg.get(0).getCode().longValue());
}
} catch (ListOperationException ex) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE,
TrustServerMessageCatalog.TRUSTSERVER_UNABLE_TO_CONNECT_WITH_SERVER.getMessage(), ex);
}
} catch (GetOperationException ex) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, TrustServerMessageCatalog.TRUSTSERVER_UNABLE_TO_CONNECT_WITH_SERVER.getMessage(),
ex);
}
} catch (WebServiceException ex) {
LOGGER.warning(
TrustServerMessageCatalog.TRUSTSERVER_UNABLE_TO_CONNECT_WITH_SERVER.getMessage(ex.getMessage()));
LOGGER.log(Level.FINE,
TrustServerMessageCatalog.TRUSTSERVER_UNABLE_TO_CONNECT_WITH_SERVER.getMessage(ex.getMessage()),
ex);
} finally {
if (get != null) {
var md = get.getMessageMetaData();
if (md != null) {
var certificate = md.getSignatureCertificate();
if (certificate != null) {
addCertificate(url.getHost() + " (" + certificate.getIssuerX500Principal().getName() + ") (" //$NON-NLS-1$ //$NON-NLS-2$
+ TrustServerMessageCatalog.TRUSTSERVER_SIGNATURE.getMessage() + ") ", certificate); //$NON-NLS-1$
}
}
}
}
}
/**
* Adds the all the certificates in the server's certificate chain to the local
* trust store.
*
* @param urlEndPoint Url to connect with in order to retrieve the certificate
* chain.
* @return true
if a least one certificate is added.
* @throws KeyStoreException If the method is unable to add the retrieved
* certificate.
* @throws NoSuchAlgorithmException If the system is unable to deal with TSL
* protocol (Is this possible by the way?)
* @throws KeyManagementException If it is not possible to initialize the SSL
* context with the local trust store.
* @throws IOException If server didn't send certificate chain.
*/
private static boolean addServerCerts(final String urlEndPoint)
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException, IOException {
var certsAdded = false;
var url = new URL(urlEndPoint);
var host = url.getHost();
var port = url.getPort();
if (port == -1) {
port = DEFAULT_HTTPS_PORT;
}
var tm = new ProxyTrustManager();
var context = SSLContext.getInstance(TLS_PROTOCOL);
context.init(null, new TrustManager[] { tm }, null);
var factory = context.getSocketFactory();
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_OPENING_CONNECTION.getMessage(urlEndPoint));
}
try (var socket = (SSLSocket) factory.createSocket(host, port)) {
socket.setSoTimeout(SO_TIMEOUT);
socket.startHandshake();
} catch (UnknownHostException uhe) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe(TrustServerMessageCatalog.TRUSTSERVER_UNKNOW_HOST.getMessage(urlEndPoint));
}
} catch (ConnectException ce) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.severe(TrustServerMessageCatalog.TRUSTSERVER_CANNOT_CONNECT.getMessage(urlEndPoint));
}
} catch (IOException ioe) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, TrustServerMessageCatalog.TRUSTSERVER_CANNOT_CONNECT.getMessage(urlEndPoint),
ioe);
}
} finally {
var chain = tm.getServerCertificateChain();
if ((chain == null) || (chain.length <= 0)) {
throw new IOException(TrustServerMessageCatalog.TRUSTSERVER_NO_CERT_CHAIN.getMessage());
}
for (X509Certificate certificate : chain) {
var subjectDN = certificate.getIssuerX500Principal().getName();
if (addCertificate(host + " (" + subjectDN + ") ", certificate)) { //$NON-NLS-1$ //$NON-NLS-2$
certsAdded = true;
}
}
}
return certsAdded;
}
/**
* Adds a certificate into the local trust store. Note that only valid
* certificates are added.
*
* @param alias Alias Alias used to store the certificate.
* @param certificate Certificate to be added
* @return true
if the certificate is added to the trust store.
* false
otherwise.
* @throws KeyStoreException If the method cannot modify the local trust store.
*/
private static boolean addCertificate(final String alias, final X509Certificate certificate)
throws KeyStoreException {
var certAdded = false;
var subjectDN = certificate.getIssuerX500Principal().getName();
var issuer = certificate.getIssuerX500Principal().getName();
try {
var validity = X509Util.checkCertificate(certificate);
if (!validity.isValid()) {
throw validity.getCause();
}
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_SKIPPING_CERTIFICATE.getMessage(subjectDN, issuer));
}
} catch (CertificateExpiredException e) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_SKIPPING_EXPIRED.getMessage(subjectDN, issuer));
}
} catch (CertificateNotYetValidException e) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_SKIPPING_NOT_YET_VALID.getMessage(subjectDN, issuer));
}
} catch (CertificateException e1) {
if (addedCertificates.contains(certificate)) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_SKIPPING_ALREADY_ADDED.getMessage(subjectDN));
}
} else {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info(TrustServerMessageCatalog.TRUSTSERVER_ADDING_CERTIFICATE.getMessage(subjectDN, issuer));
}
addedCertificates.add(certificate);
certAdded = true;
localTrustStore.setCertificateEntry(alias, certificate);
}
}
return certAdded;
}
/**
* Implements a proxy trust manager that will store the server's certificate
* chain.
*/
private static class ProxyTrustManager implements X509TrustManager {
/** Default trust manager (real trust manager). */
private final X509TrustManager defaultTrustManager;
/** Server certificates chain. */
private X509Certificate[] serverCertificateChain;
/**
* Creates a new ProxyTrustManager using the given keystore as trusted server's
* CAs.
*
* @throws NoSuchAlgorithmException If it's not possible to get a
* TrustManagerFactory instance for the default
* algorithm.
* @throws KeyStoreException if it's not possible to inicialize the trust
* manager with the given key store.
*/
ProxyTrustManager() throws NoSuchAlgorithmException, KeyStoreException {
var tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(localTrustStore);
defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
}
/**
* Returns the retrieved server's certificate chain.
*
* @return An array with the retrieved certificate chain.
*/
public X509Certificate[] getServerCertificateChain() {
return serverCertificateChain;
}
/**
* Returns the accepted issuers.
*/
@Override
public X509Certificate[] getAcceptedIssuers() {
return defaultTrustManager.getAcceptedIssuers();
}
/**
* Not supported. There is no need to implement this method, but it's necessary
* according to the X509TrustManager interface.
*
* @throws UnsupportedOperationException
*/
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType)
throws CertificateException {
throw new UnsupportedOperationException("Not supported"); //$NON-NLS-1$
}
/**
* Stores the server's certificate chain. This method nevers returns
* CertificateException: All connections are trusted.
*
* @param chain Server's certificate chain
* @param authType Authentification type.
* @throws CertificateException Never, all connections are trusted.
*/
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType)
throws CertificateException {
serverCertificateChain = chain;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy