]> source.dussan.org Git - gitblit.git/commitdiff
Revised Gitblit GO certificate generation to use new X509 utility functions
authorJames Moger <james.moger@gitblit.com>
Fri, 23 Nov 2012 15:38:38 +0000 (10:38 -0500)
committerJames Moger <james.moger@gitblit.com>
Fri, 23 Nov 2012 15:38:38 +0000 (10:38 -0500)
.gitignore
build.xml
distrib/authority.conf [new file with mode: 0644]
distrib/gitblit.properties
distrib/makekeystore.cmd
distrib/makekeystore_jdk.cmd
docs/04_releases.mkd
src/com/gitblit/GitBlitServer.java
src/com/gitblit/MakeCertificate.java

index ffaba31032d9773bb5028a6a0410d7113f7bdfdf..e335123e7e569867a572592e14e52df85a442792 100644 (file)
 /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
index 33b47d18419c29016fad7066160445ffb0f52e60..4e735195dd042ea74940b84305a8851780bfa352 100644 (file)
--- a/build.xml
+++ b/build.xml
                                <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
diff --git a/distrib/authority.conf b/distrib/authority.conf
new file mode 100644 (file)
index 0000000..3e62e3c
--- /dev/null
@@ -0,0 +1,6 @@
+[new]\r
+       duration = 365
+       organizationalUnit = Gitblit
+       organization = Gitblit
+       locality = Gitblit
+       stateProvince = NY
index 1562c51397b6109e78827a82919f23acd7e38e4d..9daabde400a2aa238a9d4c914e24be9633001a8b 100644 (file)
@@ -1137,6 +1137,20 @@ server.ajpBindInterface = localhost
 # 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
index b1413801b349d3bec84611a802048ffeac1c8c5c..8a72e03bcc8179258e4f66991e1d70ca96bb1575 100644 (file)
@@ -2,5 +2,5 @@
 @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
index d3c9e949266d624e37c3848a4146ab35c17ad711..121c6718dda47302281d581532a245a7d1036ad7 100644 (file)
@@ -2,5 +2,5 @@
 @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
index 9165793f53fdeaa95bc9c5264ceff9d13dc99ce7..a3ea493d1d88f9d5c7a12ce7420bc0bbc39f9586 100644 (file)
@@ -52,6 +52,10 @@ You can require all git servlet access be authenticated by a client certificate.
     **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
index f0dce774b242158e6c57223f0c26c58648686c88..52afb54b07be813bc3d051281257def2e6709d50 100644 (file)
@@ -29,6 +29,7 @@ import java.net.UnknownHostException;
 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
@@ -41,8 +42,11 @@ import org.eclipse.jetty.server.session.HashSessionManager;
 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
@@ -51,7 +55,11 @@ import com.beust.jcommander.JCommander;
 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
@@ -186,15 +194,32 @@ public class GitBlitServer {
 \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
@@ -364,24 +389,34 @@ public class GitBlitServer {
         * 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
@@ -400,10 +435,13 @@ public class GitBlitServer {
                }\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
@@ -540,6 +578,9 @@ public class GitBlitServer {
                @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
index f2fc73004baa963681209d0c7938b3080aeea93d..e3c39ff731fb550562f854fff1acd696d3c80764 100644 (file)
@@ -29,8 +29,6 @@ import java.util.Date;
 \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
@@ -62,65 +60,17 @@ public class MakeCertificate {
                        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