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