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