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 {
}
}
+
}