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