001/* 
002 * Copyright (C) 2016 Hobrasoft s.r.o.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Affero General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Affero General Public License for more details.
013 *
014 * You should have received a copy of the GNU Affero General Public License
015 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
016 */
017package cz.hobrasoft.pdfmu.operation.signature;
018
019import com.itextpdf.text.pdf.security.TSAClient;
020import com.itextpdf.text.pdf.security.TSAClientBouncyCastle;
021import static cz.hobrasoft.pdfmu.error.ErrorType.SIGNATURE_ADD_TSA_INVALID_URL;
022import cz.hobrasoft.pdfmu.operation.OperationException;
023import cz.hobrasoft.pdfmu.operation.args.ArgsConfiguration;
024import cz.hobrasoft.pdfmu.operation.args.PasswordArgs;
025import java.util.logging.Logger;
026import net.sourceforge.argparse4j.inf.ArgumentGroup;
027import net.sourceforge.argparse4j.inf.ArgumentParser;
028import net.sourceforge.argparse4j.inf.Namespace;
029import org.apache.commons.validator.routines.UrlValidator;
030
031/**
032 * Parameters of timestamping process.
033 *
034 * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a>
035 */
036public class TimestampParameters implements ArgsConfiguration {
037
038    private static final Logger LOGGER = Logger.getLogger(TimestampParameters.class.getName());
039
040    /**
041     * Timestamp authority URL.
042     */
043    public String url;
044
045    /**
046     * Timestamp authority login username.
047     */
048    public String username;
049
050    private final PasswordArgs passwordArgs = new PasswordArgs("TSA password");
051
052    private final KeystoreParameters sslKeystore = new KeystoreParameters(SslKeystore.PRIVATE.getName());
053    private final KeystoreParameters sslTruststore = new KeystoreParameters(SslKeystore.TRUSTSTORE.getName());
054
055    @Override
056    public void addArguments(ArgumentParser parser) {
057        ArgumentGroup group = parser.addArgumentGroup("timestamp");
058        // TODO: Add description
059
060        group.addArgument("--tsa-url")
061                .help("timestamp authority URL (set to enable timestamp)")
062                .type(String.class);
063
064        group.addArgument("--tsa-username")
065                .help("timestamp authority username (set to enable TSA authorization)")
066                .type(String.class);
067
068        passwordArgs.passwordArgument = group.addArgument("--tsa-password")
069                .help("timestamp authority password (default: <none>)");
070        passwordArgs.environmentVariableArgument = group.addArgument("--tsa-password-envvar")
071                .help("timestamp authority password environment variable")
072                .setDefault("PDFMU_TSA_PASSWORD");
073        passwordArgs.finalizeArguments();
074
075        sslKeystore.fileArgument = group.addArgument("--ssl-keystore")
076                .help("The keystore file that contains the private keys used for SSL authorization. Must be protected by a non-empty password.");
077        sslKeystore.typeArgument = group.addArgument("--ssl-keystore-type")
078                .help("SSL KeyStore type")
079                .choices(new String[]{"jks", "jceks", "pkcs12"});
080        sslKeystore.passwordArgs.passwordArgument = group.addArgument("--ssl-keystore-password")
081                .help("SSL KeyStore password (default: <none>)");
082        sslKeystore.passwordArgs.environmentVariableArgument = group.addArgument("--ssl-keystore-password-envvar")
083                .help("SSL KeyStore password environment variable")
084                .setDefault("PDFMU_SSL_KEYSTORE_PASSWORD");
085        sslKeystore.finalizeArguments();
086
087        sslTruststore.fileArgument = group.addArgument("--ssl-truststore")
088                .help("The keystore file that contains the certificates of the trusted certificate authorities");
089        // TODO: Add support for type "pkcs12"
090        sslTruststore.typeArgument = group.addArgument("--ssl-truststore-type")
091                .help("SSL TrustStore type")
092                .choices(new String[]{"jks", "jceks"});
093        sslTruststore.passwordArgs.passwordArgument = group.addArgument("--ssl-truststore-password")
094                .help("SSL TrustStore password (default: <none>)");
095        sslTruststore.passwordArgs.environmentVariableArgument = group.addArgument("--ssl-truststore-password-envvar")
096                .help("SSL TrustStore password environment variable")
097                .setDefault("PDFMU_SSL_TRUSTSTORE_PASSWORD");
098        sslTruststore.finalizeArguments();
099    }
100
101    @Override
102    public void setFromNamespace(Namespace namespace) throws OperationException {
103        url = namespace.get("tsa_url");
104
105        if (url != null) {
106            UrlValidator urlValidator = new UrlValidator();
107            if (!urlValidator.isValid(url)) {
108                throw new OperationException(SIGNATURE_ADD_TSA_INVALID_URL);
109            }
110        }
111
112        username = namespace.getString("tsa_username");
113
114        passwordArgs.setFromNamespace(namespace);
115
116        sslKeystore.setFromNamespace(namespace);
117        if (sslKeystore.file != null) {
118            String password = sslKeystore.getPassword();
119            if (password == null || password.isEmpty()) {
120                LOGGER.warning("SSL KeyStore: Location has been set but password has not. Only KeyStores protected by a non-empty password are supported.");
121            }
122        }
123        sslKeystore.setSystemProperties(SslKeystore.PRIVATE);
124
125        sslTruststore.setFromNamespace(namespace);
126        sslTruststore.setSystemProperties(SslKeystore.TRUSTSTORE);
127    }
128
129    /**
130     * Returns the {@link TSAClient} that corresponds to these parameters.
131     *
132     * @return null if the timestamp authority has not been configured
133     */
134    public TSAClient getTSAClient() {
135        if (url == null) {
136            return null;
137        }
138        LOGGER.info("TSA URL has been set. Will attempt to attach a timestamp to the signature.");
139        String password = getPassword();
140        if (username != null && password == null) {
141            LOGGER.warning("TSA username has been set but password has not.");
142        }
143        if (password != null && username == null) {
144            LOGGER.warning("TSA password has been set but username has not.");
145        }
146        return new TSAClientBouncyCastle(url, username, password);
147    }
148
149    private String getPassword() {
150        assert passwordArgs != null;
151        return passwordArgs.getPassword();
152    }
153
154}