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-2016 ForgeRock AS.
017 */
018package org.opends.admin.ads.util;
019
020import java.net.Socket;
021import java.security.KeyStore;
022import java.security.KeyStoreException;
023import java.security.NoSuchAlgorithmException;
024import java.security.NoSuchProviderException;
025import java.security.Principal;
026import java.security.PrivateKey;
027import java.security.UnrecoverableKeyException;
028import java.security.cert.X509Certificate;
029
030import javax.net.ssl.KeyManager;
031import javax.net.ssl.KeyManagerFactory;
032import javax.net.ssl.TrustManagerFactory;
033import javax.net.ssl.X509KeyManager;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.forgerock.i18n.slf4j.LocalizedLogger;
037import org.opends.server.util.Platform;
038
039/**
040 * This class is in charge of checking whether the certificates that are
041 * presented are trusted or not.
042 * This implementation tries to check also that the subject DN of the
043 * certificate corresponds to the host passed using the setHostName method.
044 *
045 * The constructor tries to use a default TrustManager from the system and if
046 * it cannot be retrieved this class will only accept the certificates
047 * explicitly accepted by the user (and specified by calling acceptCertificate).
048 *
049 * NOTE: this class is not aimed to be used when we have connections in parallel.
050 */
051public class ApplicationKeyManager implements X509KeyManager
052{
053  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
054
055  /** The default keyManager. */
056  private X509KeyManager keyManager;
057
058  /**
059   * The default constructor.
060   * @param keystore The keystore to use for this keymanager.
061   * @param password The keystore password to use for this keymanager.
062   */
063  public ApplicationKeyManager(KeyStore keystore, char[] password)
064  {
065    KeyManagerFactory kmf = null;
066    String userSpecifiedAlgo =
067      System.getProperty("org.opends.admin.keymanageralgo");
068    String userSpecifiedProvider =
069      System.getProperty("org.opends.admin.keymanagerprovider");
070
071    //Handle IBM specific cases if the user did not specify a algorithm and/or
072    //provider.
073    if(userSpecifiedAlgo == null && Platform.isVendor("IBM"))
074    {
075      userSpecifiedAlgo = "IbmX509";
076    }
077    if(userSpecifiedProvider == null && Platform.isVendor("IBM"))
078    {
079      userSpecifiedProvider = "IBMJSSE2";
080    }
081
082    // Have some fallbacks to choose the provider and algorith of the key
083    // manager.  First see if the user wanted to use something specific,
084    // then try with the SunJSSE provider and SunX509 algorithm. Finally,
085    // fallback to the default algorithm of the JVM.
086    String[] preferredProvider =
087        { userSpecifiedProvider, "SunJSSE", null, null };
088    String[] preferredAlgo =
089        { userSpecifiedAlgo, "SunX509", "SunX509",
090          TrustManagerFactory.getDefaultAlgorithm() };
091
092    for (int i=0; i<preferredProvider.length && keyManager == null; i++)
093    {
094      String provider = preferredProvider[i];
095      String algo = preferredAlgo[i];
096      if (algo == null)
097      {
098        continue;
099      }
100      try
101      {
102        if (provider != null)
103        {
104          kmf = KeyManagerFactory.getInstance(algo, provider);
105        }
106        else
107        {
108          kmf = KeyManagerFactory.getInstance(algo);
109        }
110        kmf.init(keystore, password);
111        KeyManager kms[] = kmf.getKeyManagers();
112        /*
113         * Iterate over the returned keymanagers, look for an instance
114         * of X509KeyManager. If found, use that as our "default" key manager.
115         */
116        for (KeyManager km : kms)
117        {
118          if (kms[i] instanceof X509KeyManager)
119          {
120            keyManager = (X509KeyManager) km;
121            break;
122          }
123        }
124      }
125      catch (NoSuchAlgorithmException e)
126      {
127        // Nothing to do. Maybe we should avoid this and be strict, but we are
128        // in a best effort mode.
129        logger.warn(LocalizableMessage.raw("Error with the algorithm", e));
130      }
131      catch (KeyStoreException 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 keystore", e));
136      }
137      catch (UnrecoverableKeyException 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 key", e));
142      }
143      catch (NoSuchProviderException 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 provider", e));
148      }
149    }
150  }
151
152  @Override
153  public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
154  {
155    return keyManager != null ? keyManager.chooseClientAlias(keyType, issuers, socket) : null;
156  }
157
158  @Override
159  public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
160  {
161    return keyManager != null ? keyManager.chooseServerAlias(keyType, issuers, socket) : null;
162  }
163
164  @Override
165  public X509Certificate[] getCertificateChain(String alias)
166  {
167    return keyManager != null ? keyManager.getCertificateChain(alias) : null;
168  }
169
170  @Override
171  public String[] getClientAliases(String keyType, Principal[] issuers)
172  {
173    return keyManager != null ? keyManager.getClientAliases(keyType, issuers) : null;
174  }
175
176  @Override
177  public PrivateKey getPrivateKey(String alias)
178  {
179    return keyManager != null ? keyManager.getPrivateKey(alias) : null;
180  }
181
182  @Override
183  public String[] getServerAliases(String keyType, Principal[] issuers)
184  {
185    return keyManager != null ? keyManager.getServerAliases(keyType, issuers) : null;
186  }
187}