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 2013-2015 ForgeRock AS
026
027 */
028
029package org.opends.quicksetup.installer.ui;
030
031import java.awt.Component;
032import java.awt.GridBagConstraints;
033import java.awt.GridBagLayout;
034import java.awt.Insets;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.awt.event.WindowAdapter;
038import java.awt.event.WindowEvent;
039import java.io.File;
040import java.security.KeyStoreException;
041import java.util.ArrayList;
042
043import javax.swing.Box;
044import javax.swing.ButtonGroup;
045import javax.swing.JButton;
046import javax.swing.JCheckBox;
047import javax.swing.JComponent;
048import javax.swing.JDialog;
049import javax.swing.JFrame;
050import javax.swing.JLabel;
051import javax.swing.JPanel;
052import javax.swing.JPasswordField;
053import javax.swing.JRadioButton;
054import javax.swing.JTextField;
055import javax.swing.SwingUtilities;
056import javax.swing.text.JTextComponent;
057
058import org.opends.quicksetup.SecurityOptions;
059import org.opends.quicksetup.event.BrowseActionListener;
060import org.opends.quicksetup.event.MinimumSizeComponentListener;
061import org.opends.quicksetup.installer.Installer;
062import org.opends.quicksetup.ui.UIFactory;
063import org.opends.quicksetup.ui.Utilities;
064import org.opends.quicksetup.util.BackgroundTask;
065import org.opends.quicksetup.util.Utils;
066import org.opends.server.util.CertificateManager;
067import org.opends.server.util.StaticUtils;
068import org.forgerock.i18n.LocalizableMessage;
069
070import static org.opends.messages.QuickSetupMessages.*;
071import static com.forgerock.opendj.cli.Utils.getThrowableMsg;
072
073/**
074 * This class is a dialog that appears when the user wants to configure
075 * security parameters for the new OpenDS instance.
076 */
077public class SecurityOptionsDialog extends JDialog
078{
079  private static final long serialVersionUID = 4083707346899442215L;
080
081  private JCheckBox cbEnableSSL;
082  private JCheckBox cbEnableStartTLS;
083  private JTextField tfPort;
084  private JRadioButton rbUseSelfSignedCertificate;
085  private JRadioButton rbUseExistingCertificate;
086  private JLabel lKeystoreType;
087  private JRadioButton rbPKCS11;
088  private JRadioButton rbJKS;
089  private JRadioButton rbJCEKS;
090  private JRadioButton rbPKCS12;
091  private JLabel lKeystorePath;
092  private JTextField tfKeystorePath;
093  private JButton browseButton;
094  private JLabel lKeystorePwd;
095  private JPasswordField tfKeystorePwd;
096
097  private JButton cancelButton;
098  private JButton okButton;
099
100  private SelectAliasDialog aliasDlg;
101
102  private boolean isCanceled = true;
103
104  private SecurityOptions securityOptions;
105
106  private String[] aliases;
107  private boolean certificateHasAlias;
108  private String selectedAlias;
109
110  private final int DEFAULT_PORT = 636;
111
112  /**
113   * Constructor of the SecurityOptionsDialog.
114   * @param parent the parent frame for this dialog.
115   * @param options the SecurityOptions used to populate this dialog.
116   * @throws IllegalArgumentException if options is null.
117   */
118  public SecurityOptionsDialog(JFrame parent, SecurityOptions options)
119  throws IllegalArgumentException
120  {
121    super(parent);
122    setTitle(INFO_SECURITY_OPTIONS_DIALOG_TITLE.get().toString());
123    securityOptions = options;
124    getContentPane().add(createPanel());
125    pack();
126
127    updateContents();
128
129    int minWidth = (int) getPreferredSize().getWidth();
130    int minHeight = (int) getPreferredSize().getHeight();
131    addComponentListener(new MinimumSizeComponentListener(this, minWidth,
132        minHeight));
133    getRootPane().setDefaultButton(okButton);
134
135    addWindowListener(new WindowAdapter()
136    {
137      @Override
138      public void windowClosing(WindowEvent e)
139      {
140        cancelClicked();
141      }
142    });
143    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
144
145    Utilities.centerOnComponent(this, parent);
146  }
147
148  /**
149   * Returns <CODE>true</CODE> if the user clicked on cancel and
150   * <CODE>false</CODE> otherwise.
151   * @return <CODE>true</CODE> if the user clicked on cancel and
152   * <CODE>false</CODE> otherwise.
153   */
154  public boolean isCanceled()
155  {
156    return isCanceled;
157  }
158
159  /**
160   * Displays this dialog and populates its contents with the provided
161   * SecurityOptions object.
162   * @param options the SecurityOptions used to populate this dialog.
163   * @throws IllegalArgumentException if options is null.
164   */
165  public void display(SecurityOptions options) throws IllegalArgumentException
166  {
167    if (options == null)
168    {
169      throw new IllegalArgumentException("options parameter cannot be null.");
170    }
171    UIFactory.setTextStyle(cbEnableSSL,
172        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
173    UIFactory.setTextStyle(lKeystorePath,
174        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
175    UIFactory.setTextStyle(lKeystorePwd,
176        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
177
178    securityOptions = options;
179    updateContents();
180
181    isCanceled = true;
182
183    setVisible(true);
184  }
185
186  /**
187   * Returns the security options object representing the input of the user
188   * in this panel.
189   * @return the security options object representing the input of the user
190   * in this panel.
191   */
192  public SecurityOptions getSecurityOptions()
193  {
194    SecurityOptions ops;
195
196    boolean enableSSL = cbEnableSSL.isSelected();
197    boolean enableStartTLS = cbEnableStartTLS.isSelected();
198    if (enableSSL || enableStartTLS)
199    {
200      int sslPort = -1;
201      try
202      {
203        sslPort = Integer.parseInt(tfPort.getText());
204      }
205      catch (Throwable t)
206      {
207      }
208      if (rbUseSelfSignedCertificate.isSelected())
209      {
210        ops = SecurityOptions.createSelfSignedCertificateOptions(
211            enableSSL, enableStartTLS, sslPort);
212      }
213      else if (rbJKS.isSelected())
214      {
215        ops = SecurityOptions.createJKSCertificateOptions(
216            tfKeystorePath.getText(),
217            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
218            enableStartTLS, sslPort, selectedAlias);
219      }
220      else if (rbJCEKS.isSelected())
221      {
222        ops = SecurityOptions.createJCEKSCertificateOptions(
223            tfKeystorePath.getText(),
224            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
225            enableStartTLS, sslPort, selectedAlias);
226      }
227      else if (rbPKCS11.isSelected())
228      {
229        ops = SecurityOptions.createPKCS11CertificateOptions(
230            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
231            enableStartTLS, sslPort, selectedAlias);
232      }
233      else if (rbPKCS12.isSelected())
234      {
235        ops = SecurityOptions.createPKCS12CertificateOptions(
236            tfKeystorePath.getText(),
237            String.valueOf(tfKeystorePwd.getPassword()), enableSSL,
238            enableStartTLS, sslPort, selectedAlias);
239      }
240      else
241      {
242        throw new IllegalStateException("No certificate options selected.");
243      }
244    }
245    else
246    {
247      ops = SecurityOptions.createNoCertificateOptions();
248    }
249    return ops;
250  }
251
252  /**
253   * Creates and returns the panel of the dialog.
254   * @return the panel of the dialog.
255   */
256  private JPanel createPanel()
257  {
258    GridBagConstraints gbc = new GridBagConstraints();
259
260    JPanel contentPanel = new JPanel(new GridBagLayout());
261    contentPanel.setBackground(UIFactory.DEFAULT_BACKGROUND);
262    gbc.fill = GridBagConstraints.BOTH;
263    gbc.gridwidth = GridBagConstraints.REMAINDER;
264    gbc.weightx = 1.0;
265
266    JPanel topPanel = new JPanel(new GridBagLayout());
267    topPanel.setBorder(UIFactory.DIALOG_PANEL_BORDER);
268    topPanel.setBackground(UIFactory.CURRENT_STEP_PANEL_BACKGROUND);
269    Insets insets = UIFactory.getCurrentStepPanelInsets();
270
271    gbc.weighty = 0.0;
272    insets.bottom = 0;
273    gbc.insets = insets;
274    topPanel.add(createTitlePanel(), gbc);
275    gbc.insets.top = UIFactory.TOP_INSET_INSTRUCTIONS_SUBPANEL;
276    topPanel.add(createInstructionsPane(), gbc);
277    gbc.insets.top = UIFactory.TOP_INSET_INPUT_SUBPANEL;
278    gbc.insets.bottom = UIFactory.TOP_INSET_INPUT_SUBPANEL;
279    topPanel.add(createInputPanel(), gbc);
280    gbc.weighty = 1.0;
281    gbc.insets = UIFactory.getEmptyInsets();
282    topPanel.add(Box.createVerticalGlue(), gbc);
283    contentPanel.add(topPanel, gbc);
284    gbc.weighty = 0.0;
285    gbc.insets = UIFactory.getButtonsPanelInsets();
286    contentPanel.add(createButtonsPanel(), gbc);
287
288    return contentPanel;
289  }
290
291  /**
292   * Creates and returns the title sub panel.
293   * @return the title sub panel.
294   */
295  private Component createTitlePanel()
296  {
297    JPanel titlePanel = new JPanel(new GridBagLayout());
298    GridBagConstraints gbc = new GridBagConstraints();
299    titlePanel.setOpaque(false);
300    gbc.anchor = GridBagConstraints.NORTHWEST;
301    gbc.fill = GridBagConstraints.BOTH;
302    gbc.weightx = 0.0;
303    gbc.gridwidth = GridBagConstraints.RELATIVE;
304
305    LocalizableMessage title = INFO_SECURITY_OPTIONS_TITLE.get();
306    JLabel l =
307        UIFactory.makeJLabel(UIFactory.IconType.NO_ICON, title,
308            UIFactory.TextStyle.TITLE);
309    l.setOpaque(false);
310    titlePanel.add(l, gbc);
311
312    gbc.gridwidth = GridBagConstraints.RELATIVE;
313    gbc.anchor = GridBagConstraints.NORTHWEST;
314    gbc.weightx = 1.0;
315    gbc.gridwidth = GridBagConstraints.REMAINDER;
316    gbc.insets.left = 0;
317    gbc.weightx = 1.0;
318    gbc.gridwidth = GridBagConstraints.REMAINDER;
319    titlePanel.add(Box.createHorizontalGlue(), gbc);
320
321    return titlePanel;
322  }
323
324  /**
325   * Creates and returns the instructions sub panel.
326   * @return the instructions sub panel.
327   */
328  private Component createInstructionsPane()
329  {
330    LocalizableMessage instructions = INFO_SECURITY_OPTIONS_INSTRUCTIONS.get();
331
332    JTextComponent instructionsPane =
333      UIFactory.makeHtmlPane(instructions, UIFactory.INSTRUCTIONS_FONT);
334    instructionsPane.setOpaque(false);
335    instructionsPane.setEditable(false);
336
337    return instructionsPane;
338  }
339
340  /**
341   * Creates and returns the input sub panel: the panel with all the widgets
342   * that are used to define the security options.
343   * @return the input sub panel.
344   */
345  private Component createInputPanel()
346  {
347    JPanel inputPanel = new JPanel(new GridBagLayout());
348    inputPanel.setOpaque(false);
349
350    ActionListener l = new ActionListener()
351    {
352      public void actionPerformed(ActionEvent ev)
353      {
354        updateEnablingState();
355      }
356    };
357
358    cbEnableSSL = UIFactory.makeJCheckBox(INFO_ENABLE_SSL_LABEL.get(),
359        INFO_ENABLE_SSL_TOOLTIP.get(), UIFactory.TextStyle.PRIMARY_FIELD_VALID);
360    cbEnableSSL.addActionListener(l);
361    String sPort = "";
362    int port = securityOptions.getSslPort();
363    if (port > 0)
364    {
365      sPort = String.valueOf(port);
366    }
367    tfPort = UIFactory.makeJTextField(LocalizableMessage.raw(sPort),
368        INFO_SSL_PORT_TEXTFIELD_TOOLTIP.get(), UIFactory.PORT_FIELD_SIZE,
369        UIFactory.TextStyle.TEXTFIELD);
370    cbEnableStartTLS = UIFactory.makeJCheckBox(INFO_ENABLE_STARTTLS_LABEL.get(),
371        INFO_ENABLE_STARTTLS_TOOLTIP.get(),
372        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
373    cbEnableStartTLS.addActionListener(l);
374    rbUseSelfSignedCertificate = UIFactory.makeJRadioButton(
375        INFO_USE_SELF_SIGNED_LABEL.get(),
376        INFO_USE_SELF_SIGNED_TOOLTIP.get(),
377        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
378    rbUseSelfSignedCertificate.addActionListener(l);
379    rbUseExistingCertificate = UIFactory.makeJRadioButton(
380        INFO_USE_EXISTING_CERTIFICATE_LABEL.get(),
381        INFO_USE_EXISTING_CERTIFICATE_TOOLTIP.get(),
382        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
383    rbUseExistingCertificate.addActionListener(l);
384    ButtonGroup group1 = new ButtonGroup();
385    group1.add(rbUseSelfSignedCertificate);
386    group1.add(rbUseExistingCertificate);
387
388    lKeystoreType = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
389        INFO_KEYSTORE_TYPE_LABEL.get(),
390        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
391    lKeystoreType.setOpaque(false);
392    rbJKS = UIFactory.makeJRadioButton(
393        INFO_JKS_CERTIFICATE_LABEL.get(),
394        INFO_JKS_CERTIFICATE_TOOLTIP.get(),
395        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
396    rbJKS.addActionListener(l);
397    rbJCEKS = UIFactory.makeJRadioButton(
398        INFO_JCEKS_CERTIFICATE_LABEL.get(),
399        INFO_JCEKS_CERTIFICATE_TOOLTIP.get(),
400        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
401    rbJCEKS.addActionListener(l);
402    rbPKCS11 = UIFactory.makeJRadioButton(
403        INFO_PKCS11_CERTIFICATE_LABEL.get(),
404        INFO_PKCS11_CERTIFICATE_TOOLTIP.get(),
405        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
406    rbPKCS11.addActionListener(l);
407    rbPKCS12 = UIFactory.makeJRadioButton(
408        INFO_PKCS12_CERTIFICATE_LABEL.get(),
409        INFO_PKCS12_CERTIFICATE_TOOLTIP.get(),
410        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
411    rbPKCS12.addActionListener(l);
412    ButtonGroup group2 = new ButtonGroup();
413    group2.add(rbJKS);
414    group2.add(rbJCEKS);
415    group2.add(rbPKCS11);
416    group2.add(rbPKCS12);
417    lKeystoreType.setLabelFor(rbJKS);
418
419    lKeystorePath = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
420        INFO_KEYSTORE_PATH_LABEL.get(),
421        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
422    lKeystorePath.setOpaque(false);
423    tfKeystorePath = UIFactory.makeJTextField(LocalizableMessage.EMPTY,
424        INFO_KEYSTORE_PATH_TOOLTIP.get(),
425        UIFactory.HOST_FIELD_SIZE, UIFactory.TextStyle.TEXTFIELD);
426    lKeystorePath.setLabelFor(tfKeystorePath);
427    browseButton =
428      UIFactory.makeJButton(INFO_BROWSE_BUTTON_LABEL.get(),
429          INFO_BROWSE_BUTTON_TOOLTIP.get());
430
431    BrowseActionListener browseListener =
432      new BrowseActionListener(tfKeystorePath,
433          BrowseActionListener.BrowseType.GENERIC_FILE,
434          this);
435    browseButton.addActionListener(browseListener);
436
437    lKeystorePwd = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
438        INFO_KEYSTORE_PWD_LABEL.get(),
439        UIFactory.TextStyle.SECONDARY_FIELD_VALID);
440    lKeystorePwd.setOpaque(false);
441    tfKeystorePwd = UIFactory.makeJPasswordField(LocalizableMessage.EMPTY,
442        INFO_KEYSTORE_PWD_TOOLTIP.get(),
443        UIFactory.PASSWORD_FIELD_SIZE, UIFactory.TextStyle.PASSWORD_FIELD);
444    lKeystorePwd.setLabelFor(tfKeystorePwd);
445
446    GridBagConstraints gbc = new GridBagConstraints();
447    gbc.anchor = GridBagConstraints.WEST;
448    gbc.weightx = 0.0;
449    gbc.gridwidth = GridBagConstraints.RELATIVE;
450    gbc.insets = UIFactory.getEmptyInsets();
451    gbc.fill = GridBagConstraints.HORIZONTAL;
452    inputPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
453        INFO_SSL_ACCESS_LABEL.get(), UIFactory.TextStyle.PRIMARY_FIELD_VALID),
454        gbc);
455
456    JPanel auxPanel = new JPanel(new GridBagLayout());
457    auxPanel.setOpaque(false);
458    gbc.gridwidth = 4;
459    gbc.fill = GridBagConstraints.NONE;
460    auxPanel.add(cbEnableSSL, gbc);
461    gbc.gridwidth--;
462    gbc.insets.left = UIFactory.LEFT_INSET_SECONDARY_FIELD;
463    auxPanel.add(tfPort, gbc);
464    gbc.gridwidth = GridBagConstraints.RELATIVE;
465    auxPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
466        getPortHelpMessage(), UIFactory.TextStyle.SECONDARY_FIELD_VALID), gbc);
467    gbc.gridwidth = GridBagConstraints.REMAINDER;
468    gbc.fill = GridBagConstraints.HORIZONTAL;
469    gbc.weightx = 1.0;
470    auxPanel.add(Box.createHorizontalGlue(), gbc);
471
472    gbc.insets.left = UIFactory.LEFT_INSET_PRIMARY_FIELD;
473    gbc.weightx = 1.0;
474    inputPanel.add(auxPanel, gbc);
475
476    gbc.insets = UIFactory.getEmptyInsets();
477    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
478    gbc.gridwidth = GridBagConstraints.RELATIVE;
479    gbc.weightx = 0.0;
480    inputPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
481        INFO_STARTTLS_ACCESS_LABEL.get(),
482        UIFactory.TextStyle.PRIMARY_FIELD_VALID),
483        gbc);
484    auxPanel = new JPanel(new GridBagLayout());
485    auxPanel.setOpaque(false);
486    gbc.gridwidth = GridBagConstraints.RELATIVE;
487    gbc.insets = UIFactory.getEmptyInsets();
488    auxPanel.add(cbEnableStartTLS, gbc);
489    gbc.weightx = 1.0;
490    gbc.gridwidth = GridBagConstraints.REMAINDER;
491    auxPanel.add(Box.createHorizontalGlue(), gbc);
492    gbc.insets.left = UIFactory.LEFT_INSET_PRIMARY_FIELD;
493    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
494    inputPanel.add(auxPanel, gbc);
495
496    gbc.insets = UIFactory.getEmptyInsets();
497    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
498    gbc.anchor = GridBagConstraints.NORTHWEST;
499    gbc.gridwidth = GridBagConstraints.RELATIVE;
500    gbc.weightx = 0.0;
501    JLabel lCertificate = UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
502        INFO_CERTIFICATE_LABEL.get(), UIFactory.TextStyle.PRIMARY_FIELD_VALID);
503    int additionalInset = Math.abs(lCertificate.getPreferredSize().height -
504        rbUseSelfSignedCertificate.getPreferredSize().height) / 2;
505    gbc.insets.top += additionalInset;
506    inputPanel.add(lCertificate, gbc);
507    auxPanel = new JPanel(new GridBagLayout());
508    auxPanel.setOpaque(false);
509    gbc.insets.left = UIFactory.LEFT_INSET_PRIMARY_FIELD;
510    gbc.gridwidth = GridBagConstraints.REMAINDER;
511    gbc.weightx = 1.0;
512    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
513    inputPanel.add(auxPanel, gbc);
514
515    gbc.insets = UIFactory.getEmptyInsets();
516    gbc.anchor = GridBagConstraints.WEST;
517    JPanel aux2Panel = new JPanel(new GridBagLayout());
518    aux2Panel.setOpaque(false);
519    gbc.gridwidth = GridBagConstraints.RELATIVE;
520    aux2Panel.add(rbUseSelfSignedCertificate, gbc);
521    gbc.weightx = 1.0;
522    gbc.gridwidth = GridBagConstraints.REMAINDER;
523    aux2Panel.add(Box.createHorizontalGlue(), gbc);
524    auxPanel.add(aux2Panel, gbc);
525
526    aux2Panel = new JPanel(new GridBagLayout());
527    aux2Panel.setOpaque(false);
528    gbc.gridwidth = GridBagConstraints.RELATIVE;
529    gbc.insets = UIFactory.getEmptyInsets();
530    gbc.weightx = 0.0;
531    aux2Panel.add(rbUseExistingCertificate, gbc);
532    gbc.weightx = 1.0;
533    gbc.gridwidth = GridBagConstraints.REMAINDER;
534    aux2Panel.add(Box.createHorizontalGlue(), gbc);
535    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
536    auxPanel.add(aux2Panel, gbc);
537
538    additionalInset = Math.abs(lKeystoreType.getPreferredSize().height -
539        rbJKS.getPreferredSize().height) / 2;
540    aux2Panel = new JPanel(new GridBagLayout());
541    aux2Panel.setOpaque(false);
542    gbc.insets.top -= additionalInset;
543    gbc.insets.left = UIFactory.LEFT_INSET_RADIO_SUBORDINATE;
544    auxPanel.add(aux2Panel, gbc);
545
546    gbc.gridwidth = GridBagConstraints.RELATIVE;
547    gbc.insets = UIFactory.getEmptyInsets();
548    gbc.weightx = 0.0;
549    gbc.anchor = GridBagConstraints.NORTHWEST;
550    gbc.insets.top = additionalInset;
551    aux2Panel.add(lKeystoreType, gbc);
552    gbc.gridwidth = GridBagConstraints.REMAINDER;
553    gbc.insets.top = 0;
554    aux2Panel.add(rbJKS, gbc);
555
556    gbc.insets.top = UIFactory.TOP_INSET_RADIOBUTTON;
557    gbc.gridwidth = GridBagConstraints.RELATIVE;
558    aux2Panel.add(Box.createHorizontalGlue(), gbc);
559    gbc.gridwidth = GridBagConstraints.REMAINDER;
560    aux2Panel.add(rbJCEKS, gbc);
561    gbc.gridwidth = GridBagConstraints.RELATIVE;
562    aux2Panel.add(Box.createHorizontalGlue(), gbc);
563    gbc.gridwidth = GridBagConstraints.REMAINDER;
564    aux2Panel.add(rbPKCS12, gbc);
565    gbc.gridwidth = GridBagConstraints.RELATIVE;
566    aux2Panel.add(Box.createHorizontalGlue(), gbc);
567    gbc.gridwidth = GridBagConstraints.REMAINDER;
568    aux2Panel.add(rbPKCS11, gbc);
569
570    gbc.gridwidth = GridBagConstraints.RELATIVE;
571    gbc.insets.left = 0;
572    gbc.weightx = 0.0;
573    gbc.anchor = GridBagConstraints.WEST;
574    aux2Panel.add(lKeystorePath, gbc);
575    JPanel aux3Panel = new JPanel(new GridBagLayout());
576    aux3Panel.setOpaque(false);
577    gbc.weightx = 1.0;
578    gbc.insets.left = UIFactory.LEFT_INSET_SECONDARY_FIELD;
579    gbc.gridwidth = GridBagConstraints.REMAINDER;
580    aux2Panel.add(aux3Panel, gbc);
581    gbc.insets = UIFactory.getEmptyInsets();
582    gbc.gridwidth = GridBagConstraints.RELATIVE;
583    aux3Panel.add(tfKeystorePath, gbc);
584    gbc.insets.left = UIFactory.LEFT_INSET_BROWSE;
585    gbc.gridwidth = GridBagConstraints.REMAINDER;
586    gbc.weightx = 0.0;
587    aux3Panel.add(browseButton, gbc);
588
589    gbc.insets.left = 0;
590    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
591    gbc.gridwidth = GridBagConstraints.RELATIVE;
592    gbc.weightx = 0.0;
593    gbc.anchor = GridBagConstraints.WEST;
594    aux2Panel.add(lKeystorePwd, gbc);
595    gbc.insets.left = UIFactory.LEFT_INSET_SECONDARY_FIELD;
596    gbc.gridwidth = GridBagConstraints.REMAINDER;
597    gbc.fill = GridBagConstraints.NONE;
598    aux2Panel.add(tfKeystorePwd, gbc);
599
600    return inputPanel;
601  }
602
603  /**
604   * Creates and returns the buttons OK/CANCEL sub panel.
605   * @return the buttons OK/CANCEL sub panel.
606   */
607  private Component createButtonsPanel()
608  {
609    JPanel buttonsPanel = new JPanel(new GridBagLayout());
610    buttonsPanel.setOpaque(false);
611    GridBagConstraints gbc = new GridBagConstraints();
612    gbc.fill = GridBagConstraints.HORIZONTAL;
613    gbc.gridwidth = 4;
614    gbc.insets = UIFactory.getEmptyInsets();
615    gbc.insets.left = UIFactory.getCurrentStepPanelInsets().left;
616    buttonsPanel.add(UIFactory.makeJLabel(UIFactory.IconType.NO_ICON,
617        null, UIFactory.TextStyle.NO_STYLE), gbc);
618    gbc.weightx = 1.0;
619    gbc.gridwidth--;
620    gbc.insets.left = 0;
621    buttonsPanel.add(Box.createHorizontalGlue(), gbc);
622    gbc.gridwidth = GridBagConstraints.RELATIVE;
623    gbc.fill = GridBagConstraints.NONE;
624    gbc.weightx = 0.0;
625    okButton =
626      UIFactory.makeJButton(INFO_OK_BUTTON_LABEL.get(),
627          INFO_SECURITY_OPTIONS_OK_BUTTON_TOOLTIP.get());
628    buttonsPanel.add(okButton, gbc);
629    okButton.addActionListener(new ActionListener()
630    {
631      public void actionPerformed(ActionEvent ev)
632      {
633        okClicked();
634      }
635    });
636
637    gbc.gridwidth = GridBagConstraints.REMAINDER;
638    gbc.insets.left = UIFactory.HORIZONTAL_INSET_BETWEEN_BUTTONS;
639    cancelButton =
640      UIFactory.makeJButton(INFO_CANCEL_BUTTON_LABEL.get(),
641          INFO_SECURITY_OPTIONS_CANCEL_BUTTON_TOOLTIP.get());
642    buttonsPanel.add(cancelButton, gbc);
643    cancelButton.addActionListener(new ActionListener()
644    {
645      public void actionPerformed(ActionEvent ev)
646      {
647        cancelClicked();
648      }
649    });
650
651    return buttonsPanel;
652  }
653
654  /**
655   * Method called when user clicks on cancel.
656   *
657   */
658  private void cancelClicked()
659  {
660    isCanceled = true;
661    dispose();
662  }
663
664  /**
665   * Method called when user clicks on OK.
666   *
667   */
668  private void okClicked()
669  {
670    BackgroundTask<ArrayList<LocalizableMessage>> worker =
671      new BackgroundTask<ArrayList<LocalizableMessage>>()
672    {
673      @Override
674      public ArrayList<LocalizableMessage> processBackgroundTask()
675      {
676        ArrayList<LocalizableMessage> errorMsgs = new ArrayList<>();
677        errorMsgs.addAll(checkPort());
678        errorMsgs.addAll(checkKeystore());
679        return errorMsgs;
680      }
681
682      @Override
683      public void backgroundTaskCompleted(ArrayList<LocalizableMessage> returnValue,
684          Throwable throwable)
685      {
686        if (throwable != null)
687        {
688          // Bug
689          throwable.printStackTrace();
690          displayError(
691              getThrowableMsg(INFO_BUG_MSG.get(), throwable),
692              INFO_ERROR_TITLE.get());
693          cancelButton.setEnabled(true);
694          okButton.setEnabled(true);
695        }
696        else
697        {
698          cancelButton.setEnabled(true);
699          okButton.setEnabled(true);
700
701          if (!returnValue.isEmpty())
702          {
703            displayError(Utils.getMessageFromCollection(returnValue, "\n"),
704                INFO_ERROR_TITLE.get());
705          }
706          else if (rbUseExistingCertificate.isSelected()
707              && (cbEnableSSL.isSelected() || cbEnableStartTLS.isSelected()))
708          {
709            if (!certificateHasAlias)
710            {
711              selectedAlias = null;
712              isCanceled = false;
713              dispose();
714            }
715            else if (aliases.length > 1)
716            {
717              if (aliasDlg == null)
718              {
719                aliasDlg = new SelectAliasDialog(SecurityOptionsDialog.this);
720              }
721              aliasDlg.display(aliases);
722
723              if (!aliasDlg.isCanceled())
724              {
725                selectedAlias = aliasDlg.getSelectedAlias();
726                isCanceled = false;
727                dispose();
728              }
729            }
730            else
731            {
732              selectedAlias = aliases[0];
733              isCanceled = false;
734              dispose();
735            }
736          }
737          else
738          {
739            isCanceled = false;
740            dispose();
741          }
742        }
743      }
744    };
745    cancelButton.setEnabled(false);
746    okButton.setEnabled(false);
747    worker.startBackgroundTask();
748  }
749
750  /**
751   * Displays an error message dialog.
752   *
753   * @param msg
754   *          the error message.
755   * @param title
756   *          the title for the dialog.
757   */
758  private void displayError(LocalizableMessage msg, LocalizableMessage title)
759  {
760    Utilities.displayError(this, msg, title);
761    toFront();
762  }
763
764  /**
765   * Updates the widgets on the dialog with the contents of the securityOptions
766   * object.
767   *
768   */
769  private void updateContents()
770  {
771    cbEnableSSL.setSelected(securityOptions.getEnableSSL());
772    cbEnableStartTLS.setSelected(securityOptions.getEnableStartTLS());
773    if (securityOptions.getEnableSSL())
774    {
775      int port = securityOptions.getSslPort();
776      if (port > 0)
777      {
778        tfPort.setText(String.valueOf(port));
779      }
780    }
781
782    switch (securityOptions.getCertificateType())
783    {
784    case NO_CERTIFICATE:
785      // Nothing else to do
786      break;
787
788    case SELF_SIGNED_CERTIFICATE:
789      rbUseSelfSignedCertificate.setSelected(true);
790      break;
791
792    case JKS:
793      rbUseExistingCertificate.setSelected(true);
794      rbJKS.setSelected(true);
795      tfKeystorePath.setText(securityOptions.getKeystorePath());
796      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
797      break;
798
799    case JCEKS:
800      rbUseExistingCertificate.setSelected(true);
801      rbJCEKS.setSelected(true);
802      tfKeystorePath.setText(securityOptions.getKeystorePath());
803      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
804      break;
805
806    case PKCS11:
807      rbUseExistingCertificate.setSelected(true);
808      rbPKCS11.setSelected(true);
809      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
810      break;
811
812    case PKCS12:
813      rbUseExistingCertificate.setSelected(true);
814      rbPKCS12.setSelected(true);
815      tfKeystorePath.setText(securityOptions.getKeystorePath());
816      tfKeystorePwd.setText(securityOptions.getKeystorePassword());
817      break;
818
819    default:
820      throw new IllegalStateException("Unknown certificate type.");
821    }
822
823    updateEnablingState();
824  }
825
826  /**
827   * Enables/disables and makes visible/invisible the objects according to what
828   * the user selected.
829   */
830  private void updateEnablingState()
831  {
832    boolean enableSSL = cbEnableSSL.isSelected();
833    boolean enableStartTLS = cbEnableStartTLS.isSelected();
834
835    boolean useSSL = enableSSL || enableStartTLS;
836
837    if (useSSL && !rbUseSelfSignedCertificate.isSelected() &&
838        !rbUseExistingCertificate.isSelected())
839    {
840      rbUseSelfSignedCertificate.setSelected(true);
841    }
842
843    if (useSSL && rbUseExistingCertificate.isSelected() &&
844        !rbJKS.isSelected() && !rbJCEKS.isSelected() &&
845        !rbPKCS11.isSelected() && !rbPKCS12.isSelected())
846    {
847      rbJKS.setSelected(true);
848    }
849    tfPort.setEnabled(enableSSL);
850
851    rbUseSelfSignedCertificate.setEnabled(useSSL);
852
853    rbUseExistingCertificate.setEnabled(useSSL);
854    lKeystoreType.setEnabled(
855        rbUseExistingCertificate.isSelected() && useSSL);
856    rbJKS.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
857    rbJCEKS.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
858    rbPKCS11.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
859    rbPKCS12.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
860
861    lKeystorePath.setEnabled(
862        rbUseExistingCertificate.isSelected() && useSSL);
863    tfKeystorePath.setEnabled(
864        rbUseExistingCertificate.isSelected() && useSSL);
865    browseButton.setEnabled(rbUseExistingCertificate.isSelected() && useSSL);
866    lKeystorePwd.setEnabled(
867        rbUseExistingCertificate.isSelected() && useSSL);
868    tfKeystorePwd.setEnabled(
869        rbUseExistingCertificate.isSelected() && useSSL);
870
871    lKeystorePath.setVisible(!rbPKCS11.isSelected());
872    tfKeystorePath.setVisible(!rbPKCS11.isSelected());
873    browseButton.setVisible(!rbPKCS11.isSelected());
874  }
875
876  /**
877   * Returns the port help message that we display when we cannot use the
878   * default port (636).
879   * @return the port help message that we display when we cannot use the
880   * default port (636).
881   */
882  private LocalizableMessage getPortHelpMessage()
883  {
884    LocalizableMessage s = LocalizableMessage.EMPTY;
885    if (securityOptions.getSslPort() != DEFAULT_PORT)
886    {
887      s = INFO_CANNOT_USE_DEFAULT_SECURE_PORT.get();
888    }
889    return s;
890  }
891
892  /**
893   * Checks the port.
894   * @return the error messages found while checking the port.
895   */
896  private ArrayList<LocalizableMessage> checkPort()
897  {
898    ArrayList<LocalizableMessage> errorMsgs = new ArrayList<>();
899
900    if (cbEnableSSL.isSelected())
901    {
902      /* Check the port. */
903      String sPort = tfPort.getText();
904      int port = -1;
905      try
906      {
907        port = Integer.parseInt(sPort);
908        if (port < Installer.MIN_PORT_VALUE
909            || port > Installer.MAX_PORT_VALUE)
910        {
911          errorMsgs.add(INFO_INVALID_SECURE_PORT_VALUE_RANGE.get(
912              Installer.MIN_PORT_VALUE, Installer.MAX_PORT_VALUE));
913        }
914        else if (!Utils.canUseAsPort(port))
915        {
916          if (Utils.isPrivilegedPort(port))
917          {
918            errorMsgs.add(INFO_CANNOT_BIND_PRIVILEDGED_PORT.get(port));
919          }
920          else
921          {
922            errorMsgs.add(INFO_CANNOT_BIND_PORT.get(port));
923          }
924        }
925      }
926      catch (NumberFormatException nfe)
927      {
928        errorMsgs.add(INFO_INVALID_SECURE_PORT_VALUE_RANGE.get(
929            Installer.MIN_PORT_VALUE, Installer.MAX_PORT_VALUE));
930      }
931    }
932    setValidLater(cbEnableSSL, errorMsgs.isEmpty());
933    return errorMsgs;
934  }
935
936  /**
937   * Checks the existing keystore parameters.
938   * @return the error messages found while checking existing keystore
939   * parameters.
940   */
941  private ArrayList<LocalizableMessage> checkKeystore()
942  {
943    ArrayList<LocalizableMessage> errorMsgs = new ArrayList<>();
944
945    boolean pathValid = true;
946    boolean pwdValid = true;
947
948    if (rbUseExistingCertificate.isSelected() &&
949        (cbEnableSSL.isSelected() || cbEnableStartTLS.isSelected()))
950    {
951      String path = tfKeystorePath.getText();
952      if (rbJKS.isSelected() || rbJCEKS.isSelected() || rbPKCS12.isSelected())
953      {
954        /* Check the path */
955        if (path == null || path.length() == 0)
956        {
957          errorMsgs.add(INFO_KEYSTORE_PATH_NOT_PROVIDED.get());
958        }
959        else
960        {
961          File f = new File(path);
962          if (!f.exists())
963          {
964            errorMsgs.add(INFO_KEYSTORE_PATH_DOES_NOT_EXIST.get());
965          }
966          else if (!f.isFile())
967          {
968            errorMsgs.add(INFO_KEYSTORE_PATH_NOT_A_FILE.get());
969          }
970        }
971
972        pathValid = errorMsgs.isEmpty();
973      }
974
975      String pwd = String.valueOf(tfKeystorePwd.getPassword());
976      if (pathValid)
977      {
978        try
979        {
980          CertificateManager certManager;
981          if (rbJKS.isSelected())
982          {
983            certManager = new CertificateManager(
984                path,
985                CertificateManager.KEY_STORE_TYPE_JKS,
986                pwd);
987          }
988          else if (rbJCEKS.isSelected())
989          {
990            certManager = new CertificateManager(
991                path,
992                CertificateManager.KEY_STORE_TYPE_JCEKS,
993                pwd);
994          }
995          else if (rbPKCS12.isSelected())
996          {
997            certManager = new CertificateManager(
998                path,
999                CertificateManager.KEY_STORE_TYPE_PKCS12,
1000                pwd);
1001          }
1002          else if (rbPKCS11.isSelected())
1003          {
1004            certManager = new CertificateManager(
1005                CertificateManager.KEY_STORE_PATH_PKCS11,
1006                CertificateManager.KEY_STORE_TYPE_PKCS11,
1007                pwd);
1008          }
1009          else
1010          {
1011            throw new IllegalStateException("No keystore type selected.");
1012          }
1013          aliases = certManager.getCertificateAliases();
1014          if (aliases == null || aliases.length == 0)
1015          {
1016            // Could not retrieve any certificate
1017            if (rbPKCS11.isSelected())
1018            {
1019              errorMsgs.add(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST.get());
1020            }
1021            else
1022            {
1023              if (rbJKS.isSelected())
1024              {
1025                errorMsgs.add(INFO_JKS_KEYSTORE_DOES_NOT_EXIST.get());
1026              }
1027              else if (rbJCEKS.isSelected())
1028              {
1029                errorMsgs.add(INFO_JCEKS_KEYSTORE_DOES_NOT_EXIST.get());
1030              }
1031              else
1032              {
1033                errorMsgs.add(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST.get());
1034              }
1035              pathValid = false;
1036            }
1037          }
1038          else
1039          {
1040            certificateHasAlias = certManager.hasRealAliases();
1041          }
1042        }
1043        catch (KeyStoreException ke)
1044        {
1045          // issue OPENDJ-18, related to JDK bug
1046          if (StaticUtils
1047              .stackTraceContainsCause(ke, ArithmeticException.class))
1048          {
1049            errorMsgs.add(INFO_ERROR_ACCESSING_KEYSTORE_JDK_BUG.get());
1050          }
1051          else
1052          {
1053            pwdValid = false;
1054            if (!rbPKCS11.isSelected())
1055            {
1056              pathValid = false;
1057            }
1058            // Could not access to the keystore: because the password is
1059            // no good, because the provided file is not a valid keystore, etc.
1060            if (rbPKCS11.isSelected())
1061            {
1062              errorMsgs.add(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE.get());
1063            }
1064            else
1065            {
1066              if (rbJKS.isSelected())
1067              {
1068                errorMsgs.add(INFO_ERROR_ACCESSING_JKS_KEYSTORE.get());
1069              }
1070              else if (rbJCEKS.isSelected())
1071              {
1072                errorMsgs.add(INFO_ERROR_ACCESSING_JCEKS_KEYSTORE.get());
1073              }
1074              else
1075              {
1076                errorMsgs.add(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE.get());
1077              }
1078              pathValid = false;
1079            }
1080          }
1081        }
1082      }
1083    }
1084
1085    setValidLater(lKeystorePath, pathValid);
1086    setValidLater(lKeystorePwd, pwdValid);
1087
1088    return errorMsgs;
1089  }
1090
1091  /**
1092   * Method that updates the text style of a provided component by calling
1093   * SwingUtilities.invokeLater.  This method is aimed to be called outside
1094   * the event thread (calling it from the event thread will also work though).
1095   * @param comp the component to be updated.
1096   * @param valid whether to use a TextStyle to mark the component as valid
1097   * or as invalid.
1098   */
1099  private void setValidLater(final JComponent comp, final boolean valid)
1100  {
1101    SwingUtilities.invokeLater(new Runnable()
1102    {
1103      public void run()
1104      {
1105        UIFactory.setTextStyle(comp,
1106            valid ? UIFactory.TextStyle.SECONDARY_FIELD_VALID :
1107              UIFactory.TextStyle.SECONDARY_FIELD_INVALID);
1108      }
1109    });
1110  }
1111
1112  /**
1113   * Method written for testing purposes.
1114   * @param args the arguments to be passed to the test program.
1115   */
1116  public static void main(String[] args)
1117  {
1118    try
1119    {
1120      // UIFactory.initialize();
1121      SecurityOptionsDialog dlg = new SecurityOptionsDialog(new JFrame(),
1122          SecurityOptions.createNoCertificateOptions());
1123      dlg.pack();
1124      dlg.setVisible(true);
1125    } catch (Exception ex)
1126    {
1127      ex.printStackTrace();
1128    }
1129  }
1130}