KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > util > KeyPairTool


1 // ========================================================================
2
// $Id: KeyPairTool.java,v 1.11 2004/11/21 11:37:28 gregwilkins Exp $
3
// Copyright 1998-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.util;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.DataInputStream JavaDoc;
20 import java.io.File JavaDoc;
21 import java.io.FileInputStream JavaDoc;
22 import java.io.FileNotFoundException JavaDoc;
23 import java.io.FileOutputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.security.GeneralSecurityException JavaDoc;
27 import java.security.KeyFactory JavaDoc;
28 import java.security.KeyStore JavaDoc;
29 import java.security.PrivateKey JavaDoc;
30 import java.security.Provider JavaDoc;
31 import java.security.Security JavaDoc;
32 import java.security.cert.Certificate JavaDoc;
33 import java.security.cert.CertificateFactory JavaDoc;
34 import java.security.spec.PKCS8EncodedKeySpec JavaDoc;
35 import java.util.ArrayList JavaDoc;
36
37 // Document our single dependency on the Util package.
38

39 /* ------------------------------------------------------------ */
40 /**
41  * Perform simple private key management for keystores.
42  *
43  * <p> The current keytool lacks the ability to insert a key/cert pair sourced
44  * from another tool. This utility fills that gap.
45  *
46  * <p> Currently this only works for RSA key/cert pairs.
47  *
48  * <p> The inverse operation, exporting a keypair to an external format, has
49  * been left as an exercise for the reader... :-)
50  *
51  * @version $Id: KeyPairTool.java,v 1.11 2004/11/21 11:37:28 gregwilkins Exp $
52  * @author Brett Sealey
53  */

54 public class KeyPairTool
55 {
56     // Default settings...
57
private File JavaDoc keyStoreFile
58         = new File JavaDoc(System.getProperty("user.home"), ".keystore");
59     private String JavaDoc keyStoreType = KeyStore.getDefaultType();
60     private Password keyStorePassword = null;
61     private Password keyPassword = null;
62     private String JavaDoc alias = "mykey";
63     private File JavaDoc privateKeyFile = null;
64     private File JavaDoc certFile = null;
65     private String JavaDoc providerClassName
66     = "org.bouncycastle.jce.provider.BouncyCastleProvider";
67
68
69     private static final String JavaDoc usageString
70         = "Tool to insert a private key/certificate pair into a keystore.\n"
71         + "Parameters:\n"
72         + " -key FILENAME, location of private key [MANDATORY]\n"
73         + " -cert FILENAME, location of certificate [MANDATORY]\n"
74         + " -storepass PASSWORD, keystore password [OPTIONAL - security RISK!]\n"
75         + " -keypass PASSWORD, password for new entry [=STOREPASS]\n"
76         + " -keystore FILENAME, location of keystore, [~/.keystore]\n"
77         + " -storetype STRING, name/type of keystore, ["
78         + KeyStore.getDefaultType() + "]\n"
79         + " -alias NAME, alias used to store key [mykey]\n"
80         + " -provider NAME, name of provider class [org.bouncycastle.jce.provider.BouncyCastleProvider]\n\n"
81         + "The keystore and key passwords will be prompted for or can be\n"
82         + "set with the following JVM system properties:\n"
83         + " jetty.ssl.password\n"
84         + " jetty.ssl.keypassword";
85
86     
87     /* ------------------------------------------------------------ */
88     /** main entry point to start this tool
89      * @param args String array containing command line arguments
90      */

91     public static void main(String JavaDoc[] args)
92     {
93         // Doit
94
KeyPairTool tool = new KeyPairTool();
95         tool.doit(args);
96     }
97
98     /* ------------------------------------------------------------ */
99     /**
100      * Load parameters and perform the import command.
101      * Catch any exceptions and clear the password arrays.
102      * @param args String array containing command line arguments
103      */

104     private void doit(String JavaDoc[] args)
105     {
106         try
107         {
108             // load parameters from the commandline
109
loadParameters(args);
110
111             // Try to load the private key
112
importKeyPair();
113         }
114         catch (Exception JavaDoc e)
115         {
116             System.out.println("Exception: " + e.getMessage());
117             e.printStackTrace();
118             
119             System.exit(23);
120         }
121     }
122
123     /* ------------------------------------------------------------ */
124     /**
125      * Import a key/cert pair into the keystore.
126      * <p> Class variables hold the state information required for this
127      * operation.
128      * @throws IOException if there are problems with file IO
129      * @throws GeneralSecurityException if there are cryptographic failures.
130      * @throws Exception on other exceptions, such as classloading failures.
131      */

132     private void importKeyPair()
133             throws IOException JavaDoc, GeneralSecurityException JavaDoc, Exception JavaDoc
134     {
135     // Load the private key
136
PrivateKey JavaDoc privateKey = loadPrivateKey(privateKeyFile);
137     
138         // Import the cert...
139
Certificate JavaDoc[] certChain = loadCertChain(certFile);
140
141         // Load any existing KeyStore
142
if (keyPassword == null)
143             keyPassword = keyStorePassword;
144
145         KeyStore JavaDoc keyStore = KeyStore.getInstance(keyStoreType);
146         InputStream JavaDoc keyStoreStream = null;
147         try
148         {
149         keyStoreStream = new FileInputStream JavaDoc(keyStoreFile);
150         System.out.println("Will load " + keyStoreType
151                    + " keystore: " + keyStoreFile);
152         }
153         catch (FileNotFoundException JavaDoc e)
154         {
155             // That's OK, we'll just create a new one
156
System.out.println("Creating keystore: " + keyStoreFile);
157         }
158
159         // The load method can accept a null keyStoreStream.
160
keyStore.load(keyStoreStream, keyStorePassword.toString().toCharArray());
161
162         if (keyStoreStream != null)
163         {
164             keyStoreStream.close();
165             System.out.println("Keystore loaded OK...");
166         }
167
168         // Insert the new key pair
169
keyStore.setKeyEntry(alias,
170                              privateKey,
171                              keyPassword.toString().toCharArray(),
172                              certChain);
173
174         // Save the KeyStore
175
FileOutputStream JavaDoc keyStoreOut = new FileOutputStream JavaDoc(keyStoreFile);
176         keyStore.store(keyStoreOut,
177                        keyStorePassword.toString().toCharArray());
178         keyStoreOut.close();
179
180         System.out.println("Keys have been written to keystore");
181     }
182
183     /* ------------------------------------------------------------ */
184     /**
185      * Load the chain of certificates from the given File.
186      * <p> Note that the certificates must be in the correct order to
187      * form a valid chain starting with the cert corresponding to the
188      * private key and ending with the cert just before the top level
189      * cert.
190      * @param certFile String name of file to load the key from
191      * @return PrivateKey loaded from the file
192      * @throws Exception if there are problems with loading the key.
193      */

194     private Certificate JavaDoc[] loadCertChain(File JavaDoc certFile)
195     throws Exception JavaDoc
196     {
197         DataInputStream JavaDoc dis = null;
198         try
199         {
200             FileInputStream JavaDoc fis = new FileInputStream JavaDoc(certFile);
201             dis = new DataInputStream JavaDoc(fis);
202             byte[] bytes = new byte[dis.available()];
203             dis.readFully(bytes);
204             ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(bytes);
205     
206             CertificateFactory JavaDoc certificateFactory
207                 = CertificateFactory.getInstance("X.509");
208
209             ArrayList JavaDoc chain = new ArrayList JavaDoc();
210             while (bais.available() > 0) {
211                 Certificate JavaDoc cert
212                     = certificateFactory.generateCertificate(bais);
213                 // System.out.println(cert.toString());
214
chain.add(cert);
215             }
216
217             Certificate JavaDoc[] certChain
218                 = (Certificate JavaDoc[])chain.toArray(new Certificate JavaDoc[chain.size()]);
219
220             System.out.println("Loaded the cert chain. Depth = "
221                                + certChain.length);
222             return certChain;
223         }
224         finally
225         {
226             IO.close(dis);
227         }
228     }
229     
230     /* ------------------------------------------------------------ */
231     /**
232      * Load an RSA private key from the given File
233      * @param privateKeyFile String name of file to load the key from
234      * @return PrivateKey loaded from the file
235      * @throws Exception if there are problems with loading the key.
236      */

237     private PrivateKey JavaDoc loadPrivateKey(File JavaDoc privateKeyFile)
238     throws Exception JavaDoc
239     {
240     // Load the key file.
241
System.out.println("Loading private key from "
242                + privateKeyFile
243                + ", using " + providerClassName
244                + " as the private key loading provider");
245         FileInputStream JavaDoc privateKeyInputStream = null;
246         byte[] keyBytes;
247         
248         try
249         {
250             privateKeyInputStream = new FileInputStream JavaDoc(privateKeyFile);
251             keyBytes = new byte[(int) privateKeyFile.length()];
252             privateKeyInputStream.read(keyBytes);
253         }
254         finally
255         {
256             IO.close(privateKeyInputStream);
257         }
258     
259         // Dynamically register the Bouncy Castle provider for RSA
260
// support.
261
Class JavaDoc providerClass = Loader.loadClass(this.getClass(),providerClassName);
262     Provider JavaDoc provider = (Provider JavaDoc)providerClass.newInstance();
263     Security.insertProviderAt(provider, 1);
264     try {
265         // Load the private key
266
PKCS8EncodedKeySpec JavaDoc privateKeySpec
267         = new PKCS8EncodedKeySpec JavaDoc(keyBytes);
268         KeyFactory JavaDoc keyFactory
269         = KeyFactory.getInstance("RSA");
270         PrivateKey JavaDoc privateKey
271         = keyFactory.generatePrivate(privateKeySpec);
272         
273         System.out.println("Loaded " + privateKey.getAlgorithm()
274                    + " " + privateKey.getFormat()
275                    + " private key.");
276         return privateKey;
277     } finally {
278         // Dynamically deinstall the RSA provider
279
Security.removeProvider(provider.getName());
280     }
281     }
282     
283     /* ------------------------------------------------------------ */
284     /**
285      * Show a usage message.
286      */

287     static private void usage()
288     {
289         System.out.println(usageString);
290         System.exit(23);
291     }
292
293     /* ------------------------------------------------------------ */
294     /**
295      * Load parameters from the given args and check usage.
296      * Will exit on usage errors.
297      * <p> Class variables are populated from the command line arguments
298      * @param args Array of Strings from the command line.
299      */

300     private void loadParameters(String JavaDoc[] args)
301     {
302         for (int i = 0; (i < args.length) && args[i].startsWith("-"); i++)
303         {
304             String JavaDoc parameterName = args[i];
305             if (parameterName.equalsIgnoreCase("-key"))
306                 privateKeyFile = new File JavaDoc(args[++i]);
307             else if (parameterName.equalsIgnoreCase("-cert"))
308                 certFile = new File JavaDoc(args[++i]);
309         else if (parameterName.equalsIgnoreCase("-keystore"))
310                 keyStoreFile = new File JavaDoc(args[++i]);
311             else if (parameterName.equalsIgnoreCase("-storetype"))
312                 keyStoreType = args[++i];
313             else if (parameterName.equalsIgnoreCase("-alias"))
314                 alias = args[++i];
315             else if (parameterName.equalsIgnoreCase("-provider"))
316                 providerClassName = args[++i];
317             else
318             {
319                 System.err.println("Illegal parameter: " + parameterName);
320                 usage();
321             }
322         }
323
324         // Check that mandatory fields have been populated
325
if (privateKeyFile == null || certFile == null)
326         {
327             usage();
328         }
329
330         keyStorePassword = Password.getPassword("jetty.ssl.password",null,null);
331         keyPassword = Password.getPassword("jetty.ssl.keypassword",
332                                            null,
333                                            keyStorePassword.toString());
334     }
335 }
336
Popular Tags