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