001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.jbi.security.keystore.impl;
018    
019    import java.security.GeneralSecurityException;
020    import java.security.SecureRandom;
021    
022    import javax.net.ssl.SSLContext;
023    import javax.net.ssl.SSLServerSocketFactory;
024    import javax.net.ssl.SSLSocketFactory;
025    
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    import org.apache.servicemix.jbi.security.keystore.KeystoreInstance;
029    import org.apache.servicemix.jbi.security.keystore.KeystoreIsLocked;
030    import org.apache.servicemix.jbi.security.keystore.KeystoreManager;
031    
032    /**
033     * 
034     * @org.apache.xbean.XBean element="keystoreManager"
035     *
036     */
037    public class BaseKeystoreManager implements KeystoreManager {
038    
039        protected final Log log = LogFactory.getLog(getClass());
040        
041        protected KeystoreInstance[] keystores;
042    
043        /**
044         * @return the keystores
045         */
046        public KeystoreInstance[] getKeystores() {
047            return keystores;
048        }
049    
050        /**
051         * @param keystores the keystores to set
052         */
053        public void setKeystores(KeystoreInstance[] keystores) {
054            this.keystores = keystores;
055        }
056    
057        /**
058         * Gets a SocketFactory using one Keystore to access the private key and
059         * another to provide the list of trusted certificate authorities.
060         * 
061         * @param provider
062         *            The SSL provider to use, or null for the default
063         * @param protocol
064         *            The SSL protocol to use
065         * @param algorithm
066         *            The SSL algorithm to use
067         * @param keyStore
068         *            The key keystore name as provided by listKeystores. The
069         *            KeystoreInstance for this keystore must be unlocked.
070         * @param keyAlias
071         *            The name of the private key in the keystore. The
072         *            KeystoreInstance for this keystore must have unlocked this
073         *            key.
074         * @param trustStore
075         *            The trust keystore name as provided by listKeystores. The
076         *            KeystoreInstance for this keystore must have unlocked this
077         *            key.
078         * @param loader
079         *            The class loader used to resolve factory classes.
080         * 
081         * @return A created SSLSocketFactory item created from the KeystoreManager.
082         * @throws GeneralSecurityException 
083         * @throws KeystoreIsLocked
084         *             Occurs when the requested key keystore cannot be used because
085         *             it has not been unlocked.
086         * @throws KeyIsLocked
087         *             Occurs when the requested private key in the key keystore
088         *             cannot be used because it has not been unlocked.
089         * @throws NoSuchAlgorithmException
090         * @throws UnrecoverableKeyException
091         * @throws KeyStoreException
092         * @throws KeyManagementException
093         * @throws NoSuchProviderException
094         */
095        public SSLSocketFactory createSSLFactory(String provider, String protocol, 
096                                                 String algorithm, String keyStore,
097                                                 String keyAlias, String trustStore) throws GeneralSecurityException  {
098            // the keyStore is optional.
099            KeystoreInstance keyInstance = null;
100            if (keyStore != null) {
101                keyInstance = getKeystore(keyStore);
102                if (keyInstance.isKeystoreLocked()) {
103                    throw new KeystoreIsLocked("Keystore '" + keyStore
104                                    + "' is locked; please use the keystore page in the admin console to unlock it");
105                }
106                if (keyInstance.isKeyLocked(keyAlias)) {
107                    throw new KeystoreIsLocked("Key '" + keyAlias + "' in keystore '" + keyStore
108                                    + "' is locked; please use the keystore page in the admin console to unlock it");
109                }
110            }
111            KeystoreInstance trustInstance = trustStore == null ? null : getKeystore(trustStore);
112            if (trustInstance != null && trustInstance.isKeystoreLocked()) {
113                throw new KeystoreIsLocked("Keystore '" + trustStore
114                                + "' is locked; please use the keystore page in the admin console to unlock it");
115            }
116    
117            // OMG this hurts, but it causes ClassCastExceptions elsewhere unless
118            // done this way!
119            try {
120                /*
121                Class cls = loader.loadClass("javax.net.ssl.SSLContext");
122                Object ctx = cls.getMethod("getInstance", new Class[] { String.class }).invoke(null,
123                                new Object[] { protocol });
124                Class kmc = loader.loadClass("[Ljavax.net.ssl.KeyManager;");
125                Class tmc = loader.loadClass("[Ljavax.net.ssl.TrustManager;");
126                Class src = loader.loadClass("java.security.SecureRandom");
127                cls.getMethod("init", new Class[] { kmc, tmc, src }).invoke(
128                                ctx,
129                                new Object[] { keyInstance == null ? null : keyInstance.getKeyManager(algorithm, keyAlias),
130                                                trustInstance == null ? null : trustInstance.getTrustManager(algorithm),
131                                                new java.security.SecureRandom() });
132                Object result = cls.getMethod("getSocketFactory", new Class[0]).invoke(ctx, new Object[0]);
133                return (SSLSocketFactory) result;
134                */
135                SSLContext context;
136                if (provider == null) {
137                    context = SSLContext.getInstance(protocol);
138                } else {
139                    context = SSLContext.getInstance(protocol, provider);
140                }
141                context.init(keyInstance == null ? null : keyInstance.getKeyManager(algorithm, keyAlias), 
142                             trustInstance == null ? null : trustInstance.getTrustManager(algorithm), 
143                                             new SecureRandom());
144                return context.getSocketFactory();
145            } catch (Exception e) {
146                log.error("Unable to dynamically load", e);
147                return null;
148            }
149        }
150    
151        /**
152         * Gets a ServerSocketFactory using one Keystore to access the private key
153         * and another to provide the list of trusted certificate authorities.
154         * 
155         * @param provider
156         *            The SSL provider to use, or null for the default
157         * @param protocol
158         *            The SSL protocol to use
159         * @param algorithm
160         *            The SSL algorithm to use
161         * @param keyStore
162         *            The key keystore name as provided by listKeystores. The
163         *            KeystoreInstance for this keystore must be unlocked.
164         * @param keyAlias
165         *            The name of the private key in the keystore. The
166         *            KeystoreInstance for this keystore must have unlocked this
167         *            key.
168         * @param trustStore
169         *            The trust keystore name as provided by listKeystores. The
170         *            KeystoreInstance for this keystore must have unlocked this
171         *            key.
172         * @param loader
173         *            The class loader used to resolve factory classes.
174         * 
175         * @throws KeystoreIsLocked
176         *             Occurs when the requested key keystore cannot be used because
177         *             it has not been unlocked.
178         * @throws KeyIsLocked
179         *             Occurs when the requested private key in the key keystore
180         *             cannot be used because it has not been unlocked.
181         */
182        public SSLServerSocketFactory createSSLServerFactory(String provider, String protocol, 
183                                                             String algorithm, String keyStore, 
184                                                             String keyAlias, String trustStore) throws GeneralSecurityException {
185            KeystoreInstance keyInstance = getKeystore(keyStore);
186            if (keyInstance.isKeystoreLocked()) {
187                throw new KeystoreIsLocked("Keystore '" + keyStore
188                                + "' is locked; please use the keystore page in the admin console to unlock it");
189            }
190            if (keyInstance.isKeyLocked(keyAlias)) {
191                throw new KeystoreIsLocked("Key '" + keyAlias + "' in keystore '" + keyStore
192                                + "' is locked; please use the keystore page in the admin console to unlock it");
193            }
194            KeystoreInstance trustInstance = trustStore == null ? null : getKeystore(trustStore);
195            if (trustInstance != null && trustInstance.isKeystoreLocked()) {
196                throw new KeystoreIsLocked("Keystore '" + trustStore
197                                + "' is locked; please use the keystore page in the admin console to unlock it");
198            }
199    
200            // OMG this hurts, but it causes ClassCastExceptions elsewhere unless
201            // done this way!
202            try {
203                /*
204                Class cls = loader.loadClass("javax.net.ssl.SSLContext");
205                Object ctx = cls.getMethod("getInstance", new Class[] { String.class }).invoke(null,
206                                new Object[] { protocol });
207                Class kmc = loader.loadClass("[Ljavax.net.ssl.KeyManager;");
208                Class tmc = loader.loadClass("[Ljavax.net.ssl.TrustManager;");
209                Class src = loader.loadClass("java.security.SecureRandom");
210                cls.getMethod("init", new Class[] { kmc, tmc, src }).invoke(
211                                ctx,
212                                new Object[] { keyInstance.getKeyManager(algorithm, keyAlias),
213                                                trustInstance == null ? null : trustInstance.getTrustManager(algorithm),
214                                                new java.security.SecureRandom() });
215                Object result = cls.getMethod("getServerSocketFactory", new Class[0]).invoke(ctx, new Object[0]);
216                return (SSLServerSocketFactory) result;
217                */
218                SSLContext context;
219                if (provider == null) {
220                    context = SSLContext.getInstance(protocol);
221                } else {
222                    context = SSLContext.getInstance(protocol, provider);
223                }
224                context.init(keyInstance == null ? null : keyInstance.getKeyManager(algorithm, keyAlias), 
225                             trustInstance == null ? null : trustInstance.getTrustManager(algorithm), 
226                                             new SecureRandom());
227                return context.getServerSocketFactory();
228            } catch (Exception e) {
229                log.error("Unable to dynamically load", e);
230                return null;
231            }
232        }
233    
234        public KeystoreInstance getKeystore(String name) {
235            if (keystores != null) {
236                for (int i = 0; i < keystores.length; i++) {
237                    if (name.equals(keystores[i].getName())) {
238                        return keystores[i];
239                    }
240                }
241            }
242            return null;
243        }
244    
245    }