/* * 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; } }