001/*
002 * Copyright 2008-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2008-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util.ssl;
037
038
039
040import java.net.Socket;
041import java.security.Principal;
042import java.security.PrivateKey;
043import java.security.cert.X509Certificate;
044import java.util.Arrays;
045import java.util.LinkedHashSet;
046import javax.net.ssl.KeyManager;
047import javax.net.ssl.SSLEngine;
048import javax.net.ssl.X509ExtendedKeyManager;
049import javax.net.ssl.X509KeyManager;
050
051import com.unboundid.util.NotExtensible;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.StaticUtils;
055import com.unboundid.util.ThreadSafety;
056import com.unboundid.util.ThreadSafetyLevel;
057
058
059
060/**
061 * This class provides an SSL key manager that may be used to wrap a provided
062 * set of key managers.  It provides the ability to select the desired
063 * certificate based on a given nickname.
064 */
065@NotExtensible()
066@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
067public abstract class WrapperKeyManager
068       extends X509ExtendedKeyManager
069{
070  // The nickname of the certificate that should be selected.
071  @Nullable private final String certificateAlias;
072
073  // The set of key managers that will be used to perform the processing.
074  @NotNull private final X509KeyManager[] keyManagers;
075
076
077
078  /**
079   * Creates a new instance of this wrapper key manager with the provided
080   * information.
081   *
082   * @param  keyManagers       The set of key managers to be wrapped.  It must
083   *                           not be {@code null} or empty, and it must contain
084   *                           only X509KeyManager instances.
085   * @param  certificateAlias  The nickname of the certificate that should be
086   *                           selected.  It may be {@code null} if any
087   *                           acceptable certificate found may be used.
088   */
089  protected WrapperKeyManager(@NotNull final KeyManager[] keyManagers,
090                              @Nullable final String certificateAlias)
091  {
092    this.certificateAlias = certificateAlias;
093
094    this.keyManagers = new X509KeyManager[keyManagers.length];
095    for (int i=0; i < keyManagers.length; i++)
096    {
097      this.keyManagers[i] = (X509KeyManager) keyManagers[i];
098    }
099  }
100
101
102
103  /**
104   * Creates a new instance of this wrapper key manager with the provided
105   * information.
106   *
107   * @param  keyManagers       The set of key managers to be wrapped.  It must
108   *                           not be {@code null} or empty.
109   * @param  certificateAlias  The nickname of the certificate that should be
110   *                           selected.  It may be {@code null} if any
111   *                           acceptable certificate found may be used.
112   */
113  protected WrapperKeyManager(@NotNull final X509KeyManager[] keyManagers,
114                              @Nullable final String certificateAlias)
115  {
116    this.keyManagers      = keyManagers;
117    this.certificateAlias = certificateAlias;
118  }
119
120
121
122  /**
123   * Retrieves the nickname of the certificate that should be selected.
124   *
125   * @return  The nickname of the certificate that should be selected, or
126   *          {@code null} if any acceptable certificate found in the key store
127   *          may be used.
128   */
129  @Nullable()
130  public String getCertificateAlias()
131  {
132    return certificateAlias;
133  }
134
135
136
137  /**
138   * Retrieves the nicknames of the client certificates of the specified type
139   * contained in the key store.
140   *
141   * @param  keyType  The key algorithm name for which to retrieve the available
142   *                  certificate nicknames.
143   * @param  issuers  The list of acceptable issuer certificate subjects.  It
144   *                  may be {@code null} if any issuer may be used.
145   *
146   * @return  The nicknames of the client certificates, or {@code null} if none
147   *          were found in the key store.
148   */
149  @Override()
150  @Nullable()
151  public final synchronized String[] getClientAliases(
152                                          @NotNull final String keyType,
153                                          @Nullable final Principal[] issuers)
154  {
155    final LinkedHashSet<String> clientAliases =
156         new LinkedHashSet<>(StaticUtils.computeMapCapacity(10));
157
158    for (final X509KeyManager m : keyManagers)
159    {
160      final String[] aliases = m.getClientAliases(keyType, issuers);
161      if (aliases != null)
162      {
163        clientAliases.addAll(Arrays.asList(aliases));
164      }
165    }
166
167    if (clientAliases.isEmpty())
168    {
169      return null;
170    }
171    else
172    {
173      final String[] aliases = new String[clientAliases.size()];
174      return clientAliases.toArray(aliases);
175    }
176  }
177
178
179
180  /**
181   * Retrieves the nickname of the certificate that a client should use to
182   * authenticate to a server.
183   *
184   * @param  keyType  The list of key algorithm names that may be used.
185   * @param  issuers  The list of acceptable issuer certificate subjects.  It
186   *                  may be {@code null} if any issuer may be used.
187   * @param  socket   The socket to be used.  It may be {@code null} if the
188   *                  certificate may be for any socket.
189   *
190   * @return  The nickname of the certificate to use, or {@code null} if no
191   *          appropriate certificate is found.
192   */
193  @Override()
194  @Nullable()
195  public final synchronized String chooseClientAlias(
196                                        @NotNull final String[] keyType,
197                                        @Nullable final Principal[] issuers,
198                                        @Nullable final Socket socket)
199  {
200    if (certificateAlias == null)
201    {
202      for (final X509KeyManager m : keyManagers)
203      {
204        final String alias = m.chooseClientAlias(keyType, issuers, socket);
205        if (alias != null)
206        {
207          return alias;
208        }
209      }
210
211      return null;
212    }
213    else
214    {
215      for (final String s : keyType)
216      {
217        for (final X509KeyManager m : keyManagers)
218        {
219          final String[] aliases = m.getClientAliases(s, issuers);
220          if (aliases != null)
221          {
222            for (final String alias : aliases)
223            {
224              if (alias.equals(certificateAlias))
225              {
226                return certificateAlias;
227              }
228            }
229          }
230        }
231      }
232
233      return null;
234    }
235  }
236
237
238
239  /**
240   * Retrieves the nickname of the certificate that a client should use to
241   * authenticate to a server.
242   *
243   * @param  keyType  The list of key algorithm names that may be used.
244   * @param  issuers  The list of acceptable issuer certificate subjects.  It
245   *                  may be {@code null} if any issuer may be used.
246   * @param  engine   The SSL engine to be used.  It may be {@code null} if the
247   *                  certificate may be for any engine.
248   *
249   * @return  The nickname of the certificate to use, or {@code null} if no
250   *          appropriate certificate is found.
251   */
252  @Override()
253  @Nullable()
254  public final synchronized String chooseEngineClientAlias(
255                                        @NotNull final String[] keyType,
256                                        @Nullable final Principal[] issuers,
257                                        @Nullable final SSLEngine engine)
258  {
259    if (certificateAlias == null)
260    {
261      for (final X509KeyManager m : keyManagers)
262      {
263        if (m instanceof X509ExtendedKeyManager)
264        {
265          final X509ExtendedKeyManager em = (X509ExtendedKeyManager) m;
266          final String alias =
267               em.chooseEngineClientAlias(keyType, issuers, engine);
268          if (alias != null)
269          {
270            return alias;
271          }
272        }
273        else
274        {
275          final String alias = m.chooseClientAlias(keyType, issuers, null);
276          if (alias != null)
277          {
278            return alias;
279          }
280        }
281      }
282
283      return null;
284    }
285    else
286    {
287      for (final String s : keyType)
288      {
289        for (final X509KeyManager m : keyManagers)
290        {
291          final String[] aliases = m.getClientAliases(s, issuers);
292          if (aliases != null)
293          {
294            for (final String alias : aliases)
295            {
296              if (alias.equals(certificateAlias))
297              {
298                return certificateAlias;
299              }
300            }
301          }
302        }
303      }
304
305      return null;
306    }
307  }
308
309
310
311  /**
312   * Retrieves the nicknames of the server certificates of the specified type
313   * contained in the key store.
314   *
315   * @param  keyType  The key algorithm name for which to retrieve the available
316   *                  certificate nicknames.
317   * @param  issuers  The list of acceptable issuer certificate subjects.  It
318   *                  may be {@code null} if any issuer may be used.
319   *
320   * @return  The nicknames of the server certificates, or {@code null} if none
321   *          were found in the key store.
322   */
323  @Override()
324  @Nullable()
325  public final synchronized String[] getServerAliases(
326                                          @NotNull final String keyType,
327                                          @Nullable final Principal[] issuers)
328  {
329    final LinkedHashSet<String> serverAliases =
330         new LinkedHashSet<>(StaticUtils.computeMapCapacity(10));
331
332    for (final X509KeyManager m : keyManagers)
333    {
334      final String[] aliases = m.getServerAliases(keyType, issuers);
335      if (aliases != null)
336      {
337        serverAliases.addAll(Arrays.asList(aliases));
338      }
339    }
340
341    if (serverAliases.isEmpty())
342    {
343      return null;
344    }
345    else
346    {
347      final String[] aliases = new String[serverAliases.size()];
348      return serverAliases.toArray(aliases);
349    }
350  }
351
352
353
354  /**
355   * Retrieves the nickname of the certificate that a server should use to
356   * authenticate to a client.
357   *
358   * @param  keyType  The key algorithm name that may be used.
359   * @param  issuers  The list of acceptable issuer certificate subjects.  It
360   *                  may be {@code null} if any issuer may be used.
361   * @param  socket   The socket to be used.  It may be {@code null} if the
362   *                  certificate may be for any socket.
363   *
364   * @return  The nickname of the certificate to use, or {@code null} if no
365   *          appropriate certificate is found.
366   */
367  @Override()
368  @Nullable()
369  public final synchronized String chooseServerAlias(
370                                        @NotNull final String keyType,
371                                        @Nullable final Principal[] issuers,
372                                        @Nullable final Socket socket)
373  {
374    if (certificateAlias == null)
375    {
376      for (final X509KeyManager m : keyManagers)
377      {
378        final String alias = m.chooseServerAlias(keyType, issuers, socket);
379        if (alias != null)
380        {
381          return alias;
382        }
383      }
384
385      return null;
386    }
387    else
388    {
389      for (final X509KeyManager m : keyManagers)
390      {
391        final String[] aliases = m.getServerAliases(keyType, issuers);
392        if (aliases != null)
393        {
394          for (final String alias : aliases)
395          {
396            if (alias.equals(certificateAlias))
397            {
398              return certificateAlias;
399            }
400          }
401        }
402      }
403
404      return null;
405    }
406  }
407
408
409
410  /**
411   * Retrieves the nickname of the certificate that a server should use to
412   * authenticate to a client.
413   *
414   * @param  keyType  The key algorithm name that may be used.
415   * @param  issuers  The list of acceptable issuer certificate subjects.  It
416   *                  may be {@code null} if any issuer may be used.
417   * @param  engine   The SSL engine to be used.  It may be {@code null} if the
418   *                  certificate may be for any engine.
419   *
420   * @return  The nickname of the certificate to use, or {@code null} if no
421   *          appropriate certificate is found.
422   */
423  @Override()
424  @Nullable()
425  public final synchronized String chooseEngineServerAlias(
426                                        @NotNull final String keyType,
427                                        @Nullable final Principal[] issuers,
428                                        @Nullable final SSLEngine engine)
429  {
430    if (certificateAlias == null)
431    {
432      for (final X509KeyManager m : keyManagers)
433      {
434        if (m instanceof X509ExtendedKeyManager)
435        {
436          final X509ExtendedKeyManager em = (X509ExtendedKeyManager) m;
437          final String alias =
438               em.chooseEngineServerAlias(keyType, issuers, engine);
439          if (alias != null)
440          {
441            return alias;
442          }
443        }
444        else
445        {
446          final String alias = m.chooseServerAlias(keyType, issuers, null);
447          if (alias != null)
448          {
449            return alias;
450          }
451        }
452      }
453
454      return null;
455    }
456    else
457    {
458      for (final X509KeyManager m : keyManagers)
459      {
460        final String[] aliases = m.getServerAliases(keyType, issuers);
461        if (aliases != null)
462        {
463          for (final String alias : aliases)
464          {
465            if (alias.equals(certificateAlias))
466            {
467              return certificateAlias;
468            }
469          }
470        }
471      }
472
473      return null;
474    }
475  }
476
477
478
479  /**
480   * Retrieves the certificate chain for the certificate with the given
481   * nickname.
482   *
483   * @param  alias  The nickname of the certificate for which to retrieve the
484   *                certificate chain.
485   *
486   * @return  The certificate chain for the certificate with the given nickname,
487   *          or {@code null} if the requested certificate cannot be found.
488   */
489  @Override()
490  @Nullable()
491  public final synchronized X509Certificate[] getCertificateChain(
492                                                   @NotNull  final String alias)
493  {
494    for (final X509KeyManager m : keyManagers)
495    {
496      final X509Certificate[] chain = m.getCertificateChain(alias);
497      if (chain != null)
498      {
499        return chain;
500      }
501    }
502
503    return null;
504  }
505
506
507
508  /**
509   * Retrieves the private key for the specified certificate.
510   *
511   * @param  alias  The nickname of the certificate for which to retrieve the
512   *                private key.
513   *
514   * @return  The private key for the requested certificate, or {@code null} if
515   *          the requested certificate cannot be found.
516   */
517  @Override()
518  @Nullable()
519  public final synchronized PrivateKey getPrivateKey(
520                                            @NotNull final String alias)
521  {
522    for (final X509KeyManager m : keyManagers)
523    {
524      final PrivateKey key = m.getPrivateKey(alias);
525      if (key != null)
526      {
527        return key;
528      }
529    }
530
531    return null;
532  }
533}