View Javadoc
1   /* 
2    * Copyright (C) 2016 Hobrasoft s.r.o.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  package cz.hobrasoft.pdfmu.operation.signature;
18  
19  import static cz.hobrasoft.pdfmu.error.ErrorType.SIGNATURE_ADD_KEYSTORE_FILE_CLOSE;
20  import static cz.hobrasoft.pdfmu.error.ErrorType.SIGNATURE_ADD_KEYSTORE_FILE_NOT_SPECIFIED;
21  import static cz.hobrasoft.pdfmu.error.ErrorType.SIGNATURE_ADD_KEYSTORE_FILE_OPEN;
22  import static cz.hobrasoft.pdfmu.error.ErrorType.SIGNATURE_ADD_KEYSTORE_LOAD;
23  import static cz.hobrasoft.pdfmu.error.ErrorType.SIGNATURE_ADD_KEYSTORE_TYPE_UNSUPPORTED;
24  import cz.hobrasoft.pdfmu.operation.OperationException;
25  import cz.hobrasoft.pdfmu.operation.args.ArgsConfiguration;
26  import cz.hobrasoft.pdfmu.operation.args.PasswordArgs;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.FileNotFoundException;
30  import java.io.IOException;
31  import java.security.KeyStore;
32  import java.security.KeyStoreException;
33  import java.security.NoSuchAlgorithmException;
34  import java.security.cert.CertificateException;
35  import java.util.AbstractMap.SimpleEntry;
36  import java.util.logging.Logger;
37  import net.sourceforge.argparse4j.impl.Arguments;
38  import net.sourceforge.argparse4j.inf.Argument;
39  import net.sourceforge.argparse4j.inf.ArgumentParser;
40  import net.sourceforge.argparse4j.inf.Namespace;
41  
42  /**
43   *
44   * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a>
45   */
46  class KeystoreParameters implements ArgsConfiguration {
47  
48      public File file = null;
49      public String type = null;
50  
51      // TODO?: Replace with Console
52      private static final Logger logger = Logger.getLogger(KeystoreParameters.class.getName());
53  
54      public KeystoreParameters(String title) {
55          passwordArgs = new PasswordArgs(String.format("%s password", title));
56      }
57  
58      public Argument typeArgument;
59      public Argument fileArgument;
60      public PasswordArgs passwordArgs;
61  
62      @Deprecated
63      @Override
64      public void addArguments(ArgumentParser parser) {
65          finalizeArguments();
66      }
67  
68      public void finalizeArguments() {
69          assert typeArgument != null;
70          typeArgument.type(String.class);
71  
72          assert fileArgument != null;
73          fileArgument.type(Arguments.fileType());
74  
75          assert passwordArgs != null;
76          passwordArgs.finalizeArguments();
77      }
78  
79      @Override
80      public void setFromNamespace(Namespace namespace) {
81          file = namespace.get(fileArgument.getDest());
82          type = namespace.getString(typeArgument.getDest());
83  
84          passwordArgs.setFromNamespace(namespace);
85      }
86  
87      private String getNonnullType() {
88          if (type == null) {
89              // TODO: Guess type from `ksFile` file extension
90              logger.info("Keystore type not specified. Using the default type.");
91              return KeyStore.getDefaultType();
92          }
93          return type;
94      }
95  
96      public String getPassword() {
97          return passwordArgs.getPassword();
98      }
99  
100     private String getNonnullPassword() {
101         String password = getPassword();
102         if (password == null) {
103             logger.info("Keystore password not set. Using empty password.");
104             return "";
105         }
106         return password;
107     }
108 
109     public KeyStore loadKeystore() throws OperationException {
110         String type = getNonnullType();
111         logger.info(String.format("Keystore type: %s", type));
112         // digitalsignatures20130304.pdf : Code sample 2.2
113         // Initialize keystore
114         KeyStore ks;
115         try {
116             ks = KeyStore.getInstance(type);
117         } catch (KeyStoreException ex) {
118             throw new OperationException(SIGNATURE_ADD_KEYSTORE_TYPE_UNSUPPORTED, ex,
119                     new SimpleEntry<String, Object>("type", type));
120         }
121         logger.info(String.format("Keystore security provider: %s", ks.getProvider().getName()));
122         switch (type) {
123             case "Windows-MY":
124                 loadWindowsKeystore(ks);
125                 break;
126             default:
127                 loadFileKeystore(ks, type);
128         }
129         return ks;
130     }
131 
132     private void loadFileKeystore(KeyStore ks, String type) throws OperationException {
133         if (file == null) {
134             throw new OperationException(SIGNATURE_ADD_KEYSTORE_FILE_NOT_SPECIFIED,
135                     new SimpleEntry<String, Object>("type", type));
136         }
137         logger.info(String.format("Keystore file: %s", file));
138         // ksIs
139         FileInputStream ksIs;
140         try {
141             ksIs = new FileInputStream(file);
142         } catch (FileNotFoundException ex) {
143             throw new OperationException(SIGNATURE_ADD_KEYSTORE_FILE_OPEN, ex,
144                     new SimpleEntry<String, Object>("file", file));
145         }
146         char[] password = getNonnullPassword().toCharArray();
147         try {
148             ks.load(ksIs, password);
149         } catch (IOException ex) {
150             // Incorrect keystore password? Incorrect keystore type? Corrupted keystore file?
151             throw new OperationException(SIGNATURE_ADD_KEYSTORE_LOAD, ex,
152                     new SimpleEntry<String, Object>("type", type),
153                     new SimpleEntry<String, Object>("file", file));
154         } catch (NoSuchAlgorithmException | CertificateException ex) {
155             throw new OperationException(SIGNATURE_ADD_KEYSTORE_LOAD, ex,
156                     new SimpleEntry<String, Object>("type", type),
157                     new SimpleEntry<String, Object>("file", file));
158         } finally {
159             try {
160                 ksIs.close();
161             } catch (IOException ex) {
162                 throw new OperationException(SIGNATURE_ADD_KEYSTORE_FILE_CLOSE, ex,
163                         new SimpleEntry<String, Object>("file", file));
164             }
165         }
166     }
167 
168     private void loadWindowsKeystore(KeyStore ks) throws OperationException {
169         assert "Windows-MY".equals(type);
170         try {
171             ks.load(null, null);
172         } catch (IOException | NoSuchAlgorithmException | CertificateException ex) {
173             throw new OperationException(SIGNATURE_ADD_KEYSTORE_LOAD, ex,
174                     new SimpleEntry<String, Object>("type", type));
175         }
176     }
177 
178     public void setSystemProperties(SslKeystore sslKeystore) throws OperationException {
179         sslKeystore.setSystemProperties(file, type, getPassword());
180     }
181 }