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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS.
026 */
027package org.opends.server.util;
028
029import org.forgerock.i18n.slf4j.LocalizedLogger;
030
031import java.net.Socket;
032import java.security.Principal;
033import java.security.PrivateKey;
034import java.security.cert.X509Certificate;
035import java.util.Arrays;
036import java.util.SortedSet;
037import javax.net.ssl.KeyManager;
038import javax.net.ssl.SSLEngine;
039import javax.net.ssl.X509ExtendedKeyManager;
040import javax.net.ssl.X509KeyManager;
041
042import static org.opends.messages.ExtensionMessages.INFO_MISSING_KEY_TYPE_IN_ALIASES;
043
044/**
045 * This class implements an X.509 key manager that will be used to wrap an
046 * existing key manager and makes it possible to configure which certificate(s)
047 * should be used for client and/or server operations.  The certificate
048 * selection will be based on the alias (also called the nickname) of the
049 * certificate.
050 */
051@org.opends.server.types.PublicAPI(
052     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
053     mayInstantiate=true,
054     mayExtend=false,
055     mayInvoke=true)
056public final class SelectableCertificateKeyManager
057       extends X509ExtendedKeyManager
058{
059  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
060
061  /** The aliases of the certificates that should be selected from the key manager. */
062  private final SortedSet<String> aliases;
063
064  /** The key manager that is wrapped by this key manager. */
065  private final X509KeyManager keyManager;
066
067  /** Provide additional troubleshooting aid to localize a misconfigured SSL connection. */
068  private final String componentName;
069
070  private SelectableCertificateKeyManager(X509KeyManager keyManager, SortedSet<String> aliases, String componentName)
071  {
072    super();
073    this.keyManager = keyManager;
074    this.aliases = aliases;
075    this.componentName = componentName;
076  }
077
078  private SelectableCertificateKeyManager(X509KeyManager keyManager, String alias)
079  {
080    super();
081    this.keyManager = keyManager;
082    this.aliases = CollectionUtils.newTreeSet(alias);
083    this.componentName = "[unkown]";
084  }
085
086  /**
087   * Chooses the alias of the client certificate that should be used based on
088   * the provided criteria.  This will either return the preferred alias
089   * configured for this key manager, or {@code null} if no client certificate
090   * with that alias is configured in the underlying key manager.
091   *
092   * @param  keyType  The set of key algorithm names, ordered with the most
093   *                  preferred key type first.
094   * @param  issuers  The list of acceptable issuer subject names, or
095   *                  {@code null} if any issuer may be used.
096   * @param  socket   The socket to be used for this connection.
097   *
098   * @return  The alias configured for this key manager, or {@code null} if no
099   *          such client certificate is available with that alias.
100   */
101  @Override
102  public String chooseClientAlias(String[] keyType, Principal[] issuers,
103                                  Socket socket)
104  {
105    return findClientAlias(keyType, issuers);
106  }
107
108  private String findClientAlias(String keyType[], Principal[] issuers)
109  {
110    for(String type : keyType)
111    {
112      final String clientAlias = findAlias(keyManager.getClientAliases(type, issuers));
113      if ( clientAlias != null )
114      {
115        return clientAlias;
116      }
117    }
118    logger.debug(INFO_MISSING_KEY_TYPE_IN_ALIASES, componentName, aliases.toString(), Arrays.toString(keyType));
119    return null;
120  }
121
122  private String findAlias(String[] candidates)
123  {
124    if (candidates == null)
125    {
126      return null;
127    }
128    for (String alias : candidates)
129    {
130      for (String certificateAlias : aliases)
131      {
132        if (certificateAlias.equalsIgnoreCase(alias))
133        {
134          return alias;
135        }
136      }
137    }
138    return null;
139  }
140
141  /**
142   * Chooses the alias of the client certificate that should be used based on
143   * the provided criteria.  This will either return the preferred alias
144   * configured for this key manager, or {@code null} if no client certificate
145   * with that alias is configured in the underlying key manager.
146   *
147   * @param  keyType  The set of key algorithm names, ordered with the most
148   *                  preferred key type first.
149   * @param  issuers  The list of acceptable issuer subject names, or
150   *                  {@code null} if any issuer may be used.
151   * @param  engine   The SSL engine to be used for this connection.
152   *
153   * @return  The alias configured for this key manager, or {@code null} if no
154   *          such client certificate is available with that alias.
155   */
156  @Override
157  public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
158                                        SSLEngine engine)
159  {
160    return findClientAlias(keyType, issuers);
161  }
162
163  /**
164   * Chooses the alias of the server certificate that should be used based on
165   * the provided criteria.  This will either return the preferred alias
166   * configured for this key manager, or {@code null} if no server certificate
167   * with that alias is configured in the underlying key manager.
168   *
169   * @param  keyType  The public key type for the certificate.
170   * @param  issuers  The list of acceptable issuer subject names, or
171   *                  {@code null} if any issuer may be used.
172   * @param  socket   The socket to be used for this connection.
173   *
174   * @return  The alias configured for this key manager, or {@code null} if no
175   *          such server certificate is available with that alias.
176   */
177  @Override
178  public String chooseServerAlias(String keyType, Principal[] issuers,
179                                  Socket socket)
180  {
181    return findServerAlias(new String[] { keyType }, issuers);
182  }
183
184  private String findServerAlias(String keyType[], Principal[] issuers)
185  {
186    for (String type : keyType)
187    {
188      final String serverAlias = findAlias(keyManager.getServerAliases(type, issuers));
189      if (serverAlias != null)
190      {
191        return serverAlias;
192      }
193    }
194    logger.debug(INFO_MISSING_KEY_TYPE_IN_ALIASES, componentName, aliases.toString(), Arrays.toString(keyType));
195    return null;
196  }
197
198  /**
199   * Chooses the alias of the server certificate that should be used based on
200   * the provided criteria.  This will either return the preferred alias
201   * configured for this key manager, or {@code null} if no server certificate
202   * with that alias is configured in the underlying key manager.
203   * Note that the returned alias can be transformed in lowercase, depending
204   * on the KeyStore implementation. It is recommended not to use aliases in a
205   * KeyStore that only differ in case.
206   *
207   * @param  keyType  The public key type for the certificate.
208   * @param  issuers  The list of acceptable issuer subject names, or
209   *                  {@code null} if any issuer may be used.
210   * @param  engine   The SSL engine to be used for this connection.
211   *
212   * @return  The alias configured for this key manager, or {@code null} if no
213   *          such server certificate is available with that alias.
214   */
215  @Override
216  public String chooseEngineServerAlias(String keyType, Principal[] issuers,
217                                        SSLEngine engine)
218  {
219    return findServerAlias(new String[] { keyType }, issuers);
220   }
221
222  /**
223   * Retrieves the certificate chain for the provided alias.
224   *
225   * @param  alias  The alias for the certificate chain to retrieve.
226   *
227   * @return  The certificate chain for the provided alias, or {@code null} if
228   *          no certificate is associated with the provided alias.
229   */
230  @Override
231  public X509Certificate[] getCertificateChain(String alias)
232  {
233    return keyManager.getCertificateChain(alias);
234  }
235
236  /**
237   * Retrieves the set of certificate aliases that may be used for client
238   * authentication with the given public key type and set of issuers.
239   *
240   * @param  keyType  The public key type for the aliases to retrieve.
241   * @param  issuers  The list of acceptable issuer subject names, or
242   *                  {@code null} if any issuer may be used.
243   *
244   * @return  The set of certificate aliases that may be used for client
245   *          authentication with the given public key type and set of issuers,
246   *          or {@code null} if there were none.
247   */
248  @Override
249  public String[] getClientAliases(String keyType, Principal[] issuers)
250  {
251    return keyManager.getClientAliases(keyType, issuers);
252  }
253
254  /**
255   * Retrieves the private key for the provided alias.
256   *
257   * @param  alias  The alias for the private key to return.
258   *
259   * @return  The private key for the provided alias, or {@code null} if no
260   *          private key is available for the provided alias.
261   */
262  @Override
263  public PrivateKey getPrivateKey(String alias)
264  {
265    return keyManager.getPrivateKey(alias);
266  }
267
268  /**
269   * Retrieves the set of certificate aliases that may be used for server
270   * authentication with the given public key type and set of issuers.
271   *
272   * @param  keyType  The public key type for the aliases to retrieve.
273   * @param  issuers  The list of acceptable issuer subject names, or
274   *                  {@code null} if any issuer may be used.
275   *
276   * @return  The set of certificate aliases that may be used for server
277   *          authentication with the given public key type and set of issuers,
278   *          or {@code null} if there were none.
279   */
280  @Override
281  public String[] getServerAliases(String keyType, Principal[] issuers)
282  {
283    return keyManager.getServerAliases(keyType, issuers);
284  }
285
286  /**
287   * Wraps the provided set of key managers in selectable certificate key
288   * managers using the provided alias.
289   *
290   * @param  keyManagers      The set of key managers to be wrapped.
291   * @param  aliases          The aliases to use for selecting the desired
292   *                          certificate.
293   * @param  componentName    Name of the component to which is associated this key manager
294   *
295   * @return  A key manager array
296   */
297  public static KeyManager[] wrap(KeyManager[] keyManagers,
298                                  SortedSet<String> aliases, String componentName)
299  {
300    final KeyManager[] newKeyManagers = new KeyManager[keyManagers.length];
301    for (int i=0; i < keyManagers.length; i++)
302    {
303      newKeyManagers[i] = new SelectableCertificateKeyManager(
304                                   (X509KeyManager) keyManagers[i], aliases, componentName);
305    }
306
307    return newKeyManagers;
308  }
309
310  /**
311   * Wraps the provided set of key managers in selectable certificate key
312   * managers using the provided alias.
313   *
314   * @param  keyManagers      The set of key managers to be wrapped.
315   * @param  aliases            The aliases to use for selecting the desired
316   *                          certificate.
317   *
318   * @return  A key manager array
319   */
320  public static KeyManager[] wrap(KeyManager[] keyManagers, SortedSet<String> aliases) {
321    return wrap(keyManagers, aliases, "[unknown]");
322  }
323}