James Moger
2012-11-26 e571c4ae9d9883ba4c4a1739dd9948bf096a2cff
More refinements to GCA
1 files renamed
2 files added
10 files modified
354 ■■■■ changed files
build.xml 1 ●●●● patch | view | raw | blame | history
distrib/gitblit.properties 11 ●●●●● patch | view | raw | blame | history
resources/mail_16x16.png patch | view | raw | blame | history
src/com/gitblit/authority/AuthorityWorker.java 58 ●●●●● patch | view | raw | blame | history
src/com/gitblit/authority/GitblitAuthority.java 158 ●●●● patch | view | raw | blame | history
src/com/gitblit/authority/NewClientCertificateDialog.java 14 ●●●● patch | view | raw | blame | history
src/com/gitblit/authority/NewSSLCertificateDialog.java 11 ●●●● patch | view | raw | blame | history
src/com/gitblit/authority/UserCertificateConfig.java 1 ●●●● patch | view | raw | blame | history
src/com/gitblit/authority/UserCertificateModel.java 23 ●●●● patch | view | raw | blame | history
src/com/gitblit/authority/UserCertificatePanel.java 53 ●●●● patch | view | raw | blame | history
src/com/gitblit/authority/X509CertificateViewer.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/utils/X509Utils.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 12 ●●●● patch | view | raw | blame | history
build.xml
@@ -766,6 +766,7 @@
            <resource file="${basedir}/resources/settings_16x16.png" />
            <resource file="${basedir}/resources/settings_32x32.png" />
            <resource file="${basedir}/resources/search-icon.png" />
            <resource file="${basedir}/resources/mail_16x16.png" />
            <resource file="${basedir}/resources/blank.png" />
            <resource file="${basedir}/resources/bullet_green.png" />
            <resource file="${basedir}/resources/bullet_orange.png" />
distrib/gitblit.properties
@@ -1133,6 +1133,17 @@
# This is provided for convenience, its probably more secure to set this value
# using the --storePassword command line parameter.
#
# If you are using the official JRE or JDK from Oracle you may not have the
# JCE Unlimited Strength Jurisdiction Policy files bundled with your JVM.  Because
# of this, your store/key password can not exceed 7 characters.  If you require
# longer passwords you may need to install the JCE Unlimited Strength Jurisdiction
# Policy files from Oracle.
#
# http://www.oracle.com/technetwork/java/javase/downloads/index.html
#
# Gitblit and the Gitblit Certificate Authority will both indicate if Unlimited
# Strength encryption is available.
#
# SINCE 0.5.0
# RESTART REQUIRED
server.storePassword = gitblit
resources/mail_16x16.png
src/com/gitblit/authority/AuthorityWorker.java
New file
@@ -0,0 +1,58 @@
/*
 * Copyright 2012 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.authority;
import java.awt.Component;
import java.awt.Cursor;
import java.io.IOException;
import javax.swing.SwingWorker;
public abstract class AuthorityWorker extends SwingWorker<Boolean, Void> {
    private final Component parent;
    public AuthorityWorker(Component parent) {
        this.parent = parent;
        parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    }
    @Override
    protected Boolean doInBackground() throws IOException {
        return doRequest();
    }
    protected void done() {
        parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        try {
            Boolean success = get();
            if (success) {
                onSuccess();
            } else {
                onFailure();
            }
        } catch (Throwable t) {
            Utils.showException(parent, t);
        }
    }
    protected abstract Boolean doRequest() throws IOException;
    protected abstract void onSuccess();
    protected void onFailure() {
    }
}
src/com/gitblit/authority/GitblitAuthority.java
@@ -88,6 +88,7 @@
import com.gitblit.client.HeaderPanel;
import com.gitblit.client.Translation;
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.utils.X509Utils;
@@ -364,7 +365,10 @@
            public void newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail) {
                prepareX509Infrastructure();
                Date notAfter = metadata.notAfter;
                metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, "localhost");
                metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, Constants.NAME);
                if (StringUtils.isEmpty(metadata.serverHostname)) {
                    metadata.serverHostname = Constants.NAME;
                }
                UserModel user = ucm.user;                
                
                // set default values from config file
@@ -421,38 +425,7 @@
                table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex);
                
                if (sendEmail) {
                    // send email
                    try {
                        if (mail.isReady()) {
                            Message message = mail.createMessage(user.emailAddress);
                            message.setSubject("Your Gitblit client certificate for " + metadata.serverHostname);
                            // body of email
                            String body = X509Utils.processTemplate(new File(caKeystoreFile.getParentFile(), "mail.tmpl"), metadata);
                            if (StringUtils.isEmpty(body)) {
                                body = MessageFormat.format("Hi {0}\n\nHere is your client certificate bundle.\nInside the zip file are installation instructions.", user.getDisplayName());
                            }
                            Multipart mp = new MimeMultipart();
                            MimeBodyPart messagePart = new MimeBodyPart();
                            messagePart.setText(body);
                            mp.addBodyPart(messagePart);
                            // attach zip
                            MimeBodyPart filePart = new MimeBodyPart();
                            FileDataSource fds = new FileDataSource(zip);
                            filePart.setDataHandler(new DataHandler(fds));
                            filePart.setFileName(fds.getName());
                            mp.addBodyPart(filePart);
                            message.setContent(mp);
                            mail.sendNow(message);
                        } else {
                            JOptionPane.showMessageDialog(GitblitAuthority.this, "Sorry, the mail server settings are not configured properly.\nCan not send email.", Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
                        }
                    } catch (Exception e) {
                        Utils.showException(GitblitAuthority.this, e);
                    }
                    sendEmail(user, metadata, zip);
                }
            }
            
@@ -527,7 +500,7 @@
        
        certificateDefaultsButton = new JButton(new ImageIcon(getClass().getResource("/settings_16x16.png")));
        certificateDefaultsButton.setFocusable(false);
        certificateDefaultsButton.setToolTipText(Translation.get("gb.certificateDefaults"));
        certificateDefaultsButton.setToolTipText(Translation.get("gb.newCertificateDefaults"));
        certificateDefaultsButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
@@ -570,7 +543,7 @@
                panel.add(oids, BorderLayout.CENTER);
                int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, 
                        panel, Translation.get("gb.certificateDefaults"), JOptionPane.OK_CANCEL_OPTION,
                        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 {
@@ -587,22 +560,27 @@
            }
        });
        
        JButton newWebCertificate = new JButton(new ImageIcon(getClass().getResource("/rosette_16x16.png")));
        newWebCertificate.setFocusable(false);
        newWebCertificate.setToolTipText(Translation.get("gb.newWebCertificate"));
        newWebCertificate.addActionListener(new ActionListener() {
        JButton 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);
                NewWebCertificateDialog dialog = new NewWebCertificateDialog(GitblitAuthority.this, defaultExpiration);
                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();
                AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
                    @Override
                    protected Boolean doRequest() throws IOException {
                prepareX509Infrastructure();
                Date expires = dialog.getExpiration();
                String hostname = dialog.getHostname();
                
                // read CA private key and certificate
                File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
@@ -613,7 +591,63 @@
                X509Metadata metadata = new X509Metadata(hostname, caKeystorePassword);
                metadata.notAfter = expires;
                File serverKeystoreFile = new File(folder, X509Utils.SERVER_KEY_STORE);
                X509Utils.newSSLCertificate(metadata, caPrivateKey, caCert, serverKeystoreFile, GitblitAuthority.this);
                        X509Certificate cert = X509Utils.newSSLCertificate(metadata, caPrivateKey, caCert, serverKeystoreFile, GitblitAuthority.this);
                        return cert != null;
                    }
                    @Override
                    protected void onSuccess() {
                        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();
                        sendEmail(ucm.user, metadata, zip);
                        return true;
                    }
                    @Override
                    protected void onSuccess() {
                        JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.clientCertificateBundleSent"),
                                ucm.user.getDisplayName()));
                    }
                };
                worker.execute();
            }
        });
        
@@ -631,7 +665,8 @@
        
        JPanel buttonControls = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, Utils.MARGIN));
        buttonControls.add(certificateDefaultsButton);
        buttonControls.add(newWebCertificate);
        buttonControls.add(newSSLCertificate);
        buttonControls.add(emailBundle);
        JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, Utils.MARGIN, Utils.MARGIN));
        userControls.add(new JLabel(Translation.get("gb.filter")));
@@ -708,4 +743,39 @@
            }
        }
    }
    private void sendEmail(UserModel user, X509Metadata metadata, File zip) {
        // send email
        try {
            if (mail.isReady()) {
                Message message = mail.createMessage(user.emailAddress);
                message.setSubject("Your Gitblit client certificate for " + metadata.serverHostname);
                // body of email
                String body = X509Utils.processTemplate(new File(folder, X509Utils.CERTS + File.separator + "mail.tmpl"), metadata);
                if (StringUtils.isEmpty(body)) {
                    body = MessageFormat.format("Hi {0}\n\nHere is your client certificate bundle.\nInside the zip file are installation instructions.", user.getDisplayName());
                }
                Multipart mp = new MimeMultipart();
                MimeBodyPart messagePart = new MimeBodyPart();
                messagePart.setText(body);
                mp.addBodyPart(messagePart);
                // attach zip
                MimeBodyPart filePart = new MimeBodyPart();
                FileDataSource fds = new FileDataSource(zip);
                filePart.setDataHandler(new DataHandler(fds));
                filePart.setFileName(fds.getName());
                mp.addBodyPart(filePart);
                message.setContent(mp);
                mail.sendNow(message);
            } else {
                JOptionPane.showMessageDialog(GitblitAuthority.this, "Sorry, the mail server settings are not configured properly.\nCan not send email.", Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
            }
        } catch (Exception e) {
            Utils.showException(GitblitAuthority.this, e);
        }
    }
}
src/com/gitblit/authority/NewClientCertificateDialog.java
@@ -16,6 +16,7 @@
package com.gitblit.authority;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Insets;
@@ -30,6 +31,8 @@
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import org.bouncycastle.util.Arrays;
@@ -64,7 +67,6 @@
                return Utils.INSETS;
            }
        };
        content.add(new HeaderPanel(Translation.get("gb.newCertificate") + " (" + displayname + ")", "rosette_16x16.png"), BorderLayout.NORTH);
        
        expirationDate = new JDateChooser(defaultExpiration);
        pw1 = new JPasswordField(20);
@@ -91,7 +93,6 @@
            panel.add(sendEmail);
        }
        content.add(panel, BorderLayout.CENTER);
        
        JButton ok = new JButton(Translation.get("gb.ok"));
        ok.addActionListener(new ActionListener() {
@@ -114,8 +115,17 @@
        controls.add(ok);
        controls.add(cancel);
        
        JTextArea message = new JTextArea(Translation.get("gb.newClientCertificateMessage"));
        message.setLineWrap(true);
        message.setWrapStyleWord(true);
        message.setEditable(false);
        message.setPreferredSize(new Dimension(300, 100));
        content.add(new JScrollPane(message), BorderLayout.CENTER);
        content.add(panel, BorderLayout.NORTH);
        content.add(controls, BorderLayout.SOUTH);
        
        getContentPane().add(new HeaderPanel(Translation.get("gb.newCertificate") + " (" + displayname + ")", "rosette_16x16.png"), BorderLayout.NORTH);
        getContentPane().add(content, BorderLayout.CENTER);
        pack();
        
src/com/gitblit/authority/NewSSLCertificateDialog.java
File was renamed from src/com/gitblit/authority/NewWebCertificateDialog.java
@@ -35,7 +35,7 @@
import com.gitblit.utils.StringUtils;
import com.toedter.calendar.JDateChooser;
public class NewWebCertificateDialog extends JDialog {
public class NewSSLCertificateDialog extends JDialog {
    private static final long serialVersionUID = 1L;
    
@@ -43,10 +43,10 @@
    JTextField hostname;
    boolean isCanceled = true;
    public NewWebCertificateDialog(Frame owner, Date defaultExpiration) {
    public NewSSLCertificateDialog(Frame owner, Date defaultExpiration) {
        super(owner);
        
        setTitle(Translation.get("gb.newWebCertificate"));
        setTitle(Translation.get("gb.newSSLCertificate"));
        
        JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) {            
            private static final long serialVersionUID = 1L;
@@ -57,7 +57,6 @@
                return Utils.INSETS;
            }
        };
        content.add(new HeaderPanel(Translation.get("gb.newWebCertificate"), "rosette_16x16.png"), BorderLayout.NORTH);
        
        expirationDate = new JDateChooser(defaultExpiration);
        hostname = new JTextField(20);
@@ -69,8 +68,6 @@
        panel.add(new JLabel(Translation.get("gb.expires")));
        panel.add(expirationDate);
        content.add(panel, BorderLayout.CENTER);
        
        JButton ok = new JButton(Translation.get("gb.ok"));
        ok.addActionListener(new ActionListener() {
@@ -93,8 +90,10 @@
        controls.add(ok);
        controls.add(cancel);
        
        content.add(panel, BorderLayout.CENTER);
        content.add(controls, BorderLayout.SOUTH);
        
        getContentPane().add(new HeaderPanel(Translation.get("gb.newSSLCertificate"), "rosette_16x16.png"), BorderLayout.NORTH);
        getContentPane().add(content, BorderLayout.CENTER);
        pack();
        
src/com/gitblit/authority/UserCertificateConfig.java
@@ -51,6 +51,7 @@
                uc.expires = df.parse(c.getString("user", username, "expires"));
            } catch (ParseException e) {
                LoggerFactory.getLogger(UserCertificateConfig.class).error("Failed to parse date!", e);
            } catch (NullPointerException e) {
            }
            uc.notes = c.getString("user", username, "notes");
            uc.revoked = new ArrayList<String>(Arrays.asList(c.getStringList("user", username, "revoked")));            
src/com/gitblit/authority/UserCertificateModel.java
@@ -27,6 +27,7 @@
import com.gitblit.Constants;
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.utils.X509Utils.RevocationReason;
@@ -42,14 +43,20 @@
        }
        
        public void update(Config config) {
            if (expires != null) {
            if (expires == null) {
                config.unset("user",  user.username, "expires");
            } else {
                SimpleDateFormat df = new SimpleDateFormat(Constants.ISO8601);
                config.setString("user", user.username, "expires", df.format(expires));
            }
            if (notes != null) {
            if (StringUtils.isEmpty(notes)) {
                config.unset("user",  user.username, "notes");
            } else {
                config.setString("user", user.username, "notes", notes);
            }
            if (!ArrayUtils.isEmpty(revoked)) {
            if (ArrayUtils.isEmpty(revoked)) {
                config.unset("user",  user.username, "revoked");
            } else {
                config.setStringList("user", user.username, "revoked", revoked);
            }
        }
@@ -64,6 +71,16 @@
                revoked = new ArrayList<String>();
            }
            revoked.add(serial.toString() + ":" + reason.ordinal());
            expires = null;
            for (X509Certificate cert : certs) {
                if (!isRevoked(cert.getSerialNumber())) {
                    if (!isExpired(cert.getNotAfter())) {
                        if (expires == null || cert.getNotAfter().after(expires)) {
                            expires = cert.getNotAfter();
                        }
                    }
                }
            }
        }
        
        public boolean isRevoked(BigInteger serial) {
src/com/gitblit/authority/UserCertificatePanel.java
@@ -16,14 +16,15 @@
package com.gitblit.authority;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Date;
import javax.swing.ImageIcon;
@@ -157,19 +158,31 @@
                        return;
                    }
                    
                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                    UserModel user = ucm.user;
                    X509Metadata metadata = new X509Metadata(user.username, dialog.getPassword());
                    final boolean sendEmail = dialog.sendEmail();
                    final UserModel user = ucm.user;
                    final X509Metadata metadata = new X509Metadata(user.username, dialog.getPassword());
                    metadata.userDisplayname = user.getDisplayName();
                    metadata.emailAddress = user.emailAddress;                
                    metadata.passwordHint = dialog.getPasswordHint();
                    metadata.notAfter = dialog.getExpiration();
                    newCertificate(ucm, metadata, dialog.sendEmail());
                    AuthorityWorker worker = new AuthorityWorker(UserCertificatePanel.this.owner) {
                        @Override
                        protected Boolean doRequest() throws IOException {
                            newCertificate(ucm, metadata, sendEmail);
                            return true;
                        }
                        @Override
                        protected void onSuccess() {
                            JOptionPane.showMessageDialog(UserCertificatePanel.this.owner,
                                    MessageFormat.format(Translation.get("gb.clientCertificateGenerated"), user.getDisplayName()),
                                    Translation.get("gb.newCertificate"), JOptionPane.INFORMATION_MESSAGE);
                        }
                    };
                    worker.execute();
                } catch (Exception x) {
                    Utils.showException(UserCertificatePanel.this, x);
                } finally {
                    setCursor(Cursor.getDefaultCursor());
                }
            }
        });
@@ -184,7 +197,7 @@
                        return;
                    }
                    int modelIndex = table.convertRowIndexToModel(row);
                    X509Certificate cert = tableModel.get(modelIndex);
                    final X509Certificate cert = tableModel.get(modelIndex);
                    
                    String [] choices = new String[RevocationReason.reasons.length];
                    for (int i = 0; i < choices.length; i++) {
@@ -197,13 +210,14 @@
                    if (choice == null) {
                        return;
                    }
                    RevocationReason reason = RevocationReason.unspecified;
                    RevocationReason selection = RevocationReason.unspecified;
                    for (int i = 0 ; i < choices.length; i++) {
                        if (choices[i].equals(choice)) {
                            reason = RevocationReason.reasons[i];
                            selection = RevocationReason.reasons[i];
                            break;
                        }
                    }
                    final RevocationReason reason = selection;
                    if (!ucm.isRevoked(cert.getSerialNumber())) {
                        if (ucm.certs.size() == 1) {
                            // no other certificates
@@ -222,12 +236,27 @@
                            }
                            ucm.expires = newExpires;
                        }
                        AuthorityWorker worker = new AuthorityWorker(UserCertificatePanel.this.owner) {
                            @Override
                            protected Boolean doRequest() throws IOException {
                        revoke(ucm, cert, reason);
                                return true;
                            }
                            @Override
                            protected void onSuccess() {
                                JOptionPane.showMessageDialog(UserCertificatePanel.this.owner,
                                        MessageFormat.format(Translation.get("gb.certificateRevoked"), cert.getSerialNumber(), cert.getIssuerDN().getName()),
                                        Translation.get("gb.revokeCertificate"), JOptionPane.INFORMATION_MESSAGE);
                            }
                        };
                        worker.execute();
                    }
                } catch (Exception x) {
                    Utils.showException(UserCertificatePanel.this, x);
                } finally {
                    setCursor(Cursor.getDefaultCursor());
                }
            }
        });
src/com/gitblit/authority/X509CertificateViewer.java
@@ -56,7 +56,6 @@
                return Utils.INSETS;
            }
        };
        content.add(new HeaderPanel("certificiate", "rosette_16x16.png"), BorderLayout.NORTH);
        
        DateFormat df = DateFormat.getDateTimeInstance();
        
@@ -96,6 +95,7 @@
        
        content.add(controls, BorderLayout.SOUTH);
        
        getContentPane().add(new HeaderPanel(Translation.get("gb.certificate"), "rosette_16x16.png"), BorderLayout.NORTH);
        getContentPane().add(content, BorderLayout.CENTER);
        pack();
        
src/com/gitblit/utils/X509Utils.java
@@ -561,7 +561,7 @@
                    new Certificate[] { cert, caCert });
            saveKeyStore(targetStoreFile, serverStore, sslMetadata.password);
            
            x509log.log(MessageFormat.format("New web certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName()));
            x509log.log(MessageFormat.format("New SSL certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName()));
            return cert;
        } catch (Throwable t) {
            throw new RuntimeException("Failed to generate SSL certificate!", t);
@@ -935,12 +935,20 @@
            String message = FileUtils.readContent(template, "\n");
            if (!StringUtils.isEmpty(message)) {
                content = message;
                if (!StringUtils.isEmpty(metadata.serverHostname)) {
                content = content.replace("$serverHostname", metadata.serverHostname);
                }
                if (!StringUtils.isEmpty(metadata.commonName)) {
                content = content.replace("$username", metadata.commonName);
                }
                if (!StringUtils.isEmpty(metadata.userDisplayname)) {
                content = content.replace("$userDisplayname", metadata.userDisplayname);
                }
                if (!StringUtils.isEmpty(metadata.passwordHint)) {
                content = content.replace("$storePasswordHint", metadata.passwordHint);
            }
        }
        }
        return content;
    }
    
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -417,6 +417,14 @@
gb.time.inDays = in {0} days
gb.hostname = hostname
gb.hostnameRequired = Please enter a hostname
gb.newWebCertificate = new server SSL certificate
gb.certificateDefaults = certificate defaults
gb.newSSLCertificate = new server SSL certificate
gb.newCertificateDefaults = new certificate defaults
gb.duration = duration
gb.certificateRevoked = Certificate {0,number,0} has been revoked
gb.clientCertificateGenerated = Successfully generated new client certificate for {0}
gb.sslCertificateGenerated = Successfully generated new server SSL certificate for {0}
gb.newClientCertificateMessage = NOTE:\nThe 'password' is not the user's password, it is the password to protect the user's keystore.  This password is not saved so you must also enter a 'hint' which will be included in the user's README instructions.
gb.certificate = certificate
gb.emailCertificateBundle = email client certificate bundle
gb.pleaseGenerateClientCertificate = Please generate a client certificate for {0}
gb.clientCertificateBundleSent = Client certificate bundle for {0} sent