View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.common.crypto.ssl;
21  
22  import org.joda.time.DateTime;
23  import org.waarp.common.exception.CryptoException;
24  import org.waarp.common.file.FileUtils;
25  import org.waarp.common.logging.WaarpLogger;
26  import org.waarp.common.logging.WaarpLoggerFactory;
27  
28  import javax.net.ssl.KeyManagerFactory;
29  import javax.net.ssl.TrustManagerFactory;
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.FileNotFoundException;
33  import java.io.FileOutputStream;
34  import java.io.IOException;
35  import java.security.Key;
36  import java.security.KeyStore;
37  import java.security.KeyStoreException;
38  import java.security.NoSuchAlgorithmException;
39  import java.security.UnrecoverableKeyException;
40  import java.security.cert.Certificate;
41  import java.security.cert.CertificateException;
42  import java.security.cert.CertificateFactory;
43  import java.security.cert.X509Certificate;
44  import java.util.Date;
45  import java.util.Enumeration;
46  
47  import static org.waarp.common.digest.WaarpBC.*;
48  
49  /**
50   * SecureKeyStore for SLL
51   */
52  public class WaarpSecureKeyStore {
53    private static final String CANNOT_SAVE_TO_FILE_KEY_STORE_INSTANCE =
54        "Cannot save to file KeyStore Instance";
55  
56    private static final String CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE =
57        "Cannot create KeyManagerFactory Instance";
58  
59    private static final String CANNOT_CREATE_KEY_STORE_INSTANCE =
60        "Cannot create KeyStore Instance";
61  
62    /**
63     * Internal Logger
64     */
65    private static final WaarpLogger logger =
66        WaarpLoggerFactory.getLogger(WaarpSecureKeyStore.class);
67    private static final String CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE =
68        "Cannot create TrustManagerFactory Instance";
69    private static final String CANNOT_CREATE_KEY_TRUST_STORE_INSTANCE =
70        "Cannot create keyTrustStore Instance";
71    private static final String CANNOT_SAVE_TO_FILE_KEY_TRUST_STORE_INSTANCE =
72        "Cannot save to file keyTrustStore Instance";
73  
74    static {
75      initializedTlsContext();
76    }
77  
78    private String keyStoreFilename;
79    private KeyStore keyStore;
80    private KeyManagerFactory keyManagerFactory;
81    private String keyStorePasswd;
82    private String keyPassword;
83    private WaarpSecureTrustManagerFactory secureTrustManagerFactory;
84    private KeyStore keyTrustStore;
85    private String trustStorePasswd;
86  
87    /**
88     * Initialize empty KeyStore. No TrustStore is internally created.
89     *
90     * @param keyStorePasswd
91     * @param keyPassword
92     *
93     * @throws CryptoException
94     */
95    public WaarpSecureKeyStore(final String keyStorePasswd,
96                               final String keyPassword) throws CryptoException {
97      this.keyStorePasswd = keyStorePasswd;
98      this.keyPassword = keyPassword;
99      try {
100       keyStore = KeyStore.getInstance("JKS");
101     } catch (final KeyStoreException e) {
102       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
103       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
104     }
105     try {
106       // Empty keyStore created so null for the InputStream
107       keyStore.load(null, getKeyStorePassword());
108     } catch (final NoSuchAlgorithmException e) {
109       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
110       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
111     } catch (final CertificateException e) {
112       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
113       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
114     } catch (final FileNotFoundException e) {
115       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
116       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
117     } catch (final IOException e) {
118       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
119       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
120     }
121     initKeyManagerFactory();
122   }
123 
124   /**
125    * Initialize the SecureKeyStore with no TrustStore from file
126    *
127    * @param keyStoreFilename
128    * @param keyStorePasswd
129    * @param keyPassword
130    *
131    * @throws CryptoException
132    */
133   public WaarpSecureKeyStore(final String keyStoreFilename,
134                              final String keyStorePasswd,
135                              final String keyPassword) throws CryptoException {
136     initKeyStore(keyStoreFilename, keyStorePasswd, keyPassword);
137   }
138 
139   /**
140    * Initialize the SecureKeyStore and TrustStore from files
141    *
142    * @param keyStoreFilename
143    * @param keyStorePasswd
144    * @param keyPassword
145    * @param trustStoreFilename if Null, no TrustKeyStore will be
146    *     created
147    * @param trustStorePasswd
148    * @param needClientAuthent True if the TrustStore is also used for
149    *     Client
150    *     Authentication
151    *
152    * @throws CryptoException
153    */
154   public WaarpSecureKeyStore(final String keyStoreFilename,
155                              final String keyStorePasswd,
156                              final String keyPassword,
157                              final String trustStoreFilename,
158                              final String trustStorePasswd,
159                              final boolean needClientAuthent)
160       throws CryptoException {
161     // Create the KeyStore
162     initKeyStore(keyStoreFilename, keyStorePasswd, keyPassword);
163     // Now create the TrustKeyStore
164     if (trustStoreFilename != null) {
165       initTrustStore(trustStoreFilename, trustStorePasswd, needClientAuthent);
166     } else {
167       initEmptyTrustStore();
168     }
169   }
170 
171   /**
172    * Initialize the SecureKeyStore with no TrustStore from file
173    *
174    * @param keystoreFilename
175    * @param keystorePasswd
176    * @param keyPasswordNew
177    *
178    * @throws CryptoException
179    */
180   public final void initKeyStore(final String keystoreFilename,
181                                  final String keystorePasswd,
182                                  final String keyPasswordNew)
183       throws CryptoException {
184     keyStoreFilename = keystoreFilename;
185     keyStorePasswd = keystorePasswd;
186     keyPassword = keyPasswordNew;
187     // First keyStore itself
188     try {
189       keyStore = KeyStore.getInstance("JKS");
190     } catch (final KeyStoreException e) {
191       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
192       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
193     }
194     FileInputStream inputStream = null;
195     try {
196       final File temp = new File(keystoreFilename).getAbsoluteFile();
197       inputStream = new FileInputStream(temp);
198       keyStore.load(inputStream, getKeyStorePassword());
199     } catch (final NoSuchAlgorithmException e) {
200       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
201       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
202     } catch (final CertificateException e) {
203       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
204       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
205     } catch (final FileNotFoundException e) {
206       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
207       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
208     } catch (final IOException e) {
209       logger.error(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
210       throw new CryptoException(CANNOT_CREATE_KEY_STORE_INSTANCE, e);
211     } finally {
212       FileUtils.close(inputStream);
213     }
214     checkExpiryDate(keyStore);
215     initKeyManagerFactory();
216   }
217 
218   /**
219    * Init KeyManagerFactory
220    *
221    * @throws CryptoException
222    */
223   final void initKeyManagerFactory() throws CryptoException {
224     try {
225       keyManagerFactory = KeyManagerFactory.getInstance(
226           KeyManagerFactory.getDefaultAlgorithm());
227     } catch (final NoSuchAlgorithmException e) {
228       logger.error(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
229       throw new CryptoException(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
230     }
231     try {
232       keyManagerFactory.init(keyStore, getCertificatePassword());
233     } catch (final UnrecoverableKeyException e) {
234       logger.error(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
235       throw new CryptoException(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
236     } catch (final KeyStoreException e) {
237       logger.error(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
238       throw new CryptoException(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
239     } catch (final NoSuchAlgorithmException e) {
240       logger.error(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
241       throw new CryptoException(CANNOT_CREATE_KEY_MANAGER_FACTORY_INSTANCE, e);
242     }
243   }
244 
245   /**
246    * Delete a Key from the KeyStore based on its alias
247    *
248    * @param alias
249    *
250    * @return True if entry is deleted
251    */
252   public final boolean deleteKeyFromKeyStore(final String alias) {
253     try {
254       keyStore.deleteEntry(alias);
255     } catch (final KeyStoreException e) {
256       logger.error("Cannot delete Key from KeyStore Instance", e);
257       return false;
258     }
259     return true;
260   }
261 
262   /**
263    * Add a Key and its certificates into the KeyStore based on its alias
264    *
265    * @param alias
266    * @param key
267    * @param chain
268    *
269    * @return True if entry is added
270    */
271   public final boolean setKeytoKeyStore(final String alias, final Key key,
272                                         final Certificate[] chain) {
273     try {
274       keyStore.setKeyEntry(alias, key, getCertificatePassword(), chain);
275     } catch (final KeyStoreException e) {
276       logger.error("Cannot add Key and Certificates to KeyStore Instance", e);
277       return false;
278     }
279     return true;
280   }
281 
282   /**
283    * Save a KeyStore to a file
284    *
285    * @param filename
286    *
287    * @return True if keyStore is saved to file
288    */
289   public final boolean saveKeyStore(final String filename) {
290     FileOutputStream fos = null;
291     try {
292       fos = new FileOutputStream(filename);
293       try {
294         keyStore.store(fos, getKeyStorePassword());
295       } catch (final KeyStoreException e) {
296         logger.error(CANNOT_SAVE_TO_FILE_KEY_STORE_INSTANCE, e);
297         return false;
298       } catch (final NoSuchAlgorithmException e) {
299         logger.error(CANNOT_SAVE_TO_FILE_KEY_STORE_INSTANCE, e);
300         return false;
301       } catch (final CertificateException e) {
302         logger.error(CANNOT_SAVE_TO_FILE_KEY_STORE_INSTANCE, e);
303         return false;
304       } catch (final IOException e) {
305         logger.error(CANNOT_SAVE_TO_FILE_KEY_STORE_INSTANCE, e);
306         return false;
307       }
308     } catch (final FileNotFoundException e) {
309       logger.error(CANNOT_SAVE_TO_FILE_KEY_STORE_INSTANCE, e);
310       return false;
311     } finally {
312       FileUtils.close(fos);
313     }
314     return true;
315   }
316 
317   /**
318    * Initialize the TrustStore from a filename and its password
319    *
320    * @param truststoreFilename
321    * @param truststorePasswd
322    * @param needClientAuthent True if the TrustStore is also to
323    *     authenticate
324    *     clients
325    *
326    * @throws CryptoException
327    */
328   public final void initTrustStore(final String truststoreFilename,
329                                    final String truststorePasswd,
330                                    final boolean needClientAuthent)
331       throws CryptoException {
332     trustStorePasswd = truststorePasswd;
333     try {
334       keyTrustStore = KeyStore.getInstance("JKS");
335     } catch (final KeyStoreException e) {
336       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e);
337       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
338                                 e);
339     }
340     FileInputStream inputStream = null;
341     try {
342       final File temp = new File(truststoreFilename).getAbsoluteFile();
343       inputStream = new FileInputStream(temp);
344       keyTrustStore.load(inputStream, getKeyTrustStorePassword());
345     } catch (final NoSuchAlgorithmException e) {
346       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e);
347       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
348                                 e);
349     } catch (final CertificateException e) {
350       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e);
351       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
352                                 e);
353     } catch (final FileNotFoundException e) {
354       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e);
355       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
356                                 e);
357     } catch (final IOException e) {
358       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e);
359       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
360                                 e);
361     } finally {
362       FileUtils.close(inputStream);
363     }
364     checkExpiryDate(keyTrustStore);
365     final TrustManagerFactory trustManagerFactory;
366     try {
367       trustManagerFactory = TrustManagerFactory.getInstance(
368           KeyManagerFactory.getDefaultAlgorithm());
369     } catch (final NoSuchAlgorithmException e1) {
370       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e1);
371       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
372                                 e1);
373     }
374     try {
375       trustManagerFactory.init(keyTrustStore);
376     } catch (final KeyStoreException e1) {
377       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e1);
378       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
379                                 e1);
380     }
381     try {
382       secureTrustManagerFactory =
383           new WaarpSecureTrustManagerFactory(trustManagerFactory,
384                                              needClientAuthent);
385     } catch (final CryptoException e) {
386       logger.error(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE, e);
387       throw new CryptoException(CANNOT_CREATE_TRUST_MANAGER_FACTORY_INSTANCE,
388                                 e);
389     }
390   }
391 
392   /**
393    * Initialize an empty TrustStore
394    *
395    * @return True if correctly initialized empty
396    */
397   public final boolean initEmptyTrustStore() {
398     trustStorePasswd = "secret";//NOSONAR
399     try {
400       keyTrustStore = KeyStore.getInstance("JKS");
401     } catch (final KeyStoreException e) {
402       logger.error(CANNOT_CREATE_KEY_TRUST_STORE_INSTANCE, e);
403       return false;
404     }
405     try {
406       // Empty keyTrustStore created so null for the InputStream
407       keyTrustStore.load(null, getKeyTrustStorePassword());
408     } catch (final NoSuchAlgorithmException e) {
409       logger.error(CANNOT_CREATE_KEY_TRUST_STORE_INSTANCE, e);
410       return false;
411     } catch (final CertificateException e) {
412       logger.error(CANNOT_CREATE_KEY_TRUST_STORE_INSTANCE, e);
413       return false;
414     } catch (final FileNotFoundException e) {
415       logger.error(CANNOT_CREATE_KEY_TRUST_STORE_INSTANCE, e);
416       return false;
417     } catch (final IOException e) {
418       logger.error(CANNOT_CREATE_KEY_TRUST_STORE_INSTANCE, e);
419       return false;
420     }
421     secureTrustManagerFactory = new WaarpSecureTrustManagerFactory();
422     return true;
423   }
424 
425   /**
426    * Delete a Key from the TrustStore based on its alias
427    *
428    * @param alias
429    *
430    * @return True if entry is deleted
431    */
432   public final boolean deleteKeyFromTrustStore(final String alias) {
433     try {
434       keyStore.deleteEntry(alias);
435     } catch (final KeyStoreException e) {
436       logger.error("Cannot delete Key from keyTrustStore Instance", e);
437       return false;
438     }
439     return true;
440   }
441 
442   /**
443    * Add a Certificate into the TrustStore based on its alias
444    *
445    * @param alias
446    * @param cert
447    *
448    * @return True if entry is added
449    */
450   public final boolean setKeytoTrustStore(final String alias,
451                                           final Certificate cert) {
452     try {
453       keyStore.setCertificateEntry(alias, cert);
454     } catch (final KeyStoreException e) {
455       logger.error("Cannot add Certificate to keyTrustStore Instance", e);
456       return false;
457     }
458     return true;
459   }
460 
461   /**
462    * Save the TrustStore to a file
463    *
464    * @param filename
465    *
466    * @return True if keyTrustStore is saved to file
467    */
468   public final boolean saveTrustStore(final String filename) {
469     FileOutputStream fos = null;
470     try {
471       fos = new FileOutputStream(filename);
472       try {
473         keyTrustStore.store(fos, getKeyTrustStorePassword());
474       } catch (final KeyStoreException e) {
475         logger.error(CANNOT_SAVE_TO_FILE_KEY_TRUST_STORE_INSTANCE, e);
476         return false;
477       } catch (final NoSuchAlgorithmException e) {
478         logger.error(CANNOT_SAVE_TO_FILE_KEY_TRUST_STORE_INSTANCE, e);
479         return false;
480       } catch (final CertificateException e) {
481         logger.error(CANNOT_SAVE_TO_FILE_KEY_TRUST_STORE_INSTANCE, e);
482         return false;
483       } catch (final IOException e) {
484         logger.error(CANNOT_SAVE_TO_FILE_KEY_TRUST_STORE_INSTANCE, e);
485         return false;
486       }
487     } catch (final FileNotFoundException e) {
488       logger.error(CANNOT_SAVE_TO_FILE_KEY_TRUST_STORE_INSTANCE, e);
489       return false;
490     } finally {
491       FileUtils.close(fos);
492     }
493     return true;
494   }
495 
496   /**
497    * Load a certificate from a filename
498    *
499    * @param filename
500    *
501    * @return the X509 Certificate from filename
502    *
503    * @throws CertificateException
504    * @throws FileNotFoundException
505    */
506   public static Certificate loadX509Certificate(final String filename)
507       throws CertificateException, FileNotFoundException {
508     final CertificateFactory cf = CertificateFactory.getInstance("X.509");
509     final FileInputStream in = new FileInputStream(filename);
510     try {
511       return cf.generateCertificate(in);
512     } finally {
513       FileUtils.close(in);
514     }
515   }
516 
517   /**
518    * @return the certificate Password
519    */
520   public final char[] getCertificatePassword() {
521     if (keyPassword != null) {
522       return keyPassword.toCharArray();
523     }
524     return "nopwd".toCharArray();
525   }
526 
527   /**
528    * @return the KeyStore Password
529    */
530   public final char[] getKeyStorePassword() {
531     if (keyStorePasswd != null) {
532       return keyStorePasswd.toCharArray();
533     }
534     return "nopwd".toCharArray();
535   }
536 
537   /**
538    * @return the KeyTrustStore Password
539    */
540   public final char[] getKeyTrustStorePassword() {
541     if (trustStorePasswd != null) {
542       return trustStorePasswd.toCharArray();
543     }
544     return "nopwd".toCharArray();
545   }
546 
547   /**
548    * @return the KeyStore Filename
549    */
550   public final String getKeyStoreFilename() {
551     return keyStoreFilename;
552   }
553 
554   /**
555    * @return the secureTrustManagerFactory
556    */
557   public final WaarpSecureTrustManagerFactory getSecureTrustManagerFactory() {
558     return secureTrustManagerFactory;
559   }
560 
561   /**
562    * @return the keyManagerFactory
563    */
564   public final KeyManagerFactory getKeyManagerFactory() {
565     return keyManagerFactory;
566   }
567 
568   /**
569    * @return the KeyStore
570    */
571   public final KeyStore getKeyStore() {
572     return keyStore;
573   }
574 
575   /**
576    * @return the Trust KeyStore
577    */
578   public final KeyStore getKeyTrustStore() {
579     return keyTrustStore;
580   }
581 
582   /**
583    * @param keystore
584    *
585    * @return True if all certificates are OK
586    */
587   public static boolean checkExpiryDate(final KeyStore keystore) {
588     final Enumeration<String> aliases;
589     try {
590       aliases = keystore.aliases();
591     } catch (final KeyStoreException e) {//NOSONAR
592       logger.warn("Cannot get Aliases: {}", e.getMessage());
593       return true;
594     }
595     Date expiryDate;
596     boolean valid = true;
597     for (; aliases.hasMoreElements(); ) {
598       final String alias = aliases.nextElement();
599       try {
600         expiryDate =
601             ((X509Certificate) keystore.getCertificate(alias)).getNotAfter();
602         final DateTime dateTime = new DateTime(expiryDate);
603         if (dateTime.isBeforeNow()) {
604           logger.error("Certificate {} has an expiry date before today: {}",
605                        alias, dateTime);
606           valid = false;
607         } else {
608           logger.debug("Certificate {} has an expiry date over today: {}",
609                        alias, dateTime);
610         }
611       } catch (final KeyStoreException e) {//NOSONAR
612         logger.warn("Cannot get Expiry Date: {}", e.getMessage());
613       }
614     }
615     return valid;
616   }
617 }