	private Container getUI() {		
		userCertificatePanel = new UserCertificatePanel(this) {
			private static final long serialVersionUID = 1L;
			@Override
			public Insets getInsets() {
				return Utils.INSETS;
			}
			@Override
			public boolean isAllowEmail() {
				return mail.isReady();
			}
			@Override
			public Date getDefaultExpiration() {
				Calendar c = Calendar.getInstance();
				c.add(Calendar.DATE, defaultDuration);
				c.set(Calendar.HOUR_OF_DAY, 0);
				c.set(Calendar.MINUTE, 0);
				c.set(Calendar.SECOND, 0);
				c.set(Calendar.MILLISECOND, 0);
				return c.getTime();
			}
			@Override
			public boolean saveUser(String username, UserCertificateModel ucm) {
				return userService.updateUserModel(username, ucm.user);
			}
			@Override
			public boolean newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail) {
				if (!prepareX509Infrastructure()) {
					return false;
				}
				Date notAfter = metadata.notAfter;
				setMetadataDefaults(metadata);
				metadata.notAfter = notAfter;
				UserModel user = ucm.user;				
				if (!StringUtils.isEmpty(user.organizationalUnit)) {
					metadata.oids.put("OU", user.organizationalUnit);
				}
				if (!StringUtils.isEmpty(user.organization)) {
					metadata.oids.put("O", user.organization);
				}
				if (!StringUtils.isEmpty(user.locality)) {
					metadata.oids.put("L", user.locality);
				}
				if (!StringUtils.isEmpty(user.stateProvince)) {
					metadata.oids.put("ST", user.stateProvince);
				}
				if (!StringUtils.isEmpty(user.countryCode)) {
					metadata.oids.put("C", user.countryCode);
				}
				File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
				File zip = X509Utils.newClientBundle(metadata, caKeystoreFile, caKeystorePassword, GitblitAuthority.this);
				if (ucm.expires == null || metadata.notAfter.before(ucm.expires)) {
					ucm.expires = metadata.notAfter;
				}
				updateAuthorityConfig(ucm);
				ucm.certs = null;
				int modelIndex = table.convertRowIndexToModel(table.getSelectedRow());
				tableModel.fireTableDataChanged();
				table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex);
				if (sendEmail) {
					sendEmail(user, metadata, zip);
				}
				return true;
			}
			@Override
			public boolean revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason) {
				if (!prepareX509Infrastructure()) {
					return false;
				}
				File caRevocationList = new File(folder, X509Utils.CA_REVOCATION_LIST);
				File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
				if (X509Utils.revoke(cert, reason, caRevocationList, caKeystoreFile, caKeystorePassword, GitblitAuthority.this)) {
					File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
					FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect());
					if (certificatesConfigFile.exists()) {
						try {
							config.load();
						} catch (Exception e) {
							Utils.showException(GitblitAuthority.this, e);
						}
					}
					ucm.revoke(cert.getSerialNumber(), reason);
					ucm.update(config);
					try {
						config.save();
					} catch (Exception e) {
						Utils.showException(GitblitAuthority.this, e);
					}
					ucm.certs = null;
					int modelIndex = table.convertRowIndexToModel(table.getSelectedRow());
					tableModel.fireTableDataChanged();
					table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex);
					return true;
				}
				return false;
			}
		};
		table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
		table.setRowSorter(defaultSorter);
		table.setDefaultRenderer(CertificateStatus.class, new CertificateStatusRenderer());
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				if (e.getValueIsAdjusting()) {
					return;
				}
				int row = table.getSelectedRow();
				if (row < 0) {
					return;
				}
				int modelIndex = table.convertRowIndexToModel(row);
				UserCertificateModel ucm = tableModel.get(modelIndex);
				if (ucm.certs == null) {
					ucm.certs = findCerts(folder, ucm.user.username);
				}
				userCertificatePanel.setUserCertificateModel(ucm);
			}
		});
		JPanel usersPanel = new JPanel(new BorderLayout()) {
			private static final long serialVersionUID = 1L;
			@Override
			public Insets getInsets() {
				return Utils.INSETS;
			}
		};
		usersPanel.add(new HeaderPanel(Translation.get("gb.users"), "users_16x16.png"), BorderLayout.NORTH);
		usersPanel.add(new JScrollPane(table), BorderLayout.CENTER);
		usersPanel.setMinimumSize(new Dimension(400, 10));
		certificateDefaultsButton = new JButton(new ImageIcon(getClass().getResource("/settings_16x16.png")));
		certificateDefaultsButton.setFocusable(false);
		certificateDefaultsButton.setToolTipText(Translation.get("gb.newCertificateDefaults"));		
		certificateDefaultsButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				X509Metadata metadata = new X509Metadata("whocares", "whocares");
				File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
				FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect());
				NewCertificateConfig certificateConfig = null;
				if (certificatesConfigFile.exists()) {
					try {
						config.load();
					} catch (Exception x) {
						Utils.showException(GitblitAuthority.this, x);
					}
					certificateConfig = NewCertificateConfig.KEY.parse(config);
					certificateConfig.update(metadata);
				}
				InputVerifier verifier = new InputVerifier() {
					public boolean verify(JComponent comp) {
						boolean returnValue;
						JTextField textField = (JTextField) comp;
						try {
							Integer.parseInt(textField.getText());
							returnValue = true;
						} catch (NumberFormatException e) {
							returnValue = false;
						}
						return returnValue;
					}
				};
				JTextField siteNameTF = new JTextField(20);
				siteNameTF.setText(gitblitSettings.getString(Keys.web.siteName, "Gitblit"));
				JPanel siteNamePanel = Utils.newFieldPanel(Translation.get("gb.siteName"),
						siteNameTF, Translation.get("gb.siteNameDescription"));
				JTextField validityTF = new JTextField(4);
				validityTF.setInputVerifier(verifier);
				validityTF.setVerifyInputWhenFocusTarget(true);
				validityTF.setText("" + certificateConfig.duration);
				JPanel validityPanel = Utils.newFieldPanel(Translation.get("gb.validity"),
						validityTF, Translation.get("gb.duration.days").replace("{0}",  "").trim());
				JPanel p1 = new JPanel(new GridLayout(0, 1, 5, 2));
				p1.add(siteNamePanel);
				p1.add(validityPanel);
				DefaultOidsPanel oids = new DefaultOidsPanel(metadata);
				JPanel panel = new JPanel(new BorderLayout());
				panel.add(p1, BorderLayout.NORTH);
				panel.add(oids, BorderLayout.CENTER);
				int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, 
						panel, Translation.get("gb.newCertificateDefaults"), JOptionPane.OK_CANCEL_OPTION,
						JOptionPane.QUESTION_MESSAGE, new ImageIcon(getClass().getResource("/settings_32x32.png")));
				if (result == JOptionPane.OK_OPTION) {
					try {
						oids.update(metadata);
						certificateConfig.duration = Integer.parseInt(validityTF.getText());
						certificateConfig.store(config, metadata);
						config.save();
						Map<String, String> updates = new HashMap<String, String>();
						updates.put(Keys.web.siteName, siteNameTF.getText());
						gitblitSettings.saveSettings(updates);
					} catch (Exception e1) {
						Utils.showException(GitblitAuthority.this, e1);
					}
				}
			}
		});
		newSSLCertificate = new JButton(new ImageIcon(getClass().getResource("/rosette_16x16.png")));
		newSSLCertificate.setFocusable(false);
		newSSLCertificate.setToolTipText(Translation.get("gb.newSSLCertificate"));		
		newSSLCertificate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Date defaultExpiration = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);
				NewSSLCertificateDialog dialog = new NewSSLCertificateDialog(GitblitAuthority.this, defaultExpiration);
				dialog.setModal(true);
				dialog.setVisible(true);
				if (dialog.isCanceled()) {
					return;
				}
				final Date expires = dialog.getExpiration();
				final String hostname = dialog.getHostname();
				final boolean serveCertificate = dialog.isServeCertificate();
				AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
					@Override
					protected Boolean doRequest() throws IOException {
						if (!prepareX509Infrastructure()) {
							return false;
						}
						File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
						PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword);
						X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword);
						X509Metadata metadata = new X509Metadata(hostname, caKeystorePassword);
						setMetadataDefaults(metadata);
						metadata.notAfter = expires;
						File serverKeystoreFile = new File(folder, X509Utils.SERVER_KEY_STORE);
						X509Certificate cert = X509Utils.newSSLCertificate(metadata, caPrivateKey, caCert, serverKeystoreFile, GitblitAuthority.this);
						boolean hasCert = cert != null;
						if (hasCert && serveCertificate) {
							Map<String, String> updates = new HashMap<String, String>();
							updates.put(Keys.server.certificateAlias, metadata.commonName);
							gitblitSettings.saveSettings(updates);
						}
						return hasCert;
					}
					@Override
					protected void onSuccess() {
						if (serveCertificate) {
							JOptionPane.showMessageDialog(GitblitAuthority.this, 
									MessageFormat.format(Translation.get("gb.sslCertificateGeneratedRestart"), hostname),
									Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE);
						} else {
							JOptionPane.showMessageDialog(GitblitAuthority.this, 
								MessageFormat.format(Translation.get("gb.sslCertificateGenerated"), hostname),
								Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE);
						}
					}
				};
				worker.execute();
			}
		});
		JButton emailBundle = new JButton(new ImageIcon(getClass().getResource("/mail_16x16.png")));
		emailBundle.setFocusable(false);
		emailBundle.setToolTipText(Translation.get("gb.emailCertificateBundle"));		
		emailBundle.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				int row = table.getSelectedRow();
				if (row < 0) {
					return;
				}
				int modelIndex = table.convertRowIndexToModel(row);
				final UserCertificateModel ucm = tableModel.get(modelIndex);
				if (ArrayUtils.isEmpty(ucm.certs)) {
					JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.pleaseGenerateClientCertificate"), ucm.user.getDisplayName()));
				}
				final File zip = new File(folder, X509Utils.CERTS + File.separator + ucm.user.username + File.separator + ucm.user.username + ".zip");
				if (!zip.exists()) {
					return;
				}
				AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
					@Override
					protected Boolean doRequest() throws IOException {
						X509Metadata metadata = new X509Metadata(ucm.user.username, "whocares");
						metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, Constants.NAME);
						if (StringUtils.isEmpty(metadata.serverHostname)) {
							metadata.serverHostname = Constants.NAME;
						}
						metadata.userDisplayname = ucm.user.getDisplayName();
						return sendEmail(ucm.user, metadata, zip);
					}
					@Override
					protected void onSuccess() {
						JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.clientCertificateBundleSent"),
								ucm.user.getDisplayName()));
					}
				};
				worker.execute();				
			}
		});
		JButton logButton = new JButton(new ImageIcon(getClass().getResource("/script_16x16.png")));
		logButton.setFocusable(false);
		logButton.setToolTipText(Translation.get("gb.log"));		
		logButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				File log = new File(folder, X509Utils.CERTS + File.separator + "log.txt");
				if (log.exists()) {
					String content = FileUtils.readContent(log,  "\n");
					JTextArea textarea = new JTextArea(content);
					JScrollPane scrollPane = new JScrollPane(textarea);
					scrollPane.setPreferredSize(new Dimension(700, 400));
					JOptionPane.showMessageDialog(GitblitAuthority.this, scrollPane, log.getAbsolutePath(), JOptionPane.INFORMATION_MESSAGE);
				}
			}
		});
		final JTextField filterTextfield = new JTextField(15);
		filterTextfield.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				filterUsers(filterTextfield.getText());
			}
		});
		filterTextfield.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				filterUsers(filterTextfield.getText());
			}
		});
		JToolBar buttonControls = new JToolBar(JToolBar.HORIZONTAL);
		buttonControls.setFloatable(false);
		buttonControls.add(certificateDefaultsButton);
		buttonControls.add(newSSLCertificate);
		buttonControls.add(emailBundle);
		buttonControls.add(logButton);
		JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, Utils.MARGIN, Utils.MARGIN));
		userControls.add(new JLabel(Translation.get("gb.filter")));
		userControls.add(filterTextfield);
		JPanel topPanel = new JPanel(new BorderLayout(0, 0));
		topPanel.add(buttonControls, BorderLayout.WEST);
		topPanel.add(userControls, BorderLayout.EAST);
		JPanel leftPanel = new JPanel(new BorderLayout());
		leftPanel.add(topPanel, BorderLayout.NORTH);
		leftPanel.add(usersPanel, BorderLayout.CENTER);
		userCertificatePanel.setMinimumSize(new Dimension(375, 10));
		JLabel statusLabel = new JLabel();
		statusLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		if (X509Utils.unlimitedStrength) {
			statusLabel.setText("JCE Unlimited Strength Jurisdiction Policy");
		} else {
			statusLabel.setText("JCE Standard Encryption Policy");
		}
		JPanel root = new JPanel(new BorderLayout()) {
			private static final long serialVersionUID = 1L;
			public Insets getInsets() {
				return Utils.INSETS;
			}
		};
		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, userCertificatePanel);
		splitPane.setDividerLocation(1d);
		root.add(splitPane, BorderLayout.CENTER);
		root.add(statusLabel, BorderLayout.SOUTH);
		return root;
	}

	private Container getUI() {		
		userCertificatePanel = new UserCertificatePanel(this) {
			private static final long serialVersionUID = 1L;
			@Override
			public Insets getInsets() {
				return Utils.INSETS;
			}
			@Override
			public boolean isAllowEmail() {
				return mail.isReady();
			}
			@Override
			public Date getDefaultExpiration() {
				Calendar c = Calendar.getInstance();
				c.add(Calendar.DATE, defaultDuration);
				c.set(Calendar.HOUR_OF_DAY, 0);
				c.set(Calendar.MINUTE, 0);
				c.set(Calendar.SECOND, 0);
				c.set(Calendar.MILLISECOND, 0);
				return c.getTime();
			}
			@Override
			public boolean saveUser(String username, UserCertificateModel ucm) {
				return userService.updateUserModel(username, ucm.user);
			}
			@Override
			public boolean newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail) {
				if (!prepareX509Infrastructure()) {
					return false;
				}
				Date notAfter = metadata.notAfter;
				setMetadataDefaults(metadata);
				metadata.notAfter = notAfter;
				UserModel user = ucm.user;				
				if (!StringUtils.isEmpty(user.organizationalUnit)) {
					metadata.oids.put("OU", user.organizationalUnit);
				}
				if (!StringUtils.isEmpty(user.organization)) {
					metadata.oids.put("O", user.organization);
				}
				if (!StringUtils.isEmpty(user.locality)) {
					metadata.oids.put("L", user.locality);
				}
				if (!StringUtils.isEmpty(user.stateProvince)) {
					metadata.oids.put("ST", user.stateProvince);
				}
				if (!StringUtils.isEmpty(user.countryCode)) {
					metadata.oids.put("C", user.countryCode);
				}
				File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
				File zip = X509Utils.newClientBundle(metadata, caKeystoreFile, caKeystorePassword, GitblitAuthority.this);
				if (ucm.expires == null || metadata.notAfter.before(ucm.expires)) {
					ucm.expires = metadata.notAfter;
				}
				updateAuthorityConfig(ucm);
				ucm.certs = null;
				int selectedIndex = table.getSelectedRow();
				tableModel.fireTableDataChanged();
				table.getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex);
				if (sendEmail) {
					sendEmail(user, metadata, zip);
				}
				return true;
			}
			@Override
			public boolean revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason) {
				if (!prepareX509Infrastructure()) {
					return false;
				}
				File caRevocationList = new File(folder, X509Utils.CA_REVOCATION_LIST);
				File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
				if (X509Utils.revoke(cert, reason, caRevocationList, caKeystoreFile, caKeystorePassword, GitblitAuthority.this)) {
					File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
					FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect());
					if (certificatesConfigFile.exists()) {
						try {
							config.load();
						} catch (Exception e) {
							Utils.showException(GitblitAuthority.this, e);
						}
					}
					ucm.revoke(cert.getSerialNumber(), reason);
					ucm.update(config);
					try {
						config.save();
					} catch (Exception e) {
						Utils.showException(GitblitAuthority.this, e);
					}
					ucm.certs = null;
					int modelIndex = table.convertRowIndexToModel(table.getSelectedRow());
					tableModel.fireTableDataChanged();
					table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex);
					return true;
				}
				return false;
			}
		};
		table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
		table.setRowSorter(defaultSorter);
		table.setDefaultRenderer(CertificateStatus.class, new CertificateStatusRenderer());
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				if (e.getValueIsAdjusting()) {
					return;
				}
				int row = table.getSelectedRow();
				if (row < 0) {
					return;
				}
				int modelIndex = table.convertRowIndexToModel(row);
				UserCertificateModel ucm = tableModel.get(modelIndex);
				if (ucm.certs == null) {
					ucm.certs = findCerts(folder, ucm.user.username);
				}
				userCertificatePanel.setUserCertificateModel(ucm);
			}
		});
		JPanel usersPanel = new JPanel(new BorderLayout()) {
			private static final long serialVersionUID = 1L;
			@Override
			public Insets getInsets() {
				return Utils.INSETS;
			}
		};
		usersPanel.add(new HeaderPanel(Translation.get("gb.users"), "users_16x16.png"), BorderLayout.NORTH);
		usersPanel.add(new JScrollPane(table), BorderLayout.CENTER);
		usersPanel.setMinimumSize(new Dimension(400, 10));
		certificateDefaultsButton = new JButton(new ImageIcon(getClass().getResource("/settings_16x16.png")));
		certificateDefaultsButton.setFocusable(false);
		certificateDefaultsButton.setToolTipText(Translation.get("gb.newCertificateDefaults"));		
		certificateDefaultsButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				X509Metadata metadata = new X509Metadata("whocares", "whocares");
				File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
				FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect());
				NewCertificateConfig certificateConfig = null;
				if (certificatesConfigFile.exists()) {
					try {
						config.load();
					} catch (Exception x) {
						Utils.showException(GitblitAuthority.this, x);
					}
					certificateConfig = NewCertificateConfig.KEY.parse(config);
					certificateConfig.update(metadata);
				}
				InputVerifier verifier = new InputVerifier() {
					public boolean verify(JComponent comp) {
						boolean returnValue;
						JTextField textField = (JTextField) comp;
						try {
							Integer.parseInt(textField.getText());
							returnValue = true;
						} catch (NumberFormatException e) {
							returnValue = false;
						}
						return returnValue;
					}
				};
				JTextField siteNameTF = new JTextField(20);
				siteNameTF.setText(gitblitSettings.getString(Keys.web.siteName, "Gitblit"));
				JPanel siteNamePanel = Utils.newFieldPanel(Translation.get("gb.siteName"),
						siteNameTF, Translation.get("gb.siteNameDescription"));
				JTextField validityTF = new JTextField(4);
				validityTF.setInputVerifier(verifier);
				validityTF.setVerifyInputWhenFocusTarget(true);
				validityTF.setText("" + certificateConfig.duration);
				JPanel validityPanel = Utils.newFieldPanel(Translation.get("gb.validity"),
						validityTF, Translation.get("gb.duration.days").replace("{0}",  "").trim());
				JPanel p1 = new JPanel(new GridLayout(0, 1, 5, 2));
				p1.add(siteNamePanel);
				p1.add(validityPanel);
				DefaultOidsPanel oids = new DefaultOidsPanel(metadata);
				JPanel panel = new JPanel(new BorderLayout());
				panel.add(p1, BorderLayout.NORTH);
				panel.add(oids, BorderLayout.CENTER);
				int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, 
						panel, Translation.get("gb.newCertificateDefaults"), JOptionPane.OK_CANCEL_OPTION,
						JOptionPane.QUESTION_MESSAGE, new ImageIcon(getClass().getResource("/settings_32x32.png")));
				if (result == JOptionPane.OK_OPTION) {
					try {
						oids.update(metadata);
						certificateConfig.duration = Integer.parseInt(validityTF.getText());
						certificateConfig.store(config, metadata);
						config.save();
						Map<String, String> updates = new HashMap<String, String>();
						updates.put(Keys.web.siteName, siteNameTF.getText());
						gitblitSettings.saveSettings(updates);
					} catch (Exception e1) {
						Utils.showException(GitblitAuthority.this, e1);
					}
				}
			}
		});
		newSSLCertificate = new JButton(new ImageIcon(getClass().getResource("/rosette_16x16.png")));
		newSSLCertificate.setFocusable(false);
		newSSLCertificate.setToolTipText(Translation.get("gb.newSSLCertificate"));		
		newSSLCertificate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Date defaultExpiration = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);
				NewSSLCertificateDialog dialog = new NewSSLCertificateDialog(GitblitAuthority.this, defaultExpiration);
				dialog.setModal(true);
				dialog.setVisible(true);
				if (dialog.isCanceled()) {
					return;
				}
				final Date expires = dialog.getExpiration();
				final String hostname = dialog.getHostname();
				final boolean serveCertificate = dialog.isServeCertificate();
				AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
					@Override
					protected Boolean doRequest() throws IOException {
						if (!prepareX509Infrastructure()) {
							return false;
						}
						File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
						PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword);
						X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword);
						X509Metadata metadata = new X509Metadata(hostname, caKeystorePassword);
						setMetadataDefaults(metadata);
						metadata.notAfter = expires;
						File serverKeystoreFile = new File(folder, X509Utils.SERVER_KEY_STORE);
						X509Certificate cert = X509Utils.newSSLCertificate(metadata, caPrivateKey, caCert, serverKeystoreFile, GitblitAuthority.this);
						boolean hasCert = cert != null;
						if (hasCert && serveCertificate) {
							Map<String, String> updates = new HashMap<String, String>();
							updates.put(Keys.server.certificateAlias, metadata.commonName);
							gitblitSettings.saveSettings(updates);
						}
						return hasCert;
					}
					@Override
					protected void onSuccess() {
						if (serveCertificate) {
							JOptionPane.showMessageDialog(GitblitAuthority.this, 
									MessageFormat.format(Translation.get("gb.sslCertificateGeneratedRestart"), hostname),
									Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE);
						} else {
							JOptionPane.showMessageDialog(GitblitAuthority.this, 
								MessageFormat.format(Translation.get("gb.sslCertificateGenerated"), hostname),
								Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE);
						}
					}
				};
				worker.execute();
			}
		});
		JButton emailBundle = new JButton(new ImageIcon(getClass().getResource("/mail_16x16.png")));
		emailBundle.setFocusable(false);
		emailBundle.setToolTipText(Translation.get("gb.emailCertificateBundle"));		
		emailBundle.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				int row = table.getSelectedRow();
				if (row < 0) {
					return;
				}
				int modelIndex = table.convertRowIndexToModel(row);
				final UserCertificateModel ucm = tableModel.get(modelIndex);
				if (ArrayUtils.isEmpty(ucm.certs)) {
					JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.pleaseGenerateClientCertificate"), ucm.user.getDisplayName()));
				}
				final File zip = new File(folder, X509Utils.CERTS + File.separator + ucm.user.username + File.separator + ucm.user.username + ".zip");
				if (!zip.exists()) {
					return;
				}
				AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
					@Override
					protected Boolean doRequest() throws IOException {
						X509Metadata metadata = new X509Metadata(ucm.user.username, "whocares");
						metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, Constants.NAME);
						if (StringUtils.isEmpty(metadata.serverHostname)) {
							metadata.serverHostname = Constants.NAME;
						}
						metadata.userDisplayname = ucm.user.getDisplayName();
						return sendEmail(ucm.user, metadata, zip);
					}
					@Override
					protected void onSuccess() {
						JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.clientCertificateBundleSent"),
								ucm.user.getDisplayName()));
					}
				};
				worker.execute();				
			}
		});
		JButton logButton = new JButton(new ImageIcon(getClass().getResource("/script_16x16.png")));
		logButton.setFocusable(false);
		logButton.setToolTipText(Translation.get("gb.log"));		
		logButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				File log = new File(folder, X509Utils.CERTS + File.separator + "log.txt");
				if (log.exists()) {
					String content = FileUtils.readContent(log,  "\n");
					JTextArea textarea = new JTextArea(content);
					JScrollPane scrollPane = new JScrollPane(textarea);
					scrollPane.setPreferredSize(new Dimension(700, 400));
					JOptionPane.showMessageDialog(GitblitAuthority.this, scrollPane, log.getAbsolutePath(), JOptionPane.INFORMATION_MESSAGE);
				}
			}
		});
		final JTextField filterTextfield = new JTextField(15);
		filterTextfield.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				filterUsers(filterTextfield.getText());
			}
		});
		filterTextfield.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				filterUsers(filterTextfield.getText());
			}
		});
		JToolBar buttonControls = new JToolBar(JToolBar.HORIZONTAL);
		buttonControls.setFloatable(false);
		buttonControls.add(certificateDefaultsButton);
		buttonControls.add(newSSLCertificate);
		buttonControls.add(emailBundle);
		buttonControls.add(logButton);
		JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, Utils.MARGIN, Utils.MARGIN));
		userControls.add(new JLabel(Translation.get("gb.filter")));
		userControls.add(filterTextfield);
		JPanel topPanel = new JPanel(new BorderLayout(0, 0));
		topPanel.add(buttonControls, BorderLayout.WEST);
		topPanel.add(userControls, BorderLayout.EAST);
		JPanel leftPanel = new JPanel(new BorderLayout());
		leftPanel.add(topPanel, BorderLayout.NORTH);
		leftPanel.add(usersPanel, BorderLayout.CENTER);
		userCertificatePanel.setMinimumSize(new Dimension(375, 10));
		JLabel statusLabel = new JLabel();
		statusLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		if (X509Utils.unlimitedStrength) {
			statusLabel.setText("JCE Unlimited Strength Jurisdiction Policy");
		} else {
			statusLabel.setText("JCE Standard Encryption Policy");
		}
		JPanel root = new JPanel(new BorderLayout()) {
			private static final long serialVersionUID = 1L;
			public Insets getInsets() {
				return Utils.INSETS;
			}
		};
		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, userCertificatePanel);
		splitPane.setDividerLocation(1d);
		root.add(splitPane, BorderLayout.CENTER);
		root.add(statusLabel, BorderLayout.SOUTH);
		return root;
	}
