001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.admin.ads.util;
029    
030    import java.net.Socket;
031    import java.security.KeyStore;
032    import java.security.KeyStoreException;
033    import java.security.NoSuchAlgorithmException;
034    import java.security.NoSuchProviderException;
035    import java.security.Principal;
036    import java.security.PrivateKey;
037    import java.security.UnrecoverableKeyException;
038    import java.security.cert.X509Certificate;
039    import java.util.logging.Level;
040    import java.util.logging.Logger;
041    
042    import javax.net.ssl.KeyManager;
043    import javax.net.ssl.KeyManagerFactory;
044    import javax.net.ssl.X509KeyManager;
045    
046    
047    /**
048     * This class is in charge of checking whether the certificates that are
049     * presented are trusted or not.
050     * This implementation tries to check also that the subject DN of the
051     * certificate corresponds to the host passed using the setHostName method.
052     *
053     * The constructor tries to use a default TrustManager from the system and if
054     * it cannot be retrieved this class will only accept the certificates
055     * explicitly accepted by the user (and specified by calling acceptCertificate).
056     *
057     * NOTE: this class is not aimed to be used when we have connections in paralel.
058     */
059    public class ApplicationKeyManager implements X509KeyManager
060    {
061      static private final Logger LOG =
062        Logger.getLogger(ApplicationKeyManager.class.getName());
063    
064      /**
065       * The default keyManager.
066       */
067      private X509KeyManager sunJSSEX509KeyManager = null ;
068    
069      /**
070       * The default constructor.
071       * @param keystore The keystore to use for this keymanager.
072       * @param password The keystore password to use for this keymanager.
073       */
074      public ApplicationKeyManager(KeyStore keystore, char[] password)
075      {
076        KeyManagerFactory kmf = null;
077        String algo = "SunX509";
078        String provider = "SunJSSE";
079        try
080        {
081          kmf = KeyManagerFactory.getInstance(algo, provider);
082          kmf.init(keystore, password);
083          KeyManager kms[] = kmf.getKeyManagers();
084    
085          /*
086           * Iterate over the returned keymanagers, look for an instance
087           * of X509KeyManager. If found, use that as our "default" key
088           * manager.
089           */
090          for (int i = 0; i < kms.length; i++)
091          {
092            if (kms[i] instanceof X509KeyManager)
093            {
094              sunJSSEX509KeyManager = (X509KeyManager) kms[i];
095              break;
096            }
097          }
098    
099        }
100        catch (NoSuchAlgorithmException e)
101        {
102          // Nothing to do. Maybe we should avoid this and be strict, but we are
103          // in a best effor mode.
104          LOG.log(Level.WARNING, "Error with the algorithm", e);
105        }
106        catch (NoSuchProviderException e)
107        {
108          // Nothing to do. Maybe we should avoid this and be strict, but we are
109          // in a best effor mode.
110          LOG.log(Level.WARNING, "Error with the provider", e);
111        }
112        catch (KeyStoreException e)
113        {
114          // Nothing to do. Maybe we should avoid this and be strict, but we are
115          // in a best effor mode..
116          LOG.log(Level.WARNING, "Error with the keystore", e);
117        }
118        catch (UnrecoverableKeyException e)
119        {
120          // Nothing to do. Maybe we should avoid this and be strict, but we are
121          // in a best effor mode.
122          LOG.log(Level.WARNING, "Error with the key", e);
123        }
124      }
125    
126    
127      /**
128       * Choose an alias to authenticate the client side of a secure
129       * socket given the public key type and the list of certificate
130       * issuer authorities recognized by the peer (if any).
131       *
132       * @param keyType
133       *          the key algorithm type name(s), ordered with the
134       *          most-preferred key type first.
135       * @param issuers
136       *          the list of acceptable CA issuer subject names or null
137       *          if it does not matter which issuers are used.
138       * @param socket
139       *          the socket to be used for this connection. This
140       *          parameter can be null, in which case this method will
141       *          return the most generic alias to use.
142       * @return the alias name for the desired key, or null if there are
143       *         no matches.
144       */
145      public String chooseClientAlias(String[] keyType, Principal[] issuers,
146          Socket socket)
147      {
148        if (sunJSSEX509KeyManager != null)
149        {
150          return sunJSSEX509KeyManager.chooseClientAlias(keyType, issuers, socket);
151        }
152        else
153        {
154          return null ;
155        }
156      }
157    
158      /**
159       * Choose an alias to authenticate the client side of a secure
160       * socket given the public key type and the list of certificate
161       * issuer authorities recognized by the peer (if any).
162       *
163       * @param keyType
164       *          the key algorithm type name(s), ordered with the
165       *          most-preferred key type first.
166       * @param issuers
167       *          the list of acceptable CA issuer subject names or null
168       *          if it does not matter which issuers are used.
169       * @param socket
170       *          the socket to be used for this connection. This
171       *          parameter can be null, in which case this method will
172       *          return the most generic alias to use.
173       * @return the alias name for the desired key, or null if there are
174       *         no matches.
175       */
176      public String chooseServerAlias(String keyType, Principal[] issuers,
177          Socket socket)
178      {
179        if (sunJSSEX509KeyManager != null)
180        {
181          return sunJSSEX509KeyManager.chooseServerAlias(keyType, issuers, socket);
182        }
183        else
184        {
185          return null;
186        }
187      }
188    
189      /**
190       * Returns the certificate chain associated with the given alias.
191       *
192       * @param alias
193       *          the alias name
194       * @return the certificate chain (ordered with the user's
195       *         certificate first and the root certificate authority
196       *         last), or null if the alias can't be found.
197       */
198      public X509Certificate[] getCertificateChain(String alias)
199      {
200        if (sunJSSEX509KeyManager != null)
201        {
202          return sunJSSEX509KeyManager.getCertificateChain(alias);
203        }
204        else
205        {
206          return null;
207        }
208      }
209    
210      /**
211       * Get the matching aliases for authenticating the server side of a
212       * secure socket given the public key type and the list of
213       * certificate issuer authorities recognized by the peer (if any).
214       *
215       * @param keyType
216       *          the key algorithm type name
217       * @param issuers
218       *          the list of acceptable CA issuer subject names or null
219       *          if it does not matter which issuers are used.
220       * @return an array of the matching alias names, or null if there
221       *         were no matches.
222       */
223      public String[] getClientAliases(String keyType, Principal[] issuers)
224      {
225        if (sunJSSEX509KeyManager != null)
226        {
227          return sunJSSEX509KeyManager.getClientAliases(keyType, issuers);
228        }
229        else
230        {
231          return null;
232        }
233      }
234    
235      /**
236       * Returns the key associated with the given alias.
237       *
238       * @param alias
239       *          the alias name
240       * @return the requested key, or null if the alias can't be found.
241       */
242      public PrivateKey getPrivateKey(String alias)
243      {
244        if (sunJSSEX509KeyManager != null)
245        {
246          return sunJSSEX509KeyManager.getPrivateKey(alias);
247        }
248        else
249        {
250          return null;
251        }
252      }
253    
254      /**
255       * Get the matching aliases for authenticating the server side of a
256       * secure socket given the public key type and the list of
257       * certificate issuer authorities recognized by the peer (if any).
258       *
259       * @param keyType
260       *          the key algorithm type name
261       * @param issuers
262       *          the list of acceptable CA issuer subject names or null
263       *          if it does not matter which issuers are used.
264       * @return an array of the matching alias names, or null if there
265       *         were no matches.
266       */
267      public String[] getServerAliases(String keyType, Principal[] issuers)
268      {
269        if (sunJSSEX509KeyManager != null)
270        {
271          return sunJSSEX509KeyManager.getServerAliases(keyType, issuers);
272        }
273        else
274        {
275          return null;
276        }
277      }
278    }