You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2011/03/21 18:36:34 UTC

svn commit: r1083876 - in /commons/proper/net/trunk/src: changes/changes.xml main/java/org/apache/commons/net/util/KeyManagerUtils.java

Author: sebb
Date: Mon Mar 21 17:36:34 2011
New Revision: 1083876

URL: http://svn.apache.org/viewvc?rev=1083876&view=rev
Log:
NET-384 KeyManagerUtils - the KeyManager is not efficient

Modified:
    commons/proper/net/trunk/src/changes/changes.xml
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/KeyManagerUtils.java

Modified: commons/proper/net/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/changes/changes.xml?rev=1083876&r1=1083875&r2=1083876&view=diff
==============================================================================
--- commons/proper/net/trunk/src/changes/changes.xml (original)
+++ commons/proper/net/trunk/src/changes/changes.xml Mon Mar 21 17:36:34 2011
@@ -57,6 +57,9 @@ The <action> type attribute can be add,u
 
     <body>
         <release version="3.0" date="TBA" description="TBA">
+            <action issue="NET-384" dev="sebb" type="update">
+            KeyManagerUtils - the KeyManager is not efficient.
+            </action>
             <action issue="NET-383" dev="sebb" type="update">
             KeyManagerUtils - allow alias to be omitted when there is only one private key in the store
             </action>

Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/KeyManagerUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/KeyManagerUtils.java?rev=1083876&r1=1083875&r2=1083876&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/KeyManagerUtils.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/KeyManagerUtils.java Mon Mar 21 17:36:34 2011
@@ -18,6 +18,7 @@
 
 package org.apache.commons.net.util;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.Socket;
@@ -26,6 +27,7 @@ import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.Principal;
 import java.security.PrivateKey;
+import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.Enumeration;
 
@@ -39,121 +41,182 @@ import org.apache.commons.net.io.Util;
  * <p>
  * How to use with a client certificate:
  * <pre>
- * KeyStore ks = KeyManagerUtils.createKeyStore("JKS","/path/to/privatekeystore.jks","storepassword");
- * KeyManager km = KeyManagerUtils.createClientKeyManager(ks, "privatekeyalias", "keypassword");
+ * KeyManager km = KeyManagerUtils.createClientKeyManager("JKS",
+ *     "/path/to/privatekeystore.jks","storepassword", 
+ *     "privatekeyalias", "keypassword");
  * FTPSClient cl = new FTPSClient();
  * cl.setKeyManager(km);
  * cl.connect(...);
  * </pre>
+ * If using the default store type and the key password is the same as the
+ * store password, these parameters can be omitted. <br/>
+ * If the desired key is the first or only key in the keystore, the keyAlias parameter
+ * can be omitted, in which case the code becomes:
+ * <pre>
+ * KeyManager km = KeyManagerUtils.createClientKeyManager(
+ *     "/path/to/privatekeystore.jks","storepassword");
+ * FTPSClient cl = new FTPSClient();
+ * cl.setKeyManager(km);
+ * cl.connect(...);
+ * </pre>
+ * 
  * @since 3.0
  */
 public final class KeyManagerUtils {
 
+    private static final String DEFAULT_STORE_TYPE = KeyStore.getDefaultType();
+
     private KeyManagerUtils(){
         // Not instantiable
     }
 
     /**
-     * Create a keystore.
-     * 
-     * @param ksType the type, e.g. JKS
-     * @param keyStorePath the pathname of the store
-     * @param keyStorePass the password for the store
-     * @return the key store, loaded and ready for use.
-     * 
-     * @throws GeneralSecurityException
-     * @throws IOException
+     * Create a client key manager which returns a particular key.
+     * Does not handle server keys.
+     *
+     * @param ks the keystore to use
+     * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used
+     * @param keyPass the password of the key to use
+     * @return the customised KeyManager
      */
-    public static KeyStore createKeyStore(String ksType, String keyStorePath, String keyStorePass)
-        throws GeneralSecurityException, IOException {
-        KeyStore ks = KeyStore.getInstance(ksType);
-        FileInputStream stream = null;
-        try {
-            stream = new FileInputStream(keyStorePath);
-            ks.load(stream, keyStorePass.toCharArray());
-        } finally {
-            Util.closeQuietly(stream);
-        }
-        return ks;
+    public static KeyManager createClientKeyManager(KeyStore ks, String keyAlias, String keyPass)
+        throws GeneralSecurityException
+    {
+        ClientKeyStore cks = new ClientKeyStore(ks, keyAlias != null ? keyAlias : findAlias(ks), keyPass);
+        return new X509KeyManager(cks);
     }
 
     /**
      * Create a client key manager which returns a particular key.
      * Does not handle server keys.
-     *  
-     * @param keyStore the keystore
+     *
+     * @param storeType the type of the keyStore, e.g. "JKS"
+     * @param storePath the path to the keyStore
+     * @param storePass the keyStore password 
      * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used
      * @param keyPass the password of the key to use
-     * @return the customised X509KeyManager
+     * @return the customised KeyManager
      */
-    public static KeyManager createClientKeyManager(KeyStore keyStore, String keyAlias, String keyPass) {
-        return new X509KeyManager(keyAlias,  keyStore, keyPass);
+    public static KeyManager createClientKeyManager(String storeType, File storePath, String storePass, String keyAlias, String keyPass)
+        throws IOException, GeneralSecurityException
+    {
+        KeyStore ks = loadStore(storeType, storePath, storePass);
+        return createClientKeyManager(ks, keyAlias, keyPass);
+    }
 
+    /**
+     * Create a client key manager which returns a particular key.
+     * Does not handle server keys.
+     * Uses the default store type and assumes the key password is the same as the store password
+     *
+     * @param storePath the path to the keyStore
+     * @param storePass the keyStore password 
+     * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key entry alias is used
+     * @return the customised KeyManager
+     */
+    public static KeyManager createClientKeyManager(File storePath, String storePass, String keyAlias)
+        throws IOException, GeneralSecurityException
+    {
+        return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, keyAlias, storePass);
     }
 
-    private static class X509KeyManager extends X509ExtendedKeyManager  {
+    /**
+     * Create a client key manager which returns a particular key.
+     * Does not handle server keys.
+     * Uses the default store type and assumes the key password is the same as the store password.
+     * The key alias is found by searching the keystore for the first private key entry
+     *
+     * @param storePath the path to the keyStore
+     * @param storePass the keyStore password 
+     * @return the customised KeyManager
+     */
+    public static KeyManager createClientKeyManager(File storePath, String storePass)
+        throws IOException, GeneralSecurityException
+    {
+        return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, null, storePass);
+    }
 
-        private final KeyStore keyStore;
+    private static KeyStore loadStore(String storeType, File storePath, String storePass) 
+        throws KeyStoreException,  IOException, GeneralSecurityException {
+        KeyStore ks = KeyStore.getInstance(storeType);
+        FileInputStream stream = null;
+        try {
+            stream = new FileInputStream(storePath);
+            ks.load(stream, storePass.toCharArray());
+        } finally {
+            Util.closeQuietly(stream);
+        }
+        return ks;
+    }
+
+    private static String findAlias(KeyStore ks) throws KeyStoreException {
+        Enumeration<String> e = ks.aliases();
+        while(e.hasMoreElements()) {
+            String entry = e.nextElement();
+            if (ks.isKeyEntry(entry)) {
+                return entry;
+            }
+        }
+        throw new KeyStoreException("Cannot find a private key entry");
+    }
+
+    private static class ClientKeyStore {
 
+        private final X509Certificate[] certChain;
+        private final PrivateKey key;
         private final String keyAlias;
 
-        private final String keyPass;
+        ClientKeyStore(KeyStore ks, String keyAlias, String keyPass) throws GeneralSecurityException
+        {
+            this.keyAlias = keyAlias;
+            this.key = (PrivateKey) ks.getKey(this.keyAlias, keyPass.toCharArray());
+            Certificate[] certs = ks.getCertificateChain(this.keyAlias);
+            X509Certificate[] X509certs = new X509Certificate[certs.length];
+            for (int i=0; i < certs.length; i++) {
+                X509certs[i] = (X509Certificate) certs[i];
+            }
+            this.certChain = X509certs;
+        }
+
+        final X509Certificate[] getCertificateChain() {
+            return this.certChain;
+        }
+
+        final PrivateKey getPrivateKey() {
+            return this.key;
+        }
+
+        final String getAlias() {
+            return this.keyAlias;
+        }
+    }
+
+    private static class X509KeyManager extends X509ExtendedKeyManager  {
+
+        private final ClientKeyStore keyStore;
 
-        /**
-         * Creates a key manager to handle a specific client key
-         * @param keyAlias the key alias, may be {@code null} in which case the first key entry alias is used
-         * @param keyStore the keystore to use, which has already been loaded
-         * @param keyPass the key password, does not have to be the same as the store password
-         */
-        X509KeyManager(final String keyAlias, final KeyStore keyStore, final String keyPass){
+        X509KeyManager(final ClientKeyStore keyStore) {
             this.keyStore = keyStore;
-            if (keyAlias == null) {
-                String alias = null;
-                try {
-                    Enumeration<String> e = keyStore.aliases();
-                    while(e.hasMoreElements()) {
-                        String entry = e.nextElement();
-                        if (keyStore.isKeyEntry(entry)) {
-                            alias = entry;
-                            break;
-                        }
-                    }
-                } catch (KeyStoreException e) {
-                    // ignore
-                }
-                this.keyAlias = alias;
-            } else {
-                this.keyAlias = keyAlias;
-            }
-            this.keyPass = keyPass;
         }
 
         // Call sequence: 1
         public String chooseClientAlias(String[] keyType, Principal[] issuers,
                 Socket socket) {
-            return keyAlias;
+            return keyStore.getAlias();
         }
 
         // Call sequence: 2
         public X509Certificate[] getCertificateChain(String alias) {
-            try {
-                return new X509Certificate[]{(X509Certificate) keyStore.getCertificate(alias)};
-            } catch (GeneralSecurityException e) {
-                return null;
-            }
+            return keyStore.getCertificateChain();
         }
-
+        
         public String[] getClientAliases(String keyType, Principal[] issuers) {
-            return new String[]{ keyAlias};
+            return new String[]{ keyStore.getAlias()};
         }
 
         // Call sequence: 3
         public PrivateKey getPrivateKey(String alias) {
-            try {
-                return (PrivateKey) keyStore.getKey(alias, keyPass.toCharArray());
-            } catch (GeneralSecurityException e) {
-                return null;
-            }
+            return keyStore.getPrivateKey();
         }
 
         public String[] getServerAliases(String keyType, Principal[] issuers) {
@@ -165,4 +228,5 @@ public final class KeyManagerUtils {
         }
 
     }
+
 }