/build-demo.xml\r
/users.conf\r
*.directory\r
-/.gradle
-/projects.conf
-/pom.xml
-/deploy
+/.gradle\r
+/projects.conf\r
+/pom.xml\r
+/deploy\r
+/*.jks\r
+/x509test\r
+/certs
\ No newline at end of file
<include name="users.conf" />\r
</fileset>\r
</copy>\r
+ \r
+ <!-- copy required distribution files to project folder -->\r
+ <mkdir dir="${basedir}/certs" />\r
+ <copy todir="${basedir}/certs" overwrite="false">\r
+ <fileset dir="${basedir}/distrib">\r
+ <include name="authority.conf" />\r
+ </fileset>\r
+ </copy>\r
\r
<!-- copy gitblit.properties to the WEB-INF folder.\r
this file is only used for parsing setting descriptions. -->\r
<include name="**/*" />\r
<exclude name="federation.properties" />\r
<exclude name="openshift.mkd" />\r
+ <exclude name="authority.conf" />\r
</fileset>\r
<fileset dir="${basedir}">\r
<include name="LICENSE" />\r
<include name="NOTICE" />\r
+ <include name="authority*.jar" />\r
+ </fileset>\r
+ </copy>\r
+ \r
+ <!-- Certificate templates -->\r
+ <mkdir dir="${project.deploy.dir}/certs"/>\r
+ <copy todir="${project.deploy.dir}/certs">\r
+ <fileset dir="${basedir}/distrib">\r
+ <include name="authority.conf" />\r
</fileset>\r
</copy>\r
\r
--- /dev/null
+[new]\r
+ duration = 365
+ organizationalUnit = Gitblit
+ organization = Gitblit
+ locality = Gitblit
+ stateProvince = NY
# RESTART REQUIRED\r
server.storePassword = gitblit\r
\r
+# If serving over https (recommended) you might consider requiring clients to\r
+# authenticate with ssl certificates. If enabled, only https clients with the\r
+# a valid client certificate will be able to access Gitblit.\r
+#\r
+# If disabled, client certificate authentication is optional and will be tried\r
+# first before falling-back to form authentication or basic authentication.\r
+#\r
+# Requiring client certificates to access any of Gitblit may be too extreme,\r
+# consider this carefully.\r
+#\r
+# SINCE 1.2.0\r
+# RESTART REQUIRED\r
+server.requireClientCertificates = false\r
+\r
# Port for shutdown monitor to listen on.\r
#\r
# SINCE 0.5.0\r
@REM Set HOSTNAME to the server's hostname\r
@REM --------------------------------------------------------------------------\r
@SET HOSTNAME=localhost\r
-@del keystore\r
+@del serverKeyStore.jks\r
@java -cp gitblit.jar;"%CD%\ext\*" com.gitblit.MakeCertificate --hostname %HOSTNAME% --subject "CN=%HOSTNAME%, OU=Gitblit, O=Gitblit, L=Some Town, ST=Some State, C=US"\r
@REM Set HOSTNAME to the server's hostname\r
@REM --------------------------------------------------------------------------\r
@SET HOSTNAME=localhost\r
-@del keystore\r
-@keytool -keystore keystore -alias %HOSTNAME% -genkey -keyalg RSA -dname "CN=%HOSTNAME%, OU=Gitblit, O=Gitblit, L=Some Town, ST=Some State, C=US"
\ No newline at end of file
+@del serverKeyStore.jks\r
+@keytool -keystore serverKeyStore.jks -alias %HOSTNAME% -genkey -keyalg RSA -dname "CN=%HOSTNAME%, OU=Gitblit, O=Gitblit, L=Some Town, ST=Some State, C=US"
\ No newline at end of file
**New:** *git.requireClientCertificates = false* \r
**New:** *git.enforceCertificateValidity = true* \r
**New:** *git.certificateUsernameOIDs = CN*\r
+- Revised clean install certificate generation to create a Gitblit GO Certificate Authority certificate; an SSL certificate signed by the CA certificate; and to create distinct server key and server trust stores. <u>The store files have been renamed!</u>\r
+- Added support for Gitblit GO to require usage of client certificates to access the entire server. \r
+This is extreme and should be considered carefully since it affects every https access. The default is to **want** client certificates. Setting this value to *true* changes that to **need** client certificates. \r
+ **New:** *server.requireClientCertificates = false*\r
- Added setting to control length of shortened commit ids \r
**New:** *web.shortCommitIdLength=8* \r
- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*.\r
import java.security.ProtectionDomain;\r
import java.text.MessageFormat;\r
import java.util.ArrayList;\r
+import java.util.Date;\r
import java.util.List;\r
import java.util.Scanner;\r
\r
import org.eclipse.jetty.server.ssl.SslConnector;\r
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;\r
import org.eclipse.jetty.server.ssl.SslSocketConnector;\r
+import org.eclipse.jetty.util.ssl.SslContextFactory;\r
import org.eclipse.jetty.util.thread.QueuedThreadPool;\r
import org.eclipse.jetty.webapp.WebAppContext;\r
+import org.eclipse.jgit.storage.file.FileBasedConfig;\r
+import org.eclipse.jgit.util.FS;\r
import org.eclipse.jgit.util.FileUtils;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
import com.beust.jcommander.Parameter;\r
import com.beust.jcommander.ParameterException;\r
import com.beust.jcommander.Parameters;\r
+import com.gitblit.authority.NewCertificateConfig;\r
import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.TimeUtils;\r
+import com.gitblit.utils.X509Utils;\r
+import com.gitblit.utils.X509Utils.X509Metadata;\r
import com.unboundid.ldap.listener.InMemoryDirectoryServer;\r
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;\r
import com.unboundid.ldap.listener.InMemoryListenerConfig;\r
\r
// conditionally configure the https connector\r
if (params.securePort > 0) {\r
- File keystore = new File("keystore");\r
- if (!keystore.exists()) {\r
- logger.info("Generating self-signed SSL certificate for localhost");\r
- MakeCertificate.generateSelfSignedCertificate("localhost", keystore,\r
- params.storePassword);\r
+ File folder = new File(System.getProperty("user.dir"));\r
+ File certificatesConf = new File(folder, X509Utils.CA_CONFIG);\r
+ File serverKeyStore = new File(folder, X509Utils.SERVER_KEY_STORE);\r
+ File serverTrustStore = new File(folder, X509Utils.SERVER_TRUST_STORE);\r
+ File caRevocationList = new File(folder, X509Utils.CA_REVOCATION_LIST);\r
+\r
+ // generate CA & web certificates, create certificate stores\r
+ X509Metadata metadata = new X509Metadata("localhost", params.storePassword);\r
+ // set default certificate values from config file\r
+ if (certificatesConf.exists()) {\r
+ FileBasedConfig config = new FileBasedConfig(certificatesConf, FS.detect());\r
+ try {\r
+ config.load();\r
+ } catch (Exception e) {\r
+ logger.error("Error parsing " + certificatesConf, e);\r
+ }\r
+ NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config);\r
+ certificateConfig.update(metadata);\r
}\r
- if (keystore.exists()) {\r
- Connector secureConnector = createSSLConnector(keystore, params.storePassword,\r
- params.useNIO, params.securePort);\r
+ \r
+ metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);\r
+ X509Utils.prepareX509Infrastructure(metadata, folder);\r
+\r
+ if (serverKeyStore.exists()) { \r
+ Connector secureConnector = createSSLConnector(serverKeyStore, serverTrustStore, params.storePassword,\r
+ caRevocationList, params.useNIO, params.securePort, params.requireClientCertificates);\r
String bindInterface = settings.getString(Keys.server.httpsBindInterface, null);\r
if (!StringUtils.isEmpty(bindInterface)) {\r
logger.warn(MessageFormat.format(\r
* SSL renegotiation will be enabled if the JVM is 1.6.0_22 or later.\r
* oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html\r
* \r
- * @param keystore\r
- * @param password\r
+ * @param keyStore\r
+ * @param clientTrustStore\r
+ * @param storePassword\r
+ * @param caRevocationList\r
* @param useNIO\r
* @param port\r
+ * @param requireClientCertificates\r
* @return an https connector\r
*/\r
- private static Connector createSSLConnector(File keystore, String password, boolean useNIO,\r
- int port) {\r
+ private static Connector createSSLConnector(File keyStore, File clientTrustStore,\r
+ String storePassword, File caRevocationList, boolean useNIO, int port, \r
+ boolean requireClientCertificates) {\r
+ SslContextFactory sslContext = new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH);\r
SslConnector connector;\r
if (useNIO) {\r
logger.info("Setting up NIO SslSelectChannelConnector on port " + port);\r
- SslSelectChannelConnector ssl = new SslSelectChannelConnector();\r
+ SslSelectChannelConnector ssl = new SslSelectChannelConnector(sslContext);\r
ssl.setSoLingerTime(-1);\r
+ if (requireClientCertificates) {\r
+ sslContext.setNeedClientAuth(true);\r
+ } else {\r
+ sslContext.setWantClientAuth(true);\r
+ }\r
ssl.setThreadPool(new QueuedThreadPool(20));\r
connector = ssl;\r
} else {\r
logger.info("Setting up NIO SslSocketConnector on port " + port);\r
- SslSocketConnector ssl = new SslSocketConnector();\r
+ SslSocketConnector ssl = new SslSocketConnector(sslContext);\r
connector = ssl;\r
}\r
// disable renegotiation unless this is a patched JVM\r
}\r
if (allowRenegotiation) {\r
logger.info(" allowing SSL renegotiation on Java " + v);\r
- connector.setAllowRenegotiate(allowRenegotiation);\r
+ sslContext.setAllowRenegotiate(allowRenegotiation);\r
}\r
- connector.setKeystore(keystore.getAbsolutePath());\r
- connector.setPassword(password);\r
+ sslContext.setKeyStorePath(keyStore.getAbsolutePath());\r
+ sslContext.setKeyStorePassword(storePassword);\r
+ sslContext.setTrustStore(clientTrustStore.getAbsolutePath());\r
+ sslContext.setTrustStorePassword(storePassword);\r
+ sslContext.setCrlPath(caRevocationList.getAbsolutePath());\r
connector.setPort(port);\r
connector.setMaxIdleTime(30000);\r
return connector;\r
@Parameter(names = "--shutdownPort", description = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)")\r
public Integer shutdownPort = FILESETTINGS.getInteger(Keys.server.shutdownPort, 8081);\r
\r
+ @Parameter(names = "--requireClientCertificates", description = "Require client X509 certificates for https connections.")\r
+ public Boolean requireClientCertificates = FILESETTINGS.getBoolean(Keys.server.requireClientCertificates, false);\r
+\r
/*\r
* Setting overrides\r
*/\r
\r
import javax.security.auth.x500.X500Principal;\r
\r
-import org.bouncycastle.asn1.x500.X500NameBuilder;\r
-import org.bouncycastle.asn1.x500.style.BCStyle;\r
import org.bouncycastle.cert.X509v3CertificateBuilder;\r
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;\r
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;\r
System.err.println(t.getMessage());\r
jc.usage();\r
}\r
- File keystore = new File("keystore");\r
+ File keystore = new File("serverKeyStore.jks");\r
generateSelfSignedCertificate(params.hostname, keystore, params.storePassword,\r
params.subject);\r
}\r
\r
- public static void generateSelfSignedCertificate(String hostname, File keystore,\r
- String keystorePassword) {\r
- try {\r
- Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());\r
-\r
- KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");\r
- kpGen.initialize(1024, new SecureRandom());\r
- KeyPair pair = kpGen.generateKeyPair();\r
-\r
- // Generate self-signed certificate\r
- X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);\r
- builder.addRDN(BCStyle.OU, Constants.NAME);\r
- builder.addRDN(BCStyle.O, Constants.NAME);\r
- builder.addRDN(BCStyle.CN, hostname);\r
-\r
- Date notBefore = new Date(System.currentTimeMillis() - TimeUtils.ONEDAY);\r
- Date notAfter = new Date(System.currentTimeMillis() + 10 * TimeUtils.ONEYEAR);\r
- BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());\r
-\r
- X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(),\r
- serial, notBefore, notAfter, builder.build(), pair.getPublic());\r
- ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")\r
- .setProvider(BC).build(pair.getPrivate());\r
- X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)\r
- .getCertificate(certGen.build(sigGen));\r
- cert.checkValidity(new Date());\r
- cert.verify(cert.getPublicKey());\r
-\r
- // Save to keystore\r
- KeyStore store = KeyStore.getInstance("JKS");\r
- if (keystore.exists()) {\r
- FileInputStream fis = new FileInputStream(keystore);\r
- store.load(fis, keystorePassword.toCharArray());\r
- fis.close();\r
- } else {\r
- store.load(null);\r
- }\r
- store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(),\r
- new java.security.cert.Certificate[] { cert });\r
- FileOutputStream fos = new FileOutputStream(keystore);\r
- store.store(fos, keystorePassword.toCharArray());\r
- fos.close();\r
- } catch (Throwable t) {\r
- t.printStackTrace();\r
- throw new RuntimeException("Failed to generate self-signed certificate!", t);\r
- }\r
- }\r
-\r
public static void generateSelfSignedCertificate(String hostname, File keystore,\r
String keystorePassword, String info) {\r
try {\r
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());\r
\r
- KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");\r
+ KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC);\r
kpGen.initialize(1024, new SecureRandom());\r
KeyPair pair = kpGen.generateKeyPair();\r
\r