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  
18  /*
19   * Project: KernelConfigurator
20   * Created on 29-mar-2004
21   *
22   * Copyright (c)2004 Grid Systems
23   */
24  package com.gridsystems.config.modules.tomcat;
25  
26  import java.io.ByteArrayOutputStream;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.FileOutputStream;
30  import java.io.IOException;
31  import java.net.InetAddress;
32  import java.util.Properties;
33  
34  import com.gridsystems.utils.SystemUtils;
35  
36  /**
37   * Utility methods for keystore creation.
38   *
39   * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
40   * @version 1.0
41   */
42  public final class KeystoreUtils {
43    /**
44     * Buffer size for file copy.
45     */
46    private static final int BUF_SIZE = 4096;
47  
48    /**
49     * Utility class: should never be instantiated.
50     */
51    private KeystoreUtils() { }
52  
53    /**
54     * Creates all keystore files in the specified directory. This includes:
55     * <ul>
56     *   <li>keystore: server keystore
57     *   <li>cacerts: server truststore
58     *   <li>clientCacerts: client truststore
59     * </ul>
60     *
61     * @param ksDir Directory where files will be created
62     * @param props Properties for keystore customization
63     * @throws IOException If an error occurs
64     */
65    static void createKeystores(File ksDir, Properties props) throws IOException {
66      ksDir.mkdirs();
67  
68      // Prevents NPEs
69      props = (props == null) ? new Properties() : props;
70  
71      createKeystore(ksDir, props);
72      createClientCacerts(ksDir, props);
73    }
74  
75    /**
76     * Creates the server keystore.
77     *
78     * @param ksDir Directory where the keystore file will be created
79     * @param props Customization properties
80     * @throws IOException If an error occurs writing the file
81     */
82    private static void createKeystore(File ksDir, Properties props) throws IOException {
83      File ks = new File(ksDir, "keystore");
84      String javaHome = System.getProperty("java.home");
85  
86      // Double checks that we do not override an existent keystore
87      if (ks.exists()) {
88        String pass      = props.getProperty("keystore.pass", "changeit");
89        String oldPass   = props.getProperty("keystore.oldPass", "changeit");
90        // Changes the password
91        if (!pass.equals(oldPass)) {
92          String[] args = {
93            javaHome + "/bin/keytool",
94            "-J-Xmx96m",
95            "-storepasswd",
96            "-keystore", ks.getAbsolutePath(),
97            "-storepass", oldPass,
98            "-new", pass,
99          };
100         execCommand(args);
101 
102         String[] args2 = {
103           javaHome + "/bin/keytool",
104           "-J-Xmx96m",
105           "-keypasswd",
106           "-alias", "tomcat",
107           "-keystore", ks.getAbsolutePath(),
108           "-storepass", pass,
109           "-keypass", oldPass,
110           "-new", pass,
111         };
112         execCommand(args2);
113       }
114       return;
115     }
116 
117     // Construct the DNAME for the default server certificate
118     String cn = props.getProperty("cert.CN", getDefaultAddress());
119     String ou = props.getProperty("cert.OU", "Kernel Server");
120     String o  = props.getProperty("cert.O", "GridSystems");
121     String l  = props.getProperty("cert.L", "Palma de Mca");
122     String st = props.getProperty("cert.ST", "Baleares");
123     String c  = props.getProperty("cert.C", "ES");
124 
125     String dname = "CN=" + cn + ", OU=" + ou + ", O=" + o + ", L=" + l + ", ST=" + st
126                    + ", C=" + c;
127 
128     // Gets the rest of parameters
129     String validity  = props.getProperty("cert.validity", "1825"); // 5 years
130     String keyAlg    = props.getProperty("cert.keyAlg", "RSA");
131     String alias     = props.getProperty("cert.alias", "tomcat");
132     String pass      = props.getProperty("keystore.pass", "changeit");
133     String storeType = props.getProperty("keystore.type", "JKS");
134 
135     String[] args = {
136       javaHome + "/bin/keytool",
137       "-genkey",
138       "-J-Xmx96m",
139       "-keystore", ks.getAbsolutePath(),
140       "-storepass", pass,
141       "-alias", alias,
142       "-keyalg", keyAlg,
143       "-dname", dname,
144       "-keypass", pass,
145       "-storetype", storeType,
146       "-validity", validity
147     };
148     execCommand(args);
149   }
150 
151   /**
152    * Copies the cacerts file from the JDK.
153    *
154    * @param dst   The directory where the file will be copied
155    * @param props Customization properties
156    * @throws IOException If an error occurs while copying the file
157    */
158   private static void copyCacerts(File dst, Properties props) throws IOException {
159     File src = new File(System.getProperty("java.home"), "lib/security/cacerts");
160     String javaHome = System.getProperty("java.home");
161 
162     // Copies the file
163     copy(src, dst);
164 
165     // Sets the same password as to keystore
166     String pass = props.getProperty("keystore.pass", "changeit");
167     if (!"changeit".equals(pass)) {
168       String[] args = {
169         javaHome + "/bin/keytool",
170         "-storepasswd",
171         "-J-Xmx96m",
172         "-keystore", dst.getAbsolutePath(),
173         "-storepass", "changeit",
174         "-new", pass,
175       };
176       execCommand(args);
177     }
178   }
179 
180   /**
181    * Creates cacerts file for client applications.
182    *
183    * @param ksDir Directory where file will be created
184    * @param props Customization properties
185    * @throws IOException In case of write error
186    */
187   private static void createClientCacerts(File ksDir, Properties props)
188     throws IOException {
189 
190     String javaHome = System.getProperty("java.home");
191 
192     File cacerts = new File(ksDir, "cacerts");
193     if (!cacerts.exists()) {
194       copyCacerts(cacerts, props);
195       File ks = new File(ksDir, "keystore");
196       File cert = new File(ksDir, "server.cer");
197       String alias = props.getProperty("cert.alias", "tomcat");
198       String pass = props.getProperty("keystore.pass", "changeit");
199       // Exports server certificate
200       String[] args = { javaHome + "/bin/keytool", "-export", "-J-Xmx96m", "-keystore",
201         ks.getAbsolutePath(), "-storepass", pass, "-alias", alias, "-file",
202         cert.getAbsolutePath() };
203       execCommand(args);
204       // Imports server certificate into client CACerts
205       args = new String[] { javaHome + "/bin/keytool", "-import", "-J-Xmx96m",
206         "-noprompt", "-trustcacerts", "-alias", alias, "-file", cert.getAbsolutePath(),
207         "-keystore", cacerts.getAbsolutePath(), "-storepass", pass };
208       execCommand(args);
209     }
210   }
211 
212   /**
213    * Gets the localhost IP address.
214    *
215    * @return This host IP address, or the loopback one if an IP could not be determined
216    */
217   private static String getDefaultAddress() {
218     try {
219       return InetAddress.getLocalHost().getHostAddress();
220     } catch (IOException e) {
221       return "127.0.0.1";
222     }
223   }
224 
225   /**
226    * Copies a file.
227    *
228    * @param src The source file
229    * @param dst The destination file
230    * @throws IOException In case of read/write error
231    */
232   private static void copy(File src, File dst) throws IOException {
233     FileInputStream is = null;
234     FileOutputStream os = null;
235     try {
236       // Open streams
237       is = new FileInputStream(src);
238       os = new FileOutputStream(dst);
239 
240       byte[] buffer = new byte[BUF_SIZE];
241       int bytesRead = 0;
242       // Copy the data.
243       while ((bytesRead = is.read(buffer)) > 0) {
244         os.write(buffer, 0, bytesRead);
245       }
246     } finally {
247       try {
248         is.close();
249       } catch (Exception e) {
250       }
251       try {
252         os.close();
253       } catch (Exception e) {
254       }
255     }
256   }
257 
258   /**
259    * Execute a command line thru a console.
260    * @param args command line to be executed
261    * @throws IOException on i/o or command execution error
262    */
263   private static void execCommand(String[] args) throws IOException {
264     //     String[] cmdparts;
265     //     if (isWindows) {
266     //       cmdparts = new String[] { "cmd", "/C", cmdline };
267     //     } else {
268     //       cmdparts = new String[] { "sh", "-c", cmdline };
269     //     }
270     ByteArrayOutputStream baos = new ByteArrayOutputStream();
271     Integer exitCode = SystemUtils.execAndSaveOutput(args, null, null, baos);
272 
273     if ((exitCode == null) || (exitCode.intValue() != 0)) {
274       throw new IOException("Command execution error: " + exitCode.intValue()
275           + "; StdOut:" + baos.toString());
276     }
277   }
278 
279 }