001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.filter.ssl;
021
022import java.io.BufferedInputStream;
023import java.io.ByteArrayInputStream;
024import java.io.ByteArrayOutputStream;
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.net.URL;
030import java.security.KeyStore;
031import java.security.KeyStoreException;
032import java.security.NoSuchAlgorithmException;
033import java.security.NoSuchProviderException;
034import java.security.cert.CertificateException;
035
036/**
037 * A factory that creates and configures a new {@link KeyStore} instance.
038 *
039 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
040 */
041public class KeyStoreFactory {
042
043    private String type = "JKS";
044
045    private String provider = null;
046
047    private char[] password = null;
048
049    private byte[] data = null;
050
051    /**
052     * Creates a new {@link KeyStore}. This method will be called
053     * by the base class when Spring creates a bean using this FactoryBean.
054     *
055     * @return a new {@link KeyStore} instance.
056     * @throws KeyStoreException If we can't create an instance of the KeyStore for the given type
057     * @throws NoSuchProviderException If we don't have the provider registered to create the KeyStore
058     * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
059     * @throws CertificateException If the KeyStore certificate cannot be loaded 
060     * @throws IOException If the KeyStore cannot be loaded
061     */
062    public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
063            CertificateException, IOException {
064        if (data == null) {
065            throw new IllegalStateException("data property is not set.");
066        }
067
068        KeyStore ks;
069        if (provider == null) {
070            ks = KeyStore.getInstance(type);
071        } else {
072            ks = KeyStore.getInstance(type, provider);
073        }
074
075        InputStream is = new ByteArrayInputStream(data);
076        
077        try {
078            ks.load(is, password);
079        } finally {
080            try {
081                is.close();
082            } catch (IOException ignored) {
083                // Do nothing
084            }
085        }
086
087        return ks;
088    }
089
090    /**
091     * Sets the type of key store to create. The default is to create a
092     * JKS key store.
093     *
094     * @param type the type to use when creating the key store.
095     * @throws IllegalArgumentException if the specified value is
096     *         <code>null</code>.
097     */
098    public void setType(String type) {
099        if (type == null) {
100            throw new IllegalArgumentException("type");
101        }
102        this.type = type;
103    }
104
105    /**
106     * Sets the key store password. If this value is <code>null</code> no
107     * password will be used to check the integrity of the key store.
108     *
109     * @param password the password or <code>null</code> if no password is
110     *        needed.
111     */
112    public void setPassword(String password) {
113        if (password != null) {
114            this.password = password.toCharArray();
115        } else {
116            this.password = null;
117        }
118    }
119
120    /**
121     * Sets the name of the provider to use when creating the key store. The
122     * default is to use the platform default provider.
123     *
124     * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
125     */
126    public void setProvider(String provider) {
127        this.provider = provider;
128    }
129
130    /**
131     * Sets the data which contains the key store.
132     *
133     * @param data the byte array that contains the key store
134     */
135    public void setData(byte[] data) {
136        byte[] copy = new byte[data.length];
137        System.arraycopy(data, 0, copy, 0, data.length);
138        this.data = copy;
139    }
140
141    /**
142     * Sets the data which contains the key store.
143     *
144     * @param dataStream the {@link InputStream} that contains the key store
145     * @throws IOException If we can't process the stream
146     */
147    private void setData(InputStream dataStream) throws IOException {
148        ByteArrayOutputStream out = new ByteArrayOutputStream();
149        try {
150            for (;;) {
151                int data = dataStream.read();
152                if (data < 0) {
153                    break;
154                }
155                out.write(data);
156            }
157            setData(out.toByteArray());
158        } finally {
159            try {
160                dataStream.close();
161            } catch (IOException e) {
162                // Ignore.
163            }
164        }
165    }
166
167    /**
168     * Sets the data which contains the key store.
169     *
170     * @param dataFile the {@link File} that contains the key store
171     * @throws IOException If we can't process the file
172     */
173    public void setDataFile(File dataFile) throws IOException {
174        setData(new BufferedInputStream(new FileInputStream(dataFile)));
175    }
176
177    /**
178     * Sets the data which contains the key store.
179     *
180     * @param dataUrl the {@link URL} that contains the key store.
181     * @throws IOException If we can't process the URL
182     */
183    public void setDataUrl(URL dataUrl) throws IOException {
184        setData(dataUrl.openStream());
185    }
186}