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 2014-2015 ForgeRock AS
026 */
027package org.opends.server.tools;
028import org.forgerock.i18n.LocalizableMessage;
029
030
031
032import java.io.BufferedReader;
033import java.io.InputStreamReader;
034import java.io.IOException;
035import java.security.cert.CertificateException;
036import java.security.cert.X509Certificate;
037import java.util.Date;
038import javax.net.ssl.TrustManager;
039import javax.net.ssl.X509TrustManager;
040
041import static org.opends.messages.ToolMessages.*;
042
043
044
045/**
046 * This class provides an implementation of an X.509 trust manager which will
047 * interactively prompt the user (via the CLI) whether a given certificate
048 * should be trusted.  It should only be used by interactive command-line tools,
049 * since it will block until it gets a response from the user.
050 * <BR><BR>
051 * Note that this class is only intended for client-side use, and therefore may
052 * not be used by a server to determine whether a client certificate is trusted.
053 */
054public class PromptTrustManager
055       implements X509TrustManager
056{
057
058
059
060  /** The singleton trust manager array for this class. */
061  private static TrustManager[] trustManagerArray =
062       new TrustManager[] { new PromptTrustManager() };
063
064
065
066  /**
067   * Creates a new instance of this prompt trust manager.
068   */
069  private PromptTrustManager()
070  {
071    // No implementation is required.
072  }
073
074
075
076  /**
077   * Retrieves the trust manager array that should be used to initialize an SSL
078   * context in cases where the user should be interactively prompted about
079   * whether to trust the server certificate.
080   *
081   * @return  The trust manager array that should be used to initialize an SSL
082   *          context in cases where the user should be interactively prompted
083   *          about whether to trust the server certificate.
084   */
085  public static TrustManager[] getTrustManagers()
086  {
087    return trustManagerArray;
088  }
089
090
091
092  /**
093   * Determines whether an SSL client with the provided certificate chain should
094   * be trusted.  This implementation is not intended for server-side use, and
095   * therefore this method will always throw an exception.
096   *
097   * @param  chain     The certificate chain for the SSL client.
098   * @param  authType  The authentication type based on the client certificate.
099   *
100   * @throws  CertificateException  To indicate that the provided client
101   *                                certificate is not trusted.
102   */
103  public void checkClientTrusted(X509Certificate[] chain, String authType)
104         throws CertificateException
105  {
106    LocalizableMessage message = ERR_PROMPTTM_REJECTING_CLIENT_CERT.get();
107    throw new CertificateException(message.toString());
108  }
109
110
111
112  /**
113   * Determines whether an SSL server with the provided certificate chain should
114   * be trusted.  In this case, the user will be interactively prompted as to
115   * whether the certificate should be trusted.
116   *
117   * @param  chain     The certificate chain for the SSL server.
118   * @param  authType  The key exchange algorithm used.
119   *
120   * @throws  CertificateException  If the user rejects the certificate.
121   */
122  public void checkServerTrusted(X509Certificate[] chain, String authType)
123         throws CertificateException
124  {
125    if (chain == null || chain.length == 0)
126    {
127      System.out.println(WARN_PROMPTTM_NO_SERVER_CERT_CHAIN.get());
128    }
129    else
130    {
131      Date currentDate   = new Date();
132      Date notAfterDate  = chain[0].getNotAfter();
133      Date notBeforeDate = chain[0].getNotBefore();
134
135      if (currentDate.after(notAfterDate))
136      {
137        System.err.println(WARN_PROMPTTM_CERT_EXPIRED.get(notAfterDate));
138      }
139      else if (currentDate.before(notBeforeDate))
140      {
141        System.err.println(WARN_PROMPTTM_CERT_NOT_YET_VALID.get(notBeforeDate));
142      }
143
144      System.out.println(INFO_PROMPTTM_SERVER_CERT.get(
145              chain[0].getSubjectDN().getName(),
146              chain[0].getIssuerDN().getName(),
147              notBeforeDate,
148              notAfterDate));
149    }
150
151
152    LocalizableMessage prompt = INFO_PROMPTTM_YESNO_PROMPT.get();
153    BufferedReader reader =
154         new BufferedReader(new InputStreamReader(System.in));
155    while (true)
156    {
157      try
158      {
159        System.out.print(prompt);
160        String line = reader.readLine().toLowerCase();
161        if (line.equalsIgnoreCase(
162            INFO_PROMPT_YES_COMPLETE_ANSWER.get().toString()) ||
163            line.equalsIgnoreCase(
164            INFO_PROMPT_YES_FIRST_LETTER_ANSWER.get().toString()))
165        {
166          // Returning without an exception is sufficient to consider the
167          // certificate trusted.
168          return;
169        }
170        if (line.equalsIgnoreCase(
171            INFO_PROMPT_NO_COMPLETE_ANSWER.get().toString()) ||
172            line.equalsIgnoreCase(
173            INFO_PROMPT_NO_FIRST_LETTER_ANSWER.get().toString()))
174        {
175          LocalizableMessage message = ERR_PROMPTTM_USER_REJECTED.get();
176          throw new CertificateException(message.toString());
177        }
178      } catch (IOException ioe) {}
179
180      System.out.println();
181    }
182  }
183
184
185
186  /**
187   * Retrieves the set of certificate authority certificates which are trusted
188   * for authenticating peers.
189   *
190   * @return  An empty array, since we don't care what certificates are
191   *          presented because we will always prompt the user.
192   */
193  public X509Certificate[] getAcceptedIssuers()
194  {
195    return new X509Certificate[0];
196  }
197}
198