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}