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