View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  package com.gridsystems.utils;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.security.KeyManagementException;
26  import java.security.KeyStore;
27  import java.security.KeyStoreException;
28  import java.security.MessageDigest;
29  import java.security.NoSuchAlgorithmException;
30  import java.security.cert.CertificateException;
31  import java.security.cert.X509Certificate;
32  
33  import javax.net.ssl.SSLContext;
34  import javax.net.ssl.SSLException;
35  import javax.net.ssl.SSLSocket;
36  import javax.net.ssl.SSLSocketFactory;
37  import javax.net.ssl.TrustManager;
38  import javax.net.ssl.TrustManagerFactory;
39  import javax.net.ssl.X509TrustManager;
40  /**
41   * KeyStore Utilities.
42   *
43   * @author Xmas
44   *
45   */
46  public final class KeyStoreUtils {
47  
48    /**
49     * Private constructor.
50     */
51    private KeyStoreUtils() {
52    }
53  
54    /**
55     * Save KeyStore to File.
56     *
57     * @param file Where store.
58     * @param ks Instance of KeyStore
59     * @param passphrase Password of KeyStore
60     * @throws KeyStoreException
61     *         If the keystore has not been initialised(loaded)
62     * @throws NoSuchAlgorithmException
63     *         If the appropriate data integrity algorithm could not be found
64     * @throws CertificateException
65     *         If any of the certificates included in the keystore data could not
66     *         be stored
67     * @throws IOException If an error occurs storing the keystore
68     */
69    public static void saveKeyStoreToFile(File file, KeyStore ks, char[] passphrase)
70      throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
71      IOException {
72      OutputStream out = new FileOutputStream(file);
73      try {
74        ks.store(out, passphrase);
75      } finally {
76        FileUtils.close(out);
77      }
78    }
79  
80    /**
81     * Load KeyStore from File.
82     *
83     * @param file File where load.
84     * @param passphrase Password of KeyStore
85     * @return instance of KeyStore
86     * @throws KeyStoreException If error
87     * @throws IOException If error
88     * @throws NoSuchAlgorithmException If error
89     * @throws CertificateException If error
90     */
91    public static KeyStore loadKeyStoreFromFile(File file, char[] passphrase)
92      throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
93      IOException {
94      InputStream in = new FileInputStream(file);
95      try {
96        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
97        ks.load(in, passphrase);
98        return ks;
99      } finally {
100       FileUtils.close(in);
101     }
102   }
103 
104 
105   /**
106    * Gets certificates from server.
107    *
108    * @param host Server host
109    * @param port Server port
110    * @return List of server certificates
111    * @throws NoSuchAlgorithmException If error
112    * @throws KeyStoreException If error
113    * @throws KeyManagementException If error
114    * @throws IOException If error
115    * @throws CertificateException If error
116    */
117   public static X509Certificate[] getCertificatesFromServer(String host, int port)
118     throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException,
119            IOException, CertificateException {
120     // temporal keyStore
121     KeyStore ks = createEmptyKeyStore("changeit".toCharArray());
122 
123     SSLContext context = SSLContext.getInstance("TLS");
124     TrustManagerFactory tmf = TrustManagerFactory
125         .getInstance(TrustManagerFactory.getDefaultAlgorithm());
126     tmf.init(ks);
127     X509TrustManager defaultTrustManager = (X509TrustManager) tmf
128         .getTrustManagers()[0];
129     SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
130     context.init(null, new TrustManager[] { tm }, null);
131     SSLSocketFactory factory = context.getSocketFactory();
132 
133     SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
134     final int timeout = 10000;
135     socket.setSoTimeout(timeout);
136     try {
137       socket.startHandshake();
138       socket.close();
139     } catch (SSLException e) {
140     }
141 
142     X509Certificate[] chain = tm.chain;
143     if (chain == null) {
144       chain = new X509Certificate[0];
145     }
146 
147     return chain;
148   }
149 
150 
151   /**
152    * Create empty KeyStore.
153    *
154    * @param password Password of KeyStore
155    * @return Empty keystore.
156    * @throws KeyStoreException If error
157    * @throws IOException If error
158    * @throws CertificateException If error
159    * @throws NoSuchAlgorithmException If error
160    */
161   public static KeyStore createEmptyKeyStore(char[] password) throws KeyStoreException,
162     NoSuchAlgorithmException, CertificateException, IOException  {
163     KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
164     final InputStream is = null;
165     ks.load(is, password);
166     return ks;
167   }
168 
169   /**
170    * Delete Certificate (using alias) from KeyStore.
171    *
172    * @param ks KeyStore
173    * @param alias Certificate alias.
174    *
175    * @throws KeyStoreException If error
176    */
177   public static void removeCertificateFromKeyStore(KeyStore ks, String alias)
178     throws KeyStoreException  {
179     ks.deleteEntry(alias);
180   }
181 
182   /**
183    * Add Certificates to KeyStore.
184    *
185    * @param alias Certificate alias
186    * @param passphrase Password of KeyStore
187    * @param ks Instance of KeyStore
188    * @param certs List of Certificates to add
189    * @return List of alias added.
190    * @throws NoSuchAlgorithmException If error
191    * @throws IOException If error
192    * @throws KeyStoreException If error
193    * @throws CertificateException If error
194    */
195   public static String[] addCertificatesToKeyStore(String alias, KeyStore ks,
196     char[] passphrase, X509Certificate[] certs) throws NoSuchAlgorithmException,
197     IOException, KeyStoreException, CertificateException {
198 
199     String[] aliasArray;
200     if (certs.length == 0) {
201       aliasArray = new String[0];
202     } else {
203 
204       if (certs.length == 1) {
205         aliasArray = new String[] {alias};
206       } else {
207         aliasArray = new String[certs.length];
208         for (int i = 0; i < certs.length; i++) {
209           aliasArray[i] = alias + "-" + (i + 1);
210         }
211       }
212 
213       for (int i = 0; i < certs.length; i++) {
214         X509Certificate cert = certs[i];
215         ks.setCertificateEntry(aliasArray[i], cert);
216       }
217     }
218     return aliasArray;
219   }
220 
221   /**
222    * Valid Hexadecimal digits.
223    */
224   private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
225 
226   /**
227    * Bytes to hexadecimal format.
228    * @param bytes List of bytes
229    * @return Human format
230    */
231   private static String toHexString(byte[] bytes) {
232     StringBuilder sb = new StringBuilder(bytes.length * 3);
233     for (int b : bytes) {
234       b &= 0xff;
235       sb.append(HEXDIGITS[b >> 4]);
236       sb.append(HEXDIGITS[b & 15]);
237       sb.append(' ');
238     }
239     return sb.toString();
240   }
241 
242   /**
243    * Special TrustManager.
244    */
245   private static class SavingTrustManager implements X509TrustManager {
246 
247     /**
248      * X509TrustManager.
249      */
250     private final X509TrustManager tm;
251 
252     /**
253      * List of Certificates.
254      */
255     private X509Certificate[] chain;
256 
257     /**
258      * Constructor.
259      * @param tm X509TrustManager
260      */
261     SavingTrustManager(X509TrustManager tm) {
262       this.tm = tm;
263     }
264 
265     /**
266      * {@inheritDoc}
267      */
268     public X509Certificate[] getAcceptedIssuers() {
269       throw new UnsupportedOperationException();
270     }
271 
272     /**
273      * {@inheritDoc}
274      */
275     public void checkClientTrusted(X509Certificate[] certs, String authType)
276       throws CertificateException {
277       throw new UnsupportedOperationException();
278     }
279 
280     /**
281      * {@inheritDoc}
282      */
283     public void checkServerTrusted(X509Certificate[] certs, String authType)
284       throws CertificateException {
285       this.chain = certs;
286       tm.checkServerTrusted(certs, authType);
287     }
288   }
289 
290   /**
291    * Main method.
292    *
293    * @param args Arguments
294    * @throws Exception If an error occurs
295    */
296   public static void main(String[] args) throws Exception {
297 
298     if (args.length != 3) {
299       System.err.println("Usage parameters: host port destination_file");
300       System.exit(-1);
301     }
302     String host = args[0];
303     int port = Integer.parseInt(args[1]);
304     String filename = args[2];
305 
306     X509Certificate[] chain = getCertificatesFromServer(host, port);
307 
308     System.out.println();
309     System.out.println("Server sent " + chain.length + " certificate(s):");
310     System.out.println();
311     MessageDigest sha1 = MessageDigest.getInstance("SHA1");
312     MessageDigest md5 = MessageDigest.getInstance("MD5");
313     for (int i = 0; i < chain.length; i++) {
314       X509Certificate cert = chain[i];
315       System.out.println(" " + (i + 1) + " Subject " + cert.getSubjectDN());
316       System.out.println("   Issuer  " + cert.getIssuerDN());
317       sha1.update(cert.getEncoded());
318       System.out.println("   sha1    " + toHexString(sha1.digest()));
319       md5.update(cert.getEncoded());
320       System.out.println("   md5     " + toHexString(md5.digest()));
321       System.out.println();
322 
323       byte[] data = cert.getEncoded();
324 
325       String name;
326       if (i == 0) {
327         name = filename;
328       } else {
329         int pos = filename.lastIndexOf('.');
330         if (pos == -1) {
331           name = filename + "_" + i;
332         } else {
333           name = filename.substring(0, pos) + "_" + i
334             + filename.substring(pos, filename.length());
335         }
336       }
337       FileOutputStream out = new FileOutputStream(name);
338       out.write(data);
339       out.close();
340     }
341   }
342 
343 }