From: James Moger Date: Sun, 25 Nov 2012 16:35:31 +0000 (-0500) Subject: Gitblit Certificate Authority revisions. Eliminate certificate scripts. X-Git-Tag: v1.2.0~76 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c8b26c51aa67fc9345b624e36aab6f819e7eed74;p=gitblit.git Gitblit Certificate Authority revisions. Eliminate certificate scripts. --- diff --git a/build.xml b/build.xml index 8a7fd54b..aa247d39 100644 --- a/build.xml +++ b/build.xml @@ -190,9 +190,9 @@ - - + + @@ -461,7 +461,6 @@ - @@ -494,7 +493,6 @@ - @@ -671,7 +669,6 @@ - @@ -767,6 +764,7 @@ + @@ -775,6 +773,7 @@ + @@ -1116,16 +1115,6 @@ summary="Gitblit Manager v${gb.version} (Swing tool to remotely administer a Gitblit server)" labels="Featured, Type-Package, OpSys-All" /> - - - 0) { - File folder = new File(System.getProperty("user.dir")); + final File folder = new File(System.getProperty("user.dir")); File certificatesConf = new File(folder, X509Utils.CA_CONFIG); File serverKeyStore = new File(folder, X509Utils.SERVER_KEY_STORE); File serverTrustStore = new File(folder, X509Utils.SERVER_TRUST_STORE); @@ -215,7 +219,27 @@ public class GitBlitServer { } metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR); - X509Utils.prepareX509Infrastructure(metadata, folder); + X509Utils.prepareX509Infrastructure(metadata, folder, new X509Log() { + @Override + public void log(String message) { + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(new File(folder, X509Utils.CERTS + File.separator + "log.txt"), true)); + writer.write(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1}", new Date(), message)); + writer.newLine(); + writer.flush(); + } catch (Exception e) { + LoggerFactory.getLogger(GitblitAuthority.class).error("Failed to append log entry!", e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } + }); if (serverKeyStore.exists()) { Connector secureConnector = createSSLConnector(serverKeyStore, serverTrustStore, params.storePassword, diff --git a/src/com/gitblit/MakeCertificate.java b/src/com/gitblit/MakeCertificate.java deleted file mode 100644 index e3c39ff7..00000000 --- a/src/com/gitblit/MakeCertificate.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2011 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; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.security.Security; -import java.security.cert.X509Certificate; -import java.util.Date; - -import javax.security.auth.x500.X500Principal; - -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; - -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.gitblit.utils.TimeUtils; - -/** - * Utility class to generate self-signed certificates. - * - * @author James Moger - * - */ -public class MakeCertificate { - - private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME; - - public static void main(String... args) { - Params params = new Params(); - JCommander jc = new JCommander(params); - try { - jc.parse(args); - } catch (ParameterException t) { - System.err.println(t.getMessage()); - jc.usage(); - } - File keystore = new File("serverKeyStore.jks"); - generateSelfSignedCertificate(params.hostname, keystore, params.storePassword, - params.subject); - } - - public static void generateSelfSignedCertificate(String hostname, File keystore, - String keystorePassword, String info) { - try { - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); - - KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC); - kpGen.initialize(1024, new SecureRandom()); - KeyPair pair = kpGen.generateKeyPair(); - - // Generate self-signed certificate - X500Principal principal = new X500Principal(info); - - Date notBefore = new Date(System.currentTimeMillis() - TimeUtils.ONEDAY); - Date notAfter = new Date(System.currentTimeMillis() + 10 * TimeUtils.ONEYEAR); - BigInteger serial = BigInteger.valueOf(System.currentTimeMillis()); - - X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(principal, serial, - notBefore, notAfter, principal, pair.getPublic()); - ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption") - .setProvider(BC).build(pair.getPrivate()); - X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC) - .getCertificate(certGen.build(sigGen)); - cert.checkValidity(new Date()); - cert.verify(cert.getPublicKey()); - - // Save to keystore - KeyStore store = KeyStore.getInstance("JKS"); - if (keystore.exists()) { - FileInputStream fis = new FileInputStream(keystore); - store.load(fis, keystorePassword.toCharArray()); - fis.close(); - } else { - store.load(null); - } - store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(), - new java.security.cert.Certificate[] { cert }); - FileOutputStream fos = new FileOutputStream(keystore); - store.store(fos, keystorePassword.toCharArray()); - fos.close(); - } catch (Throwable t) { - t.printStackTrace(); - throw new RuntimeException("Failed to generate self-signed certificate!", t); - } - } - - /** - * JCommander Parameters class for MakeCertificate. - */ - @Parameters(separators = " ") - private static class Params { - - private static final FileSettings FILESETTINGS = new FileSettings(Constants.PROPERTIES_FILE); - - @Parameter(names = { "--hostname" }, description = "Server Hostname", required = true) - public String hostname; - - @Parameter(names = { "--subject" }, description = "Certificate subject", required = true) - public String subject; - - @Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.") - public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, ""); - } -} diff --git a/src/com/gitblit/authority/DefaultOidsPanel.java b/src/com/gitblit/authority/DefaultOidsPanel.java new file mode 100644 index 00000000..12b919fa --- /dev/null +++ b/src/com/gitblit/authority/DefaultOidsPanel.java @@ -0,0 +1,80 @@ +/* + * 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.GridLayout; + +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.gitblit.client.Translation; +import com.gitblit.utils.X509Utils.X509Metadata; + +public class DefaultOidsPanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private JTextField organizationalUnit; + private JTextField organization; + private JTextField locality; + private JTextField stateProvince; + private JTextField countryCode; + + public DefaultOidsPanel(X509Metadata metadata) { + super(); + + organizationalUnit = new JTextField(metadata.getOID("OU", ""), 20); + organization = new JTextField(metadata.getOID("O", ""), 20); + locality = new JTextField(metadata.getOID("L", ""), 20); + stateProvince = new JTextField(metadata.getOID("ST", ""), 20); + countryCode = new JTextField(metadata.getOID("C", ""), 20); + + setLayout(new GridLayout(0, 1, Utils.MARGIN, Utils.MARGIN)); + add(Utils.newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnit)); + add(Utils.newFieldPanel(Translation.get("gb.organization") + " (O)", organization)); + add(Utils.newFieldPanel(Translation.get("gb.locality") + " (L)", locality)); + add(Utils.newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince)); + add(Utils.newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode)); + } + + public void update(X509Metadata metadata) { + metadata.setOID("OU", organizationalUnit.getText()); + metadata.setOID("O", organization.getText()); + metadata.setOID("L", locality.getText()); + metadata.setOID("ST", stateProvince.getText()); + metadata.setOID("C", countryCode.getText()); + } + + public String getOrganizationalUnit() { + return organizationalUnit.getText(); + } + + public String getOrganization() { + return organization.getText(); + } + + public String getLocality() { + return locality.getText(); + } + + public String getStateProvince() { + return stateProvince.getText(); + } + + public String getCountryCode() { + return countryCode.getText(); + } +} diff --git a/src/com/gitblit/authority/GitblitAuthority.java b/src/com/gitblit/authority/GitblitAuthority.java index 5e8b30ec..7734a151 100644 --- a/src/com/gitblit/authority/GitblitAuthority.java +++ b/src/com/gitblit/authority/GitblitAuthority.java @@ -29,10 +29,13 @@ import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedInputStream; +import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; +import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; +import java.security.PrivateKey; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.MessageFormat; @@ -51,6 +54,9 @@ import javax.mail.Multipart; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import javax.swing.ImageIcon; +import javax.swing.InputVerifier; +import javax.swing.JButton; +import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; @@ -60,6 +66,7 @@ import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.RowFilter; +import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -69,6 +76,7 @@ import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; +import org.slf4j.LoggerFactory; import com.gitblit.ConfigUserService; import com.gitblit.Constants; @@ -81,8 +89,10 @@ import com.gitblit.client.HeaderPanel; import com.gitblit.client.Translation; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; +import com.gitblit.utils.TimeUtils; import com.gitblit.utils.X509Utils; import com.gitblit.utils.X509Utils.RevocationReason; +import com.gitblit.utils.X509Utils.X509Log; import com.gitblit.utils.X509Utils.X509Metadata; /** @@ -91,7 +101,7 @@ import com.gitblit.utils.X509Utils.X509Metadata; * @author James Moger * */ -public class GitblitAuthority extends JFrame { +public class GitblitAuthority extends JFrame implements X509Log { private static final long serialVersionUID = 1L; @@ -112,6 +122,10 @@ public class GitblitAuthority extends JFrame { private int defaultDuration; private TableRowSorter defaultSorter; + + private MailExecutor mail; + + private JButton certificateDefaultsButton; public static void main(String... args) { EventQueue.invokeLater(new Runnable() { @@ -170,7 +184,7 @@ public class GitblitAuthority extends JFrame { // try to restore saved window size if (StringUtils.isEmpty(sz)) { - setSize(850, 500); + setSize(900, 600); } else { String[] chunks = sz.split("x"); int width = Integer.parseInt(chunks[0]); @@ -218,6 +232,7 @@ public class GitblitAuthority extends JFrame { return null; } gitblitSettings = new FileSettings(file.getAbsolutePath()); + mail = new MailExecutor(gitblitSettings); caKeystorePassword = gitblitSettings.getString(Keys.server.storePassword, null); String us = gitblitSettings.getString(Keys.realm.userService, "users.conf"); String ext = us.substring(us.lastIndexOf(".") + 1).toLowerCase(); @@ -243,7 +258,9 @@ public class GitblitAuthority extends JFrame { private void load(File folder) { this.folder = folder; this.userService = loadUsers(folder); - if (userService != null) { + if (userService == null) { + JOptionPane.showMessageDialog(this, MessageFormat.format("Sorry, {0} doesn't look like a Gitblit GO installation.", folder)); + } else { // build empty certificate model for all users Map map = new HashMap(); for (String user : userService.getAllUsernames()) { @@ -273,9 +290,20 @@ public class GitblitAuthority extends JFrame { Collections.sort(tableModel.list); tableModel.fireTableDataChanged(); Utils.packColumns(table, Utils.MARGIN); + + File caKeystore = new File(folder, X509Utils.CA_KEY_STORE); + if (!caKeystore.exists()) { + // show certificate defaults dialog + certificateDefaultsButton.doClick(); + } } } + private void prepareX509Infrastructure() { + X509Metadata metadata = new X509Metadata("localhost", caKeystorePassword); + X509Utils.prepareX509Infrastructure(metadata, folder, this); + } + private List findCerts(File folder, String username) { List list = new ArrayList(); File userFolder = new File(folder, X509Utils.CERTS + File.separator + username); @@ -310,6 +338,11 @@ public class GitblitAuthority extends JFrame { public Insets getInsets() { return Utils.INSETS; } + + @Override + public boolean isAllowEmail() { + return mail.isReady(); + } @Override public Date getDefaultExpiration() { @@ -329,6 +362,7 @@ public class GitblitAuthority extends JFrame { @Override public void newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail) { + prepareX509Infrastructure(); Date notAfter = metadata.notAfter; metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, "localhost"); UserModel user = ucm.user; @@ -367,8 +401,8 @@ public class GitblitAuthority extends JFrame { } File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE); - File zip = X509Utils.newClientBundle(metadata, caKeystoreFile, caKeystorePassword); - + File zip = X509Utils.newClientBundle(metadata, caKeystoreFile, caKeystorePassword, GitblitAuthority.this); + // save latest expiration date if (ucm.expires == null || metadata.notAfter.after(ucm.expires)) { ucm.expires = metadata.notAfter; @@ -389,7 +423,6 @@ public class GitblitAuthority extends JFrame { if (sendEmail) { // send email try { - MailExecutor mail = new MailExecutor(gitblitSettings); if (mail.isReady()) { Message message = mail.createMessage(user.emailAddress); message.setSubject("Your Gitblit client certificate for " + metadata.serverHostname); @@ -427,7 +460,7 @@ public class GitblitAuthority extends JFrame { public void revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason) { 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)) { + 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()) { @@ -451,6 +484,7 @@ public class GitblitAuthority extends JFrame { int modelIndex = table.convertRowIndexToModel(table.getSelectedRow()); tableModel.fireTableDataChanged(); table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex); + } } }; @@ -491,7 +525,99 @@ public class GitblitAuthority extends JFrame { usersPanel.add(new JScrollPane(table), BorderLayout.CENTER); usersPanel.setMinimumSize(new Dimension(400, 10)); - final JTextField filterTextfield = new JTextField(20); + certificateDefaultsButton = new JButton(new ImageIcon(getClass().getResource("/settings_16x16.png"))); + certificateDefaultsButton.setFocusable(false); + certificateDefaultsButton.setToolTipText(Translation.get("gb.certificateDefaults")); + 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 durationTF = new JTextField(4); + durationTF.setInputVerifier(verifier); + durationTF.setVerifyInputWhenFocusTarget(true); + durationTF.setText("" + certificateConfig.duration); + JPanel durationPanel = Utils.newFieldPanel(Translation.get("gb.duration"), durationTF, Translation.get("gb.duration.days").replace("{0}", "").trim()); + DefaultOidsPanel oids = new DefaultOidsPanel(metadata); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(durationPanel, BorderLayout.NORTH); + panel.add(oids, BorderLayout.CENTER); + + int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, + panel, Translation.get("gb.certificateDefaults"), 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(durationTF.getText()); + certificateConfig.store(config, metadata); + config.save(); + + prepareX509Infrastructure(); + } catch (Exception e1) { + Utils.showException(GitblitAuthority.this, e1); + } + } + } + }); + + JButton newWebCertificate = new JButton(new ImageIcon(getClass().getResource("/rosette_16x16.png"))); + newWebCertificate.setFocusable(false); + newWebCertificate.setToolTipText(Translation.get("gb.newWebCertificate")); + newWebCertificate.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); + dialog.setModal(true); + dialog.setVisible(true); + if (dialog.isCanceled()) { + return; + } + prepareX509Infrastructure(); + Date expires = dialog.getExpiration(); + String hostname = dialog.getHostname(); + + // read CA private key and certificate + 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); + + // generate new SSL certificate + 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); + } + }); + + final JTextField filterTextfield = new JTextField(15); filterTextfield.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { filterUsers(filterTextfield.getText()); @@ -502,17 +628,33 @@ public class GitblitAuthority extends JFrame { filterUsers(filterTextfield.getText()); } }); + + JPanel buttonControls = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, Utils.MARGIN)); + buttonControls.add(certificateDefaultsButton); + buttonControls.add(newWebCertificate); - JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 5)); + 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(userControls, BorderLayout.NORTH); + 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() { @@ -521,7 +663,8 @@ public class GitblitAuthority extends JFrame { }; JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, userCertificatePanel); splitPane.setDividerLocation(1d); - root.add(splitPane); + root.add(splitPane, BorderLayout.CENTER); + root.add(statusLabel, BorderLayout.SOUTH); return root; } @@ -545,4 +688,24 @@ public class GitblitAuthority extends JFrame { sorter.setRowFilter(containsFilter); table.setRowSorter(sorter); } + + @Override + public void log(String message) { + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(new File(folder, X509Utils.CERTS + File.separator + "log.txt"), true)); + writer.write(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1}", new Date(), message)); + writer.newLine(); + writer.flush(); + } catch (Exception e) { + LoggerFactory.getLogger(GitblitAuthority.class).error("Failed to append log entry!", e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } } diff --git a/src/com/gitblit/authority/MakeClientCertificate.java b/src/com/gitblit/authority/MakeClientCertificate.java deleted file mode 100644 index 5829fc11..00000000 --- a/src/com/gitblit/authority/MakeClientCertificate.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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.io.File; -import java.text.MessageFormat; -import java.util.Date; - -import javax.activation.DataHandler; -import javax.activation.FileDataSource; -import javax.mail.Message; -import javax.mail.Multipart; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMultipart; - -import org.eclipse.jgit.storage.file.FileBasedConfig; -import org.eclipse.jgit.util.FS; - -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; -import com.gitblit.ConfigUserService; -import com.gitblit.Constants; -import com.gitblit.FileSettings; -import com.gitblit.IUserService; -import com.gitblit.Keys; -import com.gitblit.MailExecutor; -import com.gitblit.models.UserModel; -import com.gitblit.utils.StringUtils; -import com.gitblit.utils.TimeUtils; -import com.gitblit.utils.X509Utils; -import com.gitblit.utils.X509Utils.X509Metadata; - -/** - * Utility class to generate self-signed certificates. - * - * @author James Moger - * - */ -public class MakeClientCertificate { - - public static void main(String... args) throws Exception { - Params params = new Params(); - JCommander jc = new JCommander(params); - try { - jc.parse(args); - } catch (ParameterException t) { - System.err.println(t.getMessage()); - jc.usage(); - System.exit(-1); - } - - // Load the user list - String us = Params.FILESETTINGS.getString(Keys.realm.userService, "users.conf"); - String ext = us.substring(us.lastIndexOf(".") + 1).toLowerCase(); - IUserService service = null; - if (!ext.equals("conf") && !ext.equals("properties")) { - if (us.equals("com.gitblit.LdapUserService")) { - us = Params.FILESETTINGS.getString(Keys.realm.ldap.backingUserService, "users.conf"); - } else if (us.equals("com.gitblit.LdapUserService")) { - us = Params.FILESETTINGS.getString(Keys.realm.redmine.backingUserService, "users.conf"); - } - } - - if (us.endsWith(".conf")) { - service = new ConfigUserService(new File(us)); - } else { - throw new RuntimeException("Unsupported user service: " + us); - } - - // Confirm the user exists - UserModel user = service.getUserModel(params.username); - if (user == null) { - System.out.println(MessageFormat.format("Failed to find user \"{0}\" in {1}", params.username, us)); - System.exit(-1); - } - - File folder = new File(System.getProperty("user.dir")); - X509Metadata serverMetadata = new X509Metadata("localhost", params.storePassword); - X509Utils.prepareX509Infrastructure(serverMetadata, folder); - - File caStore = new File(folder, X509Utils.CA_KEY_STORE); - - X509Metadata clientMetadata = new X509Metadata(params.username, params.password); - clientMetadata.userDisplayname = user.getDisplayName(); - clientMetadata.emailAddress = user.emailAddress; - clientMetadata.serverHostname = params.serverHostname; - clientMetadata.passwordHint = params.hint; - - UserCertificateModel ucm = null; - - // set default values from config file - File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); - FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); - if (certificatesConfigFile.exists()) { - config.load(); - NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config); - certificateConfig.update(clientMetadata); - - ucm = UserCertificateConfig.KEY.parse(config).getUserCertificateModel(params.username); - } - - // set user's specified OID values - if (!StringUtils.isEmpty(user.organizationalUnit)) { - clientMetadata.oids.put("OU", user.organizationalUnit); - } - if (!StringUtils.isEmpty(user.organization)) { - clientMetadata.oids.put("O", user.organization); - } - if (!StringUtils.isEmpty(user.locality)) { - clientMetadata.oids.put("L", user.locality); - } - if (!StringUtils.isEmpty(user.stateProvince)) { - clientMetadata.oids.put("ST", user.stateProvince); - } - if (!StringUtils.isEmpty(user.countryCode)) { - clientMetadata.oids.put("C", user.countryCode); - } - - if (params.duration > 0) { - // overriding duration from command-line parameter - clientMetadata.notAfter = new Date(System.currentTimeMillis() + TimeUtils.ONEDAY * params.duration); - } - - // generate zip bundle - File zip = X509Utils.newClientBundle(clientMetadata, caStore, params.storePassword); - - String indent = " "; - System.out.println(MessageFormat.format("Client certificate bundle generated for {0}", params.username)); - System.out.print(indent); - System.out.println(zip); - - // update certificates.conf - if (ucm == null) { - ucm = new UserCertificateModel(new UserModel(params.username)); - } - - // save latest expiration date - if (ucm.expires == null || clientMetadata.notAfter.after(ucm.expires)) { - ucm.expires = clientMetadata.notAfter; - } - ucm.update(config); - config.save(); - - if (params.sendEmail) { - if (StringUtils.isEmpty(user.emailAddress)) { - System.out.print(indent); - System.out.println(MessageFormat.format("User \"{0}\" does not have an email address.", user.username)); - } else { - // send email - MailExecutor mail = new MailExecutor(Params.FILESETTINGS); - if (mail.isReady()) { - Message message = mail.createMessage(user.emailAddress); - message.setSubject("Your Gitblit client certificate for " + clientMetadata.serverHostname); - - // body of email - String body = X509Utils.processTemplate(new File(caStore.getParentFile(), "mail.tmpl"), clientMetadata); - 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); - System.out.println(); - System.out.println("Mail sent."); - } else { - System.out.print(indent); - System.out.println("Mail server is not properly configured. Can not send email."); - } - } - } - } - - /** - * JCommander Parameters class for MakeClientCertificate. - */ - @Parameters(separators = " ") - private static class Params { - - private static final FileSettings FILESETTINGS = new FileSettings(Constants.PROPERTIES_FILE); - - @Parameter(names = { "--username" }, description = "Username for certificate (CN)", required = true) - public String username; - - @Parameter(names = { "--password" }, description = "Password to secure user's certificate (<=7 chars unless JCE Unlimited Strength installed)", required = true) - public String password; - - @Parameter(names = { "--hint" }, description = "Hint for password", required = true) - public String hint; - - @Parameter(names = "--duration", description = "Number of days from now until the certificate expires") - public int duration = 0; - - @Parameter(names = "--storePassword", description = "Password for CA keystore.") - public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, ""); - - @Parameter(names = "--server", description = "Hostname or server identity") - public String serverHostname = Params.FILESETTINGS.getString(Keys.web.siteName, "localhost"); - - @Parameter(names = "--sendEmail", description = "Send an email to the user with their bundle") - public boolean sendEmail; - - } -} diff --git a/src/com/gitblit/authority/NewCertificateConfig.java b/src/com/gitblit/authority/NewCertificateConfig.java index e4db130e..ca047c82 100644 --- a/src/com/gitblit/authority/NewCertificateConfig.java +++ b/src/com/gitblit/authority/NewCertificateConfig.java @@ -36,13 +36,13 @@ public class NewCertificateConfig { } }; - public final String OU; - public final String O; - public final String L; - public final String ST; - public final String C; + public String OU; + public String O; + public String L; + public String ST; + public String C; - public final int duration; + public int duration; private NewCertificateConfig(final Config c) { duration = c.getInt("new", null, "duration", 0); @@ -69,4 +69,25 @@ public class NewCertificateConfig { metadata.oids.put(oid, value); } } + + public void store(Config c, X509Metadata metadata) { + store(c, "new", "organizationalUnit", metadata.getOID("OU", null)); + store(c, "new", "organization", metadata.getOID("O", null)); + store(c, "new", "locality", metadata.getOID("L", null)); + store(c, "new", "stateProvince", metadata.getOID("ST", null)); + store(c, "new", "countryCode", metadata.getOID("C", null)); + if (duration <= 0) { + c.unset("new", null, "duration"); + } else { + c.setInt("new", null, "duration", duration); + } + } + + private void store(Config c, String section, String name, String value) { + if (StringUtils.isEmpty(value)) { + c.unset(section, null, name); + } else { + c.setString(section, null, name, value); + } + } } \ No newline at end of file diff --git a/src/com/gitblit/authority/NewClientCertificateDialog.java b/src/com/gitblit/authority/NewClientCertificateDialog.java index ad4fe9aa..b04ae73c 100644 --- a/src/com/gitblit/authority/NewClientCertificateDialog.java +++ b/src/com/gitblit/authority/NewClientCertificateDialog.java @@ -50,12 +50,12 @@ public class NewClientCertificateDialog extends JDialog { JCheckBox sendEmail; boolean isCanceled = true; - public NewClientCertificateDialog(Frame owner, String displayname, Date defaultExpiration) { + public NewClientCertificateDialog(Frame owner, String displayname, Date defaultExpiration, boolean allowEmail) { super(owner); setTitle(Translation.get("gb.newCertificate")); - JPanel content = new JPanel(new BorderLayout(5, 5)) { + JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) { private static final long serialVersionUID = 1L; @Override @@ -72,7 +72,7 @@ public class NewClientCertificateDialog extends JDialog { hint = new JTextField(20); sendEmail = new JCheckBox(Translation.get("gb.sendEmail")); - JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5)); + JPanel panel = new JPanel(new GridLayout(0, 2, Utils.MARGIN, Utils.MARGIN)); panel.add(new JLabel(Translation.get("gb.expires"))); panel.add(expirationDate); @@ -86,8 +86,10 @@ public class NewClientCertificateDialog extends JDialog { panel.add(new JLabel(Translation.get("gb.passwordHint"))); panel.add(hint); - panel.add(new JLabel("")); - panel.add(sendEmail); + if (allowEmail) { + panel.add(new JLabel("")); + panel.add(sendEmail); + } content.add(panel, BorderLayout.CENTER); @@ -123,7 +125,7 @@ public class NewClientCertificateDialog extends JDialog { private boolean validateInputs() { if (getExpiration().getTime() < System.currentTimeMillis()) { // expires before now - JOptionPane.showMessageDialog(this, Translation.get("gb.invalidExpiraitonDate"), + JOptionPane.showMessageDialog(this, Translation.get("gb.invalidExpirationDate"), Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE); return false; } diff --git a/src/com/gitblit/authority/NewWebCertificateDialog.java b/src/com/gitblit/authority/NewWebCertificateDialog.java new file mode 100644 index 00000000..a6846c57 --- /dev/null +++ b/src/com/gitblit/authority/NewWebCertificateDialog.java @@ -0,0 +1,131 @@ +/* + * 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.BorderLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Date; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.gitblit.client.HeaderPanel; +import com.gitblit.client.Translation; +import com.gitblit.utils.StringUtils; +import com.toedter.calendar.JDateChooser; + +public class NewWebCertificateDialog extends JDialog { + + private static final long serialVersionUID = 1L; + + JDateChooser expirationDate; + JTextField hostname; + boolean isCanceled = true; + + public NewWebCertificateDialog(Frame owner, Date defaultExpiration) { + super(owner); + + setTitle(Translation.get("gb.newWebCertificate")); + + JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) { + private static final long serialVersionUID = 1L; + + @Override + public Insets getInsets() { + + return Utils.INSETS; + } + }; + content.add(new HeaderPanel(Translation.get("gb.newWebCertificate"), "rosette_16x16.png"), BorderLayout.NORTH); + + expirationDate = new JDateChooser(defaultExpiration); + hostname = new JTextField(20); + + JPanel panel = new JPanel(new GridLayout(0, 2, Utils.MARGIN, Utils.MARGIN)); + + panel.add(new JLabel(Translation.get("gb.hostname"))); + panel.add(hostname); + + 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() { + public void actionPerformed(ActionEvent e) { + if (validateInputs()) { + isCanceled = false; + setVisible(false); + } + } + }); + JButton cancel = new JButton(Translation.get("gb.cancel")); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + isCanceled = true; + setVisible(false); + } + }); + + JPanel controls = new JPanel(); + controls.add(ok); + controls.add(cancel); + + content.add(controls, BorderLayout.SOUTH); + + getContentPane().add(content, BorderLayout.CENTER); + pack(); + + setLocationRelativeTo(owner); + } + + private boolean validateInputs() { + if (getExpiration().getTime() < System.currentTimeMillis()) { + // expires before now + JOptionPane.showMessageDialog(this, Translation.get("gb.invalidExpirationDate"), + Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE); + return false; + } + if (StringUtils.isEmpty(getHostname())) { + // must have hostname + JOptionPane.showMessageDialog(this, Translation.get("gb.hostnameRequired"), + Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + public String getHostname() { + return hostname.getText(); + } + + public Date getExpiration() { + return expirationDate.getDate(); + } + + public boolean isCanceled() { + return isCanceled; + } +} diff --git a/src/com/gitblit/authority/UserCertificatePanel.java b/src/com/gitblit/authority/UserCertificatePanel.java index 6b1daee3..79c0d940 100644 --- a/src/com/gitblit/authority/UserCertificatePanel.java +++ b/src/com/gitblit/authority/UserCertificatePanel.java @@ -16,12 +16,9 @@ package com.gitblit.authority; import java.awt.BorderLayout; -import java.awt.Component; import java.awt.Cursor; -import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Frame; -import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -31,12 +28,10 @@ import java.util.Date; import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; -import javax.swing.JTextField; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableRowSorter; @@ -55,15 +50,8 @@ public abstract class UserCertificatePanel extends JPanel { private UserCertificateModel ucm; - private JTextField displayname; - private JTextField username; - private JTextField emailAddress; - private JTextField organizationalUnit; - private JTextField organization; - private JTextField locality; - private JTextField stateProvince; - private JTextField countryCode; - + private UserOidsPanel oidsPanel; + private CertificatesTableModel tableModel; private JButton saveUserButton; @@ -80,29 +68,10 @@ public abstract class UserCertificatePanel extends JPanel { super(new BorderLayout()); this.owner = owner; + oidsPanel = new UserOidsPanel(); - displayname = new JTextField(20); - username = new JTextField(20); - username.setEditable(false); - emailAddress = new JTextField(20); - organizationalUnit = new JTextField(20); - organization = new JTextField(20); - locality = new JTextField(20); - stateProvince = new JTextField(20); - countryCode = new JTextField(20); - - JPanel fields = new JPanel(new GridLayout(0, 1, 5, 5)); - fields.add(newFieldPanel(Translation.get("gb.displayName"), displayname)); - fields.add(newFieldPanel(Translation.get("gb.username") + " (CN)", username)); - fields.add(newFieldPanel(Translation.get("gb.emailAddress") + " (E)", emailAddress)); - fields.add(newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnit)); - fields.add(newFieldPanel(Translation.get("gb.organization") + " (O)", organization)); - fields.add(newFieldPanel(Translation.get("gb.locality") + " (L)", locality)); - fields.add(newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince)); - fields.add(newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode)); - - JPanel fp = new JPanel(new BorderLayout(5, 5)); - fp.add(fields, BorderLayout.NORTH); + JPanel fp = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)); + fp.add(oidsPanel, BorderLayout.NORTH); JPanel fieldsPanel = new JPanel(new BorderLayout()); fieldsPanel.add(new HeaderPanel(Translation.get("gb.properties"), "vcard_16x16.png"), BorderLayout.NORTH); @@ -114,7 +83,7 @@ public abstract class UserCertificatePanel extends JPanel { public void actionPerformed(ActionEvent e) { setEditable(false); String username = ucm.user.username; - updateUser(); + oidsPanel.updateUser(ucm); saveUser(username, ucm); } }); @@ -176,12 +145,12 @@ public abstract class UserCertificatePanel extends JPanel { // save changes String username = ucm.user.username; setEditable(false); - updateUser(); + oidsPanel.updateUser(ucm); saveUser(username, ucm); } NewClientCertificateDialog dialog = new NewClientCertificateDialog(UserCertificatePanel.this.owner, - ucm.user.getDisplayName(), getDefaultExpiration()); + ucm.user.getDisplayName(), getDefaultExpiration(), isAllowEmail()); dialog.setModal(true); dialog.setVisible(true); if (dialog.isCanceled()) { @@ -224,7 +193,7 @@ public abstract class UserCertificatePanel extends JPanel { Object choice = JOptionPane.showInputDialog(UserCertificatePanel.this.owner, Translation.get("gb.revokeCertificateReason"), Translation.get("gb.revokeCertificate"), - JOptionPane.PLAIN_MESSAGE, new ImageIcon(getClass().getResource("/rosette_16x16.png")), choices, Translation.get("gb.unspecified")); + JOptionPane.PLAIN_MESSAGE, new ImageIcon(getClass().getResource("/rosette_32x32.png")), choices, Translation.get("gb.unspecified")); if (choice == null) { return; } @@ -273,26 +242,10 @@ public abstract class UserCertificatePanel extends JPanel { setEditable(false); } - private JPanel newFieldPanel(String label, Component c) { - JLabel jlabel = new JLabel(label); - jlabel.setPreferredSize(new Dimension(175, 20)); - JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - panel.add(jlabel); - panel.add(c); - return panel; - } - public void setUserCertificateModel(UserCertificateModel ucm) { this.ucm = ucm; setEditable(false); - displayname.setText(ucm.user.getDisplayName()); - username.setText(ucm.user.username); - emailAddress.setText(ucm.user.emailAddress); - organizationalUnit.setText(ucm.user.organizationalUnit); - organization.setText(ucm.user.organization); - locality.setText(ucm.user.locality); - stateProvince.setText(ucm.user.stateProvince); - countryCode.setText(ucm.user.countryCode); + oidsPanel.setUserCertificateModel(ucm); tableModel.setUserCertificateModel(ucm); tableModel.fireTableDataChanged(); @@ -300,14 +253,7 @@ public abstract class UserCertificatePanel extends JPanel { } public void setEditable(boolean editable) { - displayname.setEditable(editable); -// username.setEditable(editable); - emailAddress.setEditable(editable); - organizationalUnit.setEditable(editable); - organization.setEditable(editable); - locality.setEditable(editable); - stateProvince.setEditable(editable); - countryCode.setEditable(editable); + oidsPanel.setEditable(editable); editUserButton.setEnabled(!editable && ucm != null); saveUserButton.setEnabled(editable && ucm != null); @@ -316,18 +262,8 @@ public abstract class UserCertificatePanel extends JPanel { revokeCertificateButton.setEnabled(false); } - private void updateUser() { - ucm.user.displayName = displayname.getText(); - ucm.user.username = username.getText(); - ucm.user.emailAddress = emailAddress.getText(); - ucm.user.organizationalUnit = organizationalUnit.getText(); - ucm.user.organization = organization.getText(); - ucm.user.locality = locality.getText(); - ucm.user.stateProvince = stateProvince.getText(); - ucm.user.countryCode = countryCode.getText(); - } - public abstract Date getDefaultExpiration(); + public abstract boolean isAllowEmail(); public abstract void saveUser(String username, UserCertificateModel ucm); public abstract void newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail); diff --git a/src/com/gitblit/authority/UserOidsPanel.java b/src/com/gitblit/authority/UserOidsPanel.java new file mode 100644 index 00000000..8c3adf66 --- /dev/null +++ b/src/com/gitblit/authority/UserOidsPanel.java @@ -0,0 +1,95 @@ +/* + * 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.GridLayout; + +import javax.swing.JPanel; +import javax.swing.JTextField; + +import com.gitblit.client.Translation; + +public class UserOidsPanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private JTextField displayname; + private JTextField username; + private JTextField emailAddress; + private JTextField organizationalUnit; + private JTextField organization; + private JTextField locality; + private JTextField stateProvince; + private JTextField countryCode; + + public UserOidsPanel() { + super(); + + displayname = new JTextField(20); + username = new JTextField(20); + username.setEditable(false); + emailAddress = new JTextField(20); + organizationalUnit = new JTextField(20); + organization = new JTextField(20); + locality = new JTextField(20); + stateProvince = new JTextField(20); + countryCode = new JTextField(20); + + setLayout(new GridLayout(0, 1, Utils.MARGIN, Utils.MARGIN)); + add(Utils.newFieldPanel(Translation.get("gb.displayName"), displayname)); + add(Utils.newFieldPanel(Translation.get("gb.username") + " (CN)", username)); + add(Utils.newFieldPanel(Translation.get("gb.emailAddress") + " (E)", emailAddress)); + add(Utils.newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnit)); + add(Utils.newFieldPanel(Translation.get("gb.organization") + " (O)", organization)); + add(Utils.newFieldPanel(Translation.get("gb.locality") + " (L)", locality)); + add(Utils.newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince)); + add(Utils.newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode)); + } + + public void setUserCertificateModel(UserCertificateModel ucm) { + setEditable(false); + displayname.setText(ucm.user.getDisplayName()); + username.setText(ucm.user.username); + emailAddress.setText(ucm.user.emailAddress); + organizationalUnit.setText(ucm.user.organizationalUnit); + organization.setText(ucm.user.organization); + locality.setText(ucm.user.locality); + stateProvince.setText(ucm.user.stateProvince); + countryCode.setText(ucm.user.countryCode); + } + + public void setEditable(boolean editable) { + displayname.setEditable(editable); +// username.setEditable(editable); + emailAddress.setEditable(editable); + organizationalUnit.setEditable(editable); + organization.setEditable(editable); + locality.setEditable(editable); + stateProvince.setEditable(editable); + countryCode.setEditable(editable); + } + + protected void updateUser(UserCertificateModel ucm) { + ucm.user.displayName = displayname.getText(); + ucm.user.username = username.getText(); + ucm.user.emailAddress = emailAddress.getText(); + ucm.user.organizationalUnit = organizationalUnit.getText(); + ucm.user.organization = organization.getText(); + ucm.user.locality = locality.getText(); + ucm.user.stateProvince = stateProvince.getText(); + ucm.user.countryCode = countryCode.getText(); + } +} diff --git a/src/com/gitblit/authority/Utils.java b/src/com/gitblit/authority/Utils.java index 5c824934..45e028e7 100644 --- a/src/com/gitblit/authority/Utils.java +++ b/src/com/gitblit/authority/Utils.java @@ -3,13 +3,16 @@ package com.gitblit.authority; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.Font; import java.awt.Insets; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Date; +import javax.swing.JLabel; import javax.swing.JOptionPane; +import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; @@ -20,8 +23,11 @@ import javax.swing.table.TableModel; import com.gitblit.client.DateCellRenderer; import com.gitblit.client.Translation; +import com.gitblit.utils.StringUtils; public class Utils { + + public final static int LABEL_WIDTH = 175; public final static int MARGIN = 5; @@ -44,6 +50,22 @@ public class Utils { return table; } + public static JPanel newFieldPanel(String label, Component c) { + return newFieldPanel(label, c, null); + } + + public static JPanel newFieldPanel(String label, Component c, String trailingLabel) { + JLabel jlabel = new JLabel(label); + jlabel.setPreferredSize(new Dimension(Utils.LABEL_WIDTH, 20)); + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + panel.add(jlabel); + panel.add(c); + if (!StringUtils.isEmpty(trailingLabel)) { + panel.add(new JLabel(trailingLabel)); + } + return panel; + } + public static void showException(Component c, Throwable t) { StringWriter writer = new StringWriter(); t.printStackTrace(new PrintWriter(writer)); diff --git a/src/com/gitblit/authority/X509CertificateViewer.java b/src/com/gitblit/authority/X509CertificateViewer.java index d29c6e7d..1b095153 100644 --- a/src/com/gitblit/authority/X509CertificateViewer.java +++ b/src/com/gitblit/authority/X509CertificateViewer.java @@ -47,7 +47,7 @@ public class X509CertificateViewer extends JDialog { setTitle(Translation.get("gb.viewCertificate")); - JPanel content = new JPanel(new BorderLayout(5, 5)) { + JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) { private static final long serialVersionUID = 1L; @Override @@ -63,7 +63,7 @@ public class X509CertificateViewer extends JDialog { int l1 = 15; int l2 = 25; int l3 = 45; - JPanel panel = new JPanel(new GridLayout(0, 1, 0, 10)); + JPanel panel = new JPanel(new GridLayout(0, 1, 0, 2*Utils.MARGIN)); panel.add(newField(Translation.get("gb.version"), "" + cert.getVersion(), 3)); panel.add(newField(Translation.get("gb.subject"), cert.getSubjectDN().getName(), l3)); panel.add(newField(Translation.get("gb.issuer"), cert.getIssuerDN().getName(), l3)); @@ -103,7 +103,7 @@ public class X509CertificateViewer extends JDialog { } private JPanel newField(String label, String value, int cols) { - JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2*Utils.MARGIN, 0)); JLabel lbl = new JLabel(label); lbl.setHorizontalAlignment(SwingConstants.RIGHT); lbl.setPreferredSize(new Dimension(125, 20)); diff --git a/src/com/gitblit/utils/X509Utils.java b/src/com/gitblit/utils/X509Utils.java index 3caff358..1510b2cf 100644 --- a/src/com/gitblit/utils/X509Utils.java +++ b/src/com/gitblit/utils/X509Utils.java @@ -15,7 +15,6 @@ */ package com.gitblit.utils; -import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -108,7 +107,7 @@ public class X509Utils { public static final String CA_CN = "Gitblit Certificate Authority"; - public static final String CA_FN = CA_CN; + public static final String CA_ALIAS = CA_CN; private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME; @@ -151,6 +150,10 @@ public class X509Utils { } } + public interface X509Log { + void log(String message); + } + public static class X509Metadata { // map for distinguished name OIDs @@ -211,6 +214,21 @@ public class X509Utils { clone.userDisplayname = userDisplayname; return clone; } + + public String getOID(String oid, String defaultValue) { + if (oids.containsKey(oid)) { + return oids.get(oid); + } + return defaultValue; + } + + public void setOID(String oid, String value) { + if (StringUtils.isEmpty(value)) { + oids.remove(oid); + } else { + oids.put(oid, value); + } + } } /** @@ -218,9 +236,9 @@ public class X509Utils { * * @param metadata * @param folder - * @param logger + * @param x509log */ - public static void prepareX509Infrastructure(X509Metadata metadata, File folder) { + public static void prepareX509Infrastructure(X509Metadata metadata, File folder, X509Log x509log) { // make the specified folder, if necessary folder.mkdirs(); @@ -228,7 +246,7 @@ public class X509Utils { File caKeyStore = new File(folder, CA_KEY_STORE); if (!caKeyStore.exists()) { logger.info(MessageFormat.format("Generating {0} ({1})", CA_CN, caKeyStore.getAbsolutePath())); - X509Certificate caCert = newCertificateAuthority(metadata, caKeyStore); + X509Certificate caCert = newCertificateAuthority(metadata, caKeyStore, x509log); saveCertificate(caCert, new File(caKeyStore.getParentFile(), "ca.cer")); } @@ -236,7 +254,8 @@ public class X509Utils { File caRevocationList = new File(folder, CA_REVOCATION_LIST); if (!caRevocationList.exists()) { logger.info(MessageFormat.format("Generating {0} CRL ({1})", CA_CN, caRevocationList.getAbsolutePath())); - newCertificateRevocationList(caRevocationList, caKeyStore, metadata.password); + newCertificateRevocationList(caRevocationList, caKeyStore, metadata.password); + x509log.log("new certificate revocation list created"); } // rename the old keystore to the new name @@ -250,17 +269,17 @@ public class X509Utils { File serverKeyStore = new File(folder, SERVER_KEY_STORE); if (!serverKeyStore.exists()) { logger.info(MessageFormat.format("Generating SSL certificate for {0} signed by {1} ({2})", metadata.commonName, CA_CN, serverKeyStore.getAbsolutePath())); - PrivateKey caPrivateKey = getPrivateKey(CA_FN, caKeyStore, metadata.password); - X509Certificate caCert = getCertificate(CA_FN, caKeyStore, metadata.password); - newSSLCertificate(metadata, caPrivateKey, caCert, serverKeyStore); + PrivateKey caPrivateKey = getPrivateKey(CA_ALIAS, caKeyStore, metadata.password); + X509Certificate caCert = getCertificate(CA_ALIAS, caKeyStore, metadata.password); + newSSLCertificate(metadata, caPrivateKey, caCert, serverKeyStore, x509log); } // server certificate trust store holds trusted public certificates File serverTrustStore = new File(folder, X509Utils.SERVER_TRUST_STORE); if (!serverTrustStore.exists()) { - logger.info(MessageFormat.format("Importing {0} into trust store ({1})", CA_FN, serverTrustStore.getAbsolutePath())); - X509Certificate caCert = getCertificate(CA_FN, caKeyStore, metadata.password); - addTrustedCertificate(CA_FN, caCert, serverTrustStore, metadata.password); + logger.info(MessageFormat.format("Importing {0} into trust store ({1})", CA_ALIAS, serverTrustStore.getAbsolutePath())); + X509Certificate caCert = getCertificate(CA_ALIAS, caKeyStore, metadata.password); + addTrustedCertificate(CA_ALIAS, caCert, serverTrustStore, metadata.password); } } @@ -506,8 +525,9 @@ public class X509Utils { * @param caPrivateKey * @param caCert * @param targetStoreFile + * @param x509log */ - public static X509Certificate newSSLCertificate(X509Metadata sslMetadata, PrivateKey caPrivateKey, X509Certificate caCert, File targetStoreFile) { + public static X509Certificate newSSLCertificate(X509Metadata sslMetadata, PrivateKey caPrivateKey, X509Certificate caCert, File targetStoreFile, X509Log x509log) { try { KeyPair pair = newKeyPair(); @@ -541,8 +561,7 @@ public class X509Utils { new Certificate[] { cert, caCert }); saveKeyStore(targetStoreFile, serverStore, sslMetadata.password); - log(targetStoreFile.getParentFile(), MessageFormat.format("New web certificate {0,number,0} [{1}]", cert.getSerialNumber(), webDN.toString())); - + x509log.log(MessageFormat.format("New web certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName())); return cert; } catch (Throwable t) { throw new RuntimeException("Failed to generate SSL certificate!", t); @@ -556,9 +575,10 @@ public class X509Utils { * @param metadata * @param storeFile * @param keystorePassword + * @param x509log * @return */ - public static X509Certificate newCertificateAuthority(X509Metadata metadata, File storeFile) { + public static X509Certificate newCertificateAuthority(X509Metadata metadata, File storeFile, X509Log x509log) { try { KeyPair caPair = newKeyPair(); @@ -597,11 +617,12 @@ public class X509Utils { // Save private key and certificate to new keystore KeyStore store = openKeyStore(storeFile, caMetadata.password); - store.setKeyEntry(CA_FN, caPair.getPrivate(), caMetadata.password.toCharArray(), + store.setKeyEntry(CA_ALIAS, caPair.getPrivate(), caMetadata.password.toCharArray(), new Certificate[] { cert }); saveKeyStore(storeFile, store, caMetadata.password); - log(storeFile.getParentFile(), MessageFormat.format("New CA certificate {0,number,0} [{1}]", cert.getSerialNumber(), issuerDN.toString())); + x509log.log(MessageFormat.format("New CA certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getIssuerDN().getName())); + return cert; } catch (Throwable t) { throw new RuntimeException("Failed to generate Gitblit CA certificate!", t); @@ -621,8 +642,8 @@ public class X509Utils { try { // read the Gitblit CA key and certificate KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword); - PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_FN, caKeystorePassword.toCharArray()); - X509Certificate caCert = (X509Certificate) store.getCertificate(CA_FN); + PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray()); + X509Certificate caCert = (X509Certificate) store.getCertificate(CA_ALIAS); X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName()); X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date()); @@ -642,8 +663,6 @@ public class X509Utils { caRevocationList.delete(); } tmpFile.renameTo(caRevocationList); - - log(caRevocationList.getParentFile(), "new certificate revocation list created"); } finally { if (fos != null) { fos.close(); @@ -685,19 +704,22 @@ public class X509Utils { * @param clientMetadata a container for dynamic parameters needed for generation * @param caKeystoreFile * @param caKeystorePassword + * @param x509log * @return a zip file containing the P12, PEM, and personalized README */ - public static File newClientBundle(X509Metadata clientMetadata, File caKeystoreFile, String caKeystorePassword) { + public static File newClientBundle(X509Metadata clientMetadata, File caKeystoreFile, + String caKeystorePassword, X509Log x509log) { try { // read the Gitblit CA key and certificate KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword); - PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_FN, caKeystorePassword.toCharArray()); - X509Certificate caCert = (X509Certificate) store.getCertificate(CA_FN); + PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray()); + X509Certificate caCert = (X509Certificate) store.getCertificate(CA_ALIAS); // generate the P12 and PEM files File targetFolder = new File(caKeystoreFile.getParentFile(), clientMetadata.commonName); - newClientCertificate(clientMetadata, caPrivateKey, caCert, targetFolder); - + X509Certificate cert = newClientCertificate(clientMetadata, caPrivateKey, caCert, targetFolder); + x509log.log(MessageFormat.format("New client certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName())); + // process template message String readme = processTemplate(new File(caKeystoreFile.getParentFile(), "instructions.tmpl"), clientMetadata); @@ -830,8 +852,6 @@ public class X509Utils { // save certificate after successfully creating the key stores saveCertificate(userCert, certFile); - log(targetFolder.getParentFile(), MessageFormat.format("New client certificate {0,number,0} [{1}]", userCert.getSerialNumber(), userDN.toString())); - return userCert; } catch (Throwable t) { throw new RuntimeException("Failed to generate client certificate!", t); @@ -924,25 +944,6 @@ public class X509Utils { return content; } - private static void log(File folder, String message) { - BufferedWriter writer = null; - try { - writer = new BufferedWriter(new FileWriter(new File(folder, "log.txt"), true)); - writer.write(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1}", new Date(), message)); - writer.newLine(); - writer.flush(); - } catch (Exception e) { - logger.error("Failed to append log entry!", e); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - } - } - } - } - /** * Revoke a certificate. * @@ -951,15 +952,17 @@ public class X509Utils { * @param caRevocationList * @param caKeystoreFile * @param caKeystorePassword + * @param x509log * @return true if the certificate has been revoked */ public static boolean revoke(X509Certificate cert, RevocationReason reason, - File caRevocationList, File caKeystoreFile, String caKeystorePassword) { + File caRevocationList, File caKeystoreFile, String caKeystorePassword, + X509Log x509log) { try { // read the Gitblit CA key and certificate KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword); - PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_FN, caKeystorePassword.toCharArray()); - return revoke(cert, reason, caRevocationList, caPrivateKey); + PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray()); + return revoke(cert, reason, caRevocationList, caPrivateKey, x509log); } catch (Exception e) { logger.error(MessageFormat.format("Failed to revoke certificate {0,number,0} [{1}] in {2}", cert.getSerialNumber(), cert.getSubjectDN().getName(), caRevocationList)); @@ -974,12 +977,12 @@ public class X509Utils { * @param reason * @param caRevocationList * @param caPrivateKey + * @param x509log * @return true if the certificate has been revoked */ public static boolean revoke(X509Certificate cert, RevocationReason reason, - File caRevocationList, PrivateKey caPrivateKey) { + File caRevocationList, PrivateKey caPrivateKey, X509Log x509log) { try { - X500Name subjectDN = new X500Name(PrincipalUtil.getSubjectX509Principal(cert).getName()); X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(cert).getName()); X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date()); if (caRevocationList.exists()) { @@ -1005,8 +1008,6 @@ public class X509Utils { } tmpFile.renameTo(caRevocationList); - log(caRevocationList.getParentFile(), MessageFormat.format("Revoked certificate {0,number,0} reason: {1} [{2}]", - cert.getSerialNumber(), reason.toString(), subjectDN.toString())); } finally { if (fos != null) { fos.close(); @@ -1015,6 +1016,9 @@ public class X509Utils { tmpFile.delete(); } } + + x509log.log(MessageFormat.format("Revoked certificate {0,number,0} reason: {1} [{2}]", + cert.getSerialNumber(), reason.toString(), cert.getSubjectDN().getName())); return true; } catch (Exception e) { logger.error(MessageFormat.format("Failed to revoke certificate {0,number,0} [{1}] in {2}", diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index 6b2102ef..1a1ee175 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -414,4 +414,9 @@ gb.cessationOfOperation = cessation of operation gb.privilegeWithdrawn = privilege withdrawn gb.time.inMinutes = in {0} mins gb.time.inHours = in {0} hours -gb.time.inDays = in {0} days \ No newline at end of file +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.duration = duration \ No newline at end of file diff --git a/tests/com/gitblit/tests/X509UtilsTest.java b/tests/com/gitblit/tests/X509UtilsTest.java index 85afce0c..85d30deb 100644 --- a/tests/com/gitblit/tests/X509UtilsTest.java +++ b/tests/com/gitblit/tests/X509UtilsTest.java @@ -34,6 +34,7 @@ import com.gitblit.models.UserModel; import com.gitblit.utils.HttpUtils; import com.gitblit.utils.X509Utils; import com.gitblit.utils.X509Utils.RevocationReason; +import com.gitblit.utils.X509Utils.X509Log; import com.gitblit.utils.X509Utils.X509Metadata; /** @@ -48,12 +49,18 @@ public class X509UtilsTest extends Assert { // based on the JCE policy files String caPassword = "aBcDeFg"; File folder = new File(System.getProperty("user.dir"), "x509test"); + + X509Log log = new X509Log() { + public void log(String message) { + System.out.println(message); + } + }; @Before public void prepare() throws Exception { cleanUp(); X509Metadata goMetadata = new X509Metadata("localhost", caPassword); - X509Utils.prepareX509Infrastructure(goMetadata, folder); + X509Utils.prepareX509Infrastructure(goMetadata, folder, log); } @After @@ -66,16 +73,16 @@ public class X509UtilsTest extends Assert { @Test public void testNewCA() throws Exception { File storeFile = new File(folder, X509Utils.CA_KEY_STORE); - X509Utils.getPrivateKey(X509Utils.CA_FN, storeFile, caPassword); - X509Certificate cert = X509Utils.getCertificate(X509Utils.CA_FN, storeFile, caPassword); + X509Utils.getPrivateKey(X509Utils.CA_ALIAS, storeFile, caPassword); + X509Certificate cert = X509Utils.getCertificate(X509Utils.CA_ALIAS, storeFile, caPassword); assertEquals("O=Gitblit,OU=Gitblit,CN=Gitblit Certificate Authority", cert.getIssuerDN().getName()); } @Test public void testCertificateUserMapping() throws Exception { File storeFile = new File(folder, X509Utils.CA_KEY_STORE); - PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_FN, storeFile, caPassword); - X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_FN, storeFile, caPassword); + PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_ALIAS, storeFile, caPassword); + X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_ALIAS, storeFile, caPassword); X509Metadata userMetadata = new X509Metadata("james", "james"); userMetadata.serverHostname = "www.myserver.com"; @@ -108,7 +115,7 @@ public class X509UtilsTest extends Assert { userMetadata.userDisplayname = "James Moger"; userMetadata.passwordHint = "your name"; - File zip = X509Utils.newClientBundle(userMetadata, storeFile, caPassword); + File zip = X509Utils.newClientBundle(userMetadata, storeFile, caPassword, log); assertTrue(zip.exists()); List expected = Arrays.asList(userMetadata.commonName + ".pem", userMetadata.commonName + ".p12", "README.TXT"); @@ -124,8 +131,8 @@ public class X509UtilsTest extends Assert { @Test public void testCertificateRevocation() throws Exception { File storeFile = new File(folder, X509Utils.CA_KEY_STORE); - PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_FN, storeFile, caPassword); - X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_FN, storeFile, caPassword); + PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_ALIAS, storeFile, caPassword); + X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_ALIAS, storeFile, caPassword); X509Metadata userMetadata = new X509Metadata("james", "james"); userMetadata.serverHostname = "www.myserver.com"; @@ -140,7 +147,7 @@ public class X509UtilsTest extends Assert { assertFalse(X509Utils.isRevoked(cert1, caRevocationList)); // revoke certificate and then confirm it IS revoked - X509Utils.revoke(cert1, RevocationReason.ACompromise, caRevocationList, storeFile, caPassword); + X509Utils.revoke(cert1, RevocationReason.ACompromise, caRevocationList, storeFile, caPassword, log); assertTrue(X509Utils.isRevoked(cert1, caRevocationList)); // generate a second certificate @@ -151,7 +158,7 @@ public class X509UtilsTest extends Assert { assertFalse(X509Utils.isRevoked(cert2, caRevocationList)); // revoke second certificate and then confirm it IS revoked - X509Utils.revoke(cert2, RevocationReason.ACompromise, caRevocationList, caPrivateKey); + X509Utils.revoke(cert2, RevocationReason.ACompromise, caRevocationList, caPrivateKey, log); assertTrue(X509Utils.isRevoked(cert1, caRevocationList)); assertTrue(X509Utils.isRevoked(cert2, caRevocationList)); @@ -164,7 +171,7 @@ public class X509UtilsTest extends Assert { assertFalse(X509Utils.isRevoked(cert3, caRevocationList)); // revoke third certificate and then confirm it IS revoked - X509Utils.revoke(cert3, RevocationReason.ACompromise, caRevocationList, caPrivateKey); + X509Utils.revoke(cert3, RevocationReason.ACompromise, caRevocationList, caPrivateKey, log); assertTrue(X509Utils.isRevoked(cert1, caRevocationList)); assertTrue(X509Utils.isRevoked(cert2, caRevocationList)); assertTrue(X509Utils.isRevoked(cert3, caRevocationList));