View Javadoc

1   package net.kwfgrid.gwes.client;
2   
3   /*
4    * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met:
9    *
10   *   - Redistributions of source code must retain the above copyright
11   *     notice, this list of conditions and the following disclaimer.
12   *
13   *   - Redistributions in binary form must reproduce the above copyright
14   *     notice, this list of conditions and the following disclaimer in the
15   *     documentation and/or other materials provided with the distribution.
16   *
17   *   - Neither the name of Sun Microsystems nor the names of its
18   *     contributors may be used to endorse or promote products derived
19   *     from this software without specific prior written permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33  
34  import java.io.*;
35  import org.apache.commons.io.IOUtils;
36  
37  import java.security.*;
38  import java.security.cert.*;
39  
40  import javax.net.ssl.*;
41  
42  public class CertUtils implements X509TrustManager {
43  
44      private X509Certificate[] chain;
45  
46      private String truststoreFileName;
47      private String truststorePassphrase;
48      private File truststoreFile;
49      private KeyStore truststore;
50      private X509TrustManager tm;
51  
52      private String keystoreFileName;
53      private String keystorePassphrase;
54      private File keystoreFile;
55      private KeyStore keystore;
56      private X509KeyManager[] kms;
57  
58      private static CertUtils ourInstance;
59  
60      /**
61       * Get a singleton of CertUtils.
62       * @return
63       * @throws IOException
64       * @throws NoSuchAlgorithmException
65       * @throws KeyManagementException
66       * @throws KeyStoreException
67       * @throws CertificateException
68       */
69      public synchronized static CertUtils getInstance() throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException {
70          if (CertUtils.ourInstance == null) {
71              CertUtils.ourInstance = new CertUtils();
72          }
73          return CertUtils.ourInstance;
74      }
75  
76      /**
77       * Private Constructor because of singleton pattern. Use #getInstance instead.
78       * @throws IOException
79       * @throws KeyStoreException
80       * @throws NoSuchAlgorithmException
81       * @throws CertificateException
82       * @throws KeyManagementException
83       */
84      private CertUtils() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, KeyManagementException {
85              chain = null;
86              setDefaultSystemProperties();
87              truststoreFileName = System.getProperty("javax.net.ssl.trustStore");
88              truststorePassphrase = System.getProperty("javax.net.ssl.trustStorePassword");
89              keystoreFileName = System.getProperty("javax.net.ssl.keyStore");
90              keystorePassphrase = System.getProperty("javax.net.ssl.keyStorePassword");
91      }
92  
93      static void setDefaultSystemProperties() {
94          if (System.getProperty("javax.net.ssl.trustStore") == null) {
95              String store = System.getProperty("user.home") + File.separatorChar + ".gwes-truststore.jks";
96              System.out.println("# Using default SSL trust store '"+store+"'...");
97              System.setProperty("javax.net.ssl.trustStore",store);
98          }
99          if (System.getProperty("javax.net.ssl.trustStorePassword") == null) {
100             System.out.println("# Using default SSL trust store password 'changeit'...");
101             System.setProperty("javax.net.ssl.trustStorePassword","changeit");
102         }
103         if (System.getProperty("javax.net.ssl.keyStore") == null) {
104             String store = System.getProperty("user.home") + File.separatorChar + ".gwes-keystore.jks";
105             System.out.println("# Using default SSL key store '"+store+"'...");
106             System.setProperty("javax.net.ssl.keyStore",store);
107         }
108         if (System.getProperty("javax.net.ssl.keyStorePassword") == null) {
109             System.out.println("# Using default SSL key store password 'changeit'...");
110             System.setProperty("javax.net.ssl.keyStorePassword","changeit");
111         }
112     }
113 
114     private void copyDefaultJavaTruststore() throws IOException {
115         // copy default from java security directory and store with password.
116         File javadir = new File(System.getProperty("java.home") + File.separatorChar + "lib" + File.separatorChar + "security");
117         File javastore = new File(javadir, "jssecacerts");
118         if (!javastore.isFile()) {
119             javastore = new File(javadir, "cacerts");
120         }
121         System.out.println("# Copying default truststore from "+javastore.getAbsolutePath() + " to "+ truststoreFile.getAbsolutePath() +"...");
122         FileInputStream in = new FileInputStream(javastore);
123         FileOutputStream out = new FileOutputStream(truststoreFile);
124         IOUtils.copy(in,out);
125         IOUtils.closeQuietly(in);
126         IOUtils.closeQuietly(out);
127     }
128 
129     /**
130      * Returns <code>true</code> if some certificate has been installed, <code>false</code> otherwise.
131      * @param host
132      * @param port
133      * @return
134      * @throws NoSuchAlgorithmException
135      * @throws KeyStoreException
136      * @throws IOException
137      * @throws KeyManagementException
138      * @throws CertificateException
139      */
140     public synchronized boolean installCert(String host, int port) throws NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException, CertificateException {
141         System.out.println("# Opening connection to " + host + ":" + port + "...");
142         chain = null;
143 
144         // setup truststore
145         try {
146             truststoreFile = new File(truststoreFileName);
147             if (!truststoreFile.isFile()) {
148 //            copyDefaultJavaTruststore();
149 //            setTrustStorePassword();
150                 writeEmptyTrustStore();
151             }
152             loadTrustStore();
153             TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
154             tmf.init(truststore);
155             tm =  (X509TrustManager) tmf.getTrustManagers()[0];
156         } catch (IOException e) {
157             System.out.println("##############################################################################################");
158             System.out.println("TrustStore IOException:");
159             System.out.println(e.getMessage());
160             System.out.println("##############################################################################################");
161             System.out.println("Please use Java property -Djavax.net.ssl.trustStorePassword=<TrustStorePassword> for setting passphrase!");
162             return false;
163         }
164 
165         // setup keystore
166         try {
167             keystoreFile = new File(keystoreFileName);
168             if (!keystoreFile.isFile()) {
169                 writeEmptyKeyStore();
170             }
171             loadKeyStore();
172             KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
173             kmf.init(keystore, keystorePassphrase.toCharArray());
174             kms = new X509KeyManager[]{(X509KeyManager)kmf.getKeyManagers()[0]};
175         } catch (FileNotFoundException e) {
176             System.out.println("##############################################################################################");
177             System.out.println("KeyStore FileNotFoundException:");
178             System.out.println(e.getMessage());
179             System.out.println("##############################################################################################");
180             System.out.println("Please create private keystore '"+keystoreFile+"'!");
181             return false;
182         } catch (IOException e) {
183             System.out.println("##############################################################################################");
184             System.out.println("KeyStore IOException:");
185             System.out.println(e.getMessage());
186             System.out.println("##############################################################################################");
187             System.out.println("Please use Java property -Djavax.net.ssl.keyStorePassword=<KeyStorePassword> for setting passphrase!");
188             return false;
189         } catch (UnrecoverableKeyException e) {
190             System.out.println("##############################################################################################");
191             System.out.println("UnrecoverableKeyException:");
192             System.out.println(e.getMessage());
193             System.out.println("##############################################################################################");
194             System.out.println("Please check the passphrase of the key within the keystore '"+keystoreFile+"'!");
195             return false;
196         }
197 
198         // setup context
199         SSLContext context = SSLContext.getInstance("TLS");
200         context.init(kms, new TrustManager[]{this}, null);
201         SSLSocketFactory socketFactory = context.getSocketFactory();
202         SSLSocket socket = (SSLSocket) socketFactory.createSocket(host, port);
203         socket.setSoTimeout(10000);
204         try {
205             System.out.println("# Starting SSL handshake...");
206             socket.startHandshake();
207             socket.close();
208             System.out.println();
209             System.out.println("# No errors, certificate is already trusted");
210             printCertificateChain();
211             return false;
212         } catch (javax.net.ssl.SSLHandshakeException e) {
213             System.out.println();
214             System.out.println("##############################################################################################");
215             System.out.println("SSL Handshake Exception:");
216             System.out.println(e.getMessage());
217             System.out.println("##############################################################################################");
218             if (chain == null) {
219                 System.out.println("Could not obtain server certificate chain");
220                 return false;
221             }
222 
223             // PKIX path building failed --> server cert not trusted
224             if (e.getCause() instanceof sun.security.validator.ValidatorException) {
225                 System.out.println("Server certificate not trusted.");
226                 return addCertificateToTrustStore(host);
227             }
228             // Other handshake exception
229             if (e.getMessage().indexOf("Received fatal alert: bad_certificate")!=-1) {
230                 System.out.println("Please import your private key and certificate into keystore '"+keystoreFileName+"'!");
231                 System.out.println("For example using 'keytool':");
232                 System.out.println("  keytool -importkeystore -srckeystore <user.p12> -srcstoretype pkcs12 -srcstorepass <keypass> -destkeystore "+keystoreFileName+" -deststoretype jks -deststorepass <keypass>");
233                 System.out.println("Replace <user.p12> by the filename of your PKCS12 user key/cert and <keypass> by the corresponding passphrase!");
234                 return false;
235             }
236             System.out.println("Unsupported Exception:");
237             e.printStackTrace(System.out);
238         } catch (javax.net.ssl.SSLException e) {
239             System.out.println();
240             System.out.println("##############################################################################################");
241             System.out.println("SSL Exception:");
242             System.out.println(e.getMessage());
243             System.out.println("##############################################################################################");
244             if (chain == null) {
245                 System.out.println("Could not obtain server certificate chain");
246                 return false;
247             }
248             if (e.getMessage().indexOf("the trustAnchors parameter must be non-empty") != -1) {
249                 System.out.println("Truststore is empty. Add server certificate.");
250                 return addCertificateToTrustStore(host);
251             }
252             System.out.println("Unsupported Exception:");
253             e.printStackTrace(System.out);
254         }
255 
256         return false;
257     }
258 
259     private boolean addCertificateToTrustStore(String host) throws NoSuchAlgorithmException, IOException, KeyStoreException, CertificateException {
260         printCertificateChain();
261         BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
262         System.out.println("Enter certificate to add to truststore or 'q' to quit: [1]");
263         String line = reader.readLine().trim();
264         int k;
265         try {
266             k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
267         } catch (NumberFormatException e) {
268             System.out.println("truststore not changed");
269             return false;
270         }
271 
272         X509Certificate cert = chain[k];
273         String alias = host + "-" + (k + 1);
274         truststore.setCertificateEntry(alias, cert);
275         writeTrustStore();
276 
277         System.out.println();
278         System.out.println(cert);
279         System.out.println();
280         System.out.println("Added certificate to truststore '"+ truststoreFile.getAbsolutePath() + "' using alias '" + alias + "'");
281         return true;
282     }
283 
284     private void writeEmptyTrustStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
285         System.out.println("# Creating empty truststore '" + truststoreFile + "'...");
286         truststore = KeyStore.getInstance(KeyStore.getDefaultType());
287         truststore.load(null,(truststorePassphrase ==null ? null : truststorePassphrase.toCharArray()));
288         writeTrustStore();
289     }
290 
291     private void writeEmptyKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
292         System.out.println("# Creating empty keystore '" + keystoreFile + "'...");
293         keystore = KeyStore.getInstance(KeyStore.getDefaultType());
294         keystore.load(null,(keystorePassphrase ==null ? null : keystorePassphrase.toCharArray()));
295         writeKeyStore();
296     }
297 
298     private void loadTrustStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
299         // load ca certificate truststore
300         System.out.println("# Loading truststore '" + truststoreFile + "'...");
301         truststore = KeyStore.getInstance(KeyStore.getDefaultType());
302         if (truststoreFile.length()>0L) {
303             InputStream in = new FileInputStream(truststoreFile);
304             truststore.load(in, (truststorePassphrase ==null ? null : truststorePassphrase.toCharArray()));
305             in.close();
306         } else {
307             truststore.load(null, (truststorePassphrase ==null ? null : truststorePassphrase.toCharArray()));
308         }
309     }
310 
311     private void loadKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
312         // load keystore
313         System.out.println("# Loading keystore '" + keystoreFile + "'...");
314         keystore = KeyStore.getInstance(KeyStore.getDefaultType());
315         if (keystoreFile.length()>0L) {
316             InputStream in = new FileInputStream(keystoreFile);
317             keystore.load(in, (keystorePassphrase ==null ? null : keystorePassphrase.toCharArray()));
318             in.close();
319         } else {
320             keystore.load(null, (keystorePassphrase ==null ? null : keystorePassphrase.toCharArray()));
321         }
322     }
323 
324     private void writeTrustStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
325         System.out.println("# Writing truststore '" + truststoreFile + "'...");
326         OutputStream out = new FileOutputStream(truststoreFile);
327         truststore.store(out, (truststorePassphrase ==null ? "".toCharArray() : truststorePassphrase.toCharArray()));
328         out.close();
329     }
330 
331     private void writeKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
332         System.out.println("# Writing keystore '" + keystoreFile + "'...");
333         OutputStream out = new FileOutputStream(keystoreFile);
334         keystore.store(out, (keystorePassphrase ==null ? "".toCharArray() : keystorePassphrase.toCharArray()));
335         out.close();
336     }
337 
338     private void setTrustStorePassword() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
339         System.out.println("# Setting password for new truststore '" + truststoreFile + "'...");
340         InputStream in = new FileInputStream(truststoreFile);
341         truststore = KeyStore.getInstance(KeyStore.getDefaultType());
342         truststore.load(in, null);
343         in.close();
344         writeTrustStore();
345     }
346 
347     private void printCertificateChain() throws CertificateEncodingException, NoSuchAlgorithmException {
348         System.out.println();
349         System.out.println("Server sent " + chain.length + " certificate(s):");
350         System.out.println();
351         MessageDigest sha1 = MessageDigest.getInstance("SHA1");
352         MessageDigest md5 = MessageDigest.getInstance("MD5");
353         for (int i = 0; i < chain.length; i++) {
354             X509Certificate cert = chain[i];
355             System.out.println
356                     (" " + (i + 1) + " Subject    " + cert.getSubjectDN());
357             System.out.println("   Issuer     " + cert.getIssuerDN());
358             sha1.update(cert.getEncoded());
359             System.out.println("   Valid from "+cert.getNotBefore());
360             System.out.println("   Valid util "+cert.getNotAfter());
361             System.out.println("   sha1       " + toHexString(sha1.digest()));
362             md5.update(cert.getEncoded());
363             System.out.println("   md5        " + toHexString(md5.digest()));
364             System.out.println();
365         }
366     }
367 
368     private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
369 
370     private static String toHexString(byte[] bytes) {
371         StringBuilder sb = new StringBuilder(bytes.length * 3);
372         for (int b : bytes) {
373             b &= 0xff;
374             sb.append(HEXDIGITS[b >> 4]);
375             sb.append(HEXDIGITS[b & 15]);
376             sb.append(' ');
377         }
378         return sb.toString();
379     }
380 
381     public X509Certificate[] getAcceptedIssuers() {
382         System.out.println("UnsupportedOperationException: getAcceptedIssuers()...");
383         throw new UnsupportedOperationException();
384     }
385 
386     public void checkClientTrusted(X509Certificate[] chain, String authType)
387             throws CertificateException {
388         System.out.println("UnsupportedOperationException: checkClientTrusted()...");
389         throw new UnsupportedOperationException();
390     }
391 
392     public void checkServerTrusted(X509Certificate[] chain, String authType)
393             throws CertificateException {
394         this.chain = chain;
395         tm.checkServerTrusted(chain, authType);
396     }
397 
398     public static void main(String[] args) throws Exception {
399         String host;
400         int port;
401         if (args.length == 1) {
402             String[] c = args[0].split(":");
403             host = c[0];
404             port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
405         } else {
406             System.out.println("--------------------------------------------------------------");
407             System.out.println("Usage: java CertUtils <host>[:port]");
408             System.out.println("--------------------------------------------------------------");
409             System.out.println("     The following java properties are supported:");
410             System.out.println("       -Djavax.net.ssl.trustStore=<TrustStoreFileName>");
411             System.out.println("       -Djavax.net.ssl.trustStorePassword=<TrustStorePassword>");
412             System.out.println("       -Djavax.net.ssl.keyStore=<KeyStoreFileName>");
413             System.out.println("       -Djavax.net.ssl.keyStorePassword=<KeyStorePassword>");
414             System.out.println("--------------------------------------------------------------");
415             return;
416         }
417 
418         boolean next = true;
419         while (next) {
420             next = CertUtils.getInstance().installCert(host,port);
421         }
422         
423     }
424 
425 }