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
private static Connector createSSLConnector(String certAlias, 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
+ GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias,\r
+ keyStore, clientTrustStore, storePassword, caRevocationList);\r
SslConnector connector;\r
if (useNIO) {\r
logger.info("Setting up NIO SslSelectChannelConnector on port " + port);\r
- SslSelectChannelConnector ssl = new SslSelectChannelConnector(sslContext);\r
+ SslSelectChannelConnector ssl = new SslSelectChannelConnector(factory);\r
ssl.setSoLingerTime(-1);\r
if (requireClientCertificates) {\r
- sslContext.setNeedClientAuth(true);\r
+ factory.setNeedClientAuth(true);\r
} else {\r
- sslContext.setWantClientAuth(true);\r
+ factory.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(sslContext);\r
+ SslSocketConnector ssl = new SslSocketConnector(factory);\r
connector = ssl;\r
}\r
- // disable renegotiation unless this is a patched JVM\r
- boolean allowRenegotiation = false;\r
- String v = System.getProperty("java.version");\r
- if (v.startsWith("1.7")) {\r
- allowRenegotiation = true;\r
- } else if (v.startsWith("1.6")) {\r
- // 1.6.0_22 was first release with RFC-5746 implemented fix.\r
- if (v.indexOf('_') > -1) {\r
- String b = v.substring(v.indexOf('_') + 1);\r
- if (Integer.parseInt(b) >= 22) {\r
- allowRenegotiation = true;\r
- }\r
- }\r
- }\r
- if (allowRenegotiation) {\r
- logger.info(" allowing SSL renegotiation on Java " + v);\r
- sslContext.setAllowRenegotiate(allowRenegotiation);\r
- }\r
- sslContext.setKeyStorePath(keyStore.getAbsolutePath());\r
- sslContext.setKeyStorePassword(storePassword);\r
- sslContext.setTrustStore(clientTrustStore.getAbsolutePath());\r
- sslContext.setTrustStorePassword(storePassword);\r
- sslContext.setCrlPath(caRevocationList.getAbsolutePath());\r
- if (!StringUtils.isEmpty(certAlias)) {\r
- logger.info(" certificate alias = " + certAlias);\r
- sslContext.setCertAlias(certAlias);\r
- }\r
connector.setPort(port);\r
connector.setMaxIdleTime(30000);\r
+\r
return connector;\r
}\r
\r
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.File;\r
+import java.security.KeyStore;\r
+import java.security.cert.CRL;\r
+import java.util.Collection;\r
+\r
+import javax.net.ssl.TrustManager;\r
+import javax.net.ssl.X509TrustManager;\r
+\r
+import org.eclipse.jetty.util.ssl.SslContextFactory;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Special SSL context factory that configures Gitblit GO and replaces the\r
+ * primary trustmanager with a GitblitTrustManager.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class GitblitSslContextFactory extends SslContextFactory {\r
+\r
+ private static final Logger logger = LoggerFactory.getLogger(GitblitSslContextFactory.class);\r
+\r
+ private final File caRevocationList;\r
+ \r
+ public GitblitSslContextFactory(String certAlias, File keyStore, File clientTrustStore,\r
+ String storePassword, File caRevocationList) {\r
+ super(keyStore.getAbsolutePath());\r
+ \r
+ this.caRevocationList = caRevocationList;\r
+\r
+ // disable renegotiation unless this is a patched JVM\r
+ boolean allowRenegotiation = false;\r
+ String v = System.getProperty("java.version");\r
+ if (v.startsWith("1.7")) {\r
+ allowRenegotiation = true;\r
+ } else if (v.startsWith("1.6")) {\r
+ // 1.6.0_22 was first release with RFC-5746 implemented fix.\r
+ if (v.indexOf('_') > -1) {\r
+ String b = v.substring(v.indexOf('_') + 1);\r
+ if (Integer.parseInt(b) >= 22) {\r
+ allowRenegotiation = true;\r
+ }\r
+ }\r
+ }\r
+ if (allowRenegotiation) {\r
+ logger.info(" allowing SSL renegotiation on Java " + v);\r
+ setAllowRenegotiate(allowRenegotiation);\r
+ }\r
+ \r
+ \r
+ if (!StringUtils.isEmpty(certAlias)) {\r
+ logger.info(" certificate alias = " + certAlias);\r
+ setCertAlias(certAlias);\r
+ }\r
+ setKeyStorePassword(storePassword);\r
+ setTrustStore(clientTrustStore.getAbsolutePath());\r
+ setTrustStorePassword(storePassword);\r
+ \r
+ logger.info(" keyStorePath = " + keyStore.getAbsolutePath());\r
+ logger.info(" trustStorePath = " + clientTrustStore.getAbsolutePath());\r
+ logger.info(" crlPath = " + caRevocationList.getAbsolutePath());\r
+ }\r
+\r
+ @Override\r
+ protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls)\r
+ throws Exception {\r
+ TrustManager[] managers = super.getTrustManagers(trustStore, crls);\r
+ X509TrustManager delegate = (X509TrustManager) managers[0];\r
+ GitblitTrustManager root = new GitblitTrustManager(delegate, caRevocationList);\r
+\r
+ // replace first manager with the GitblitTrustManager\r
+ managers[0] = root;\r
+ return managers;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.InputStream;\r
+import java.security.cert.CertificateException;\r
+import java.security.cert.CertificateFactory;\r
+import java.security.cert.X509CRL;\r
+import java.security.cert.X509CRLEntry;\r
+import java.security.cert.X509Certificate;\r
+import java.text.MessageFormat;\r
+import java.util.concurrent.atomic.AtomicLong;\r
+\r
+import javax.net.ssl.X509TrustManager;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+/**\r
+ * GitblitTrustManager is a wrapper trust manager that hot-reloads a local file \r
+ * CRL and enforces client certificate revocations. The GitblitTrustManager\r
+ * also implements fuzzy revocation enforcement in case of issuer mismatch BUT\r
+ * serial number match. These rejecions are specially noted in the log.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class GitblitTrustManager implements X509TrustManager {\r
+ \r
+ private static final Logger logger = LoggerFactory.getLogger(GitblitTrustManager.class);\r
+ \r
+ private final X509TrustManager delegate;\r
+ private final File caRevocationList;\r
+ \r
+ private final AtomicLong lastModified = new AtomicLong(0);\r
+ private volatile X509CRL crl;\r
+\r
+ public GitblitTrustManager(X509TrustManager delegate, File crlFile) {\r
+ this.delegate = delegate;\r
+ this.caRevocationList = crlFile;\r
+ }\r
+\r
+ @Override\r
+ public void checkClientTrusted(X509Certificate[] chain, String authType)\r
+ throws CertificateException {\r
+ X509Certificate cert = chain[0];\r
+ if (isRevoked(cert)) {\r
+ String message = MessageFormat.format("Rejecting revoked certificate {0,number,0} for {1}",\r
+ cert.getSerialNumber(), cert.getSubjectDN().getName());\r
+ logger.warn(message);\r
+ throw new CertificateException(message);\r
+ }\r
+ delegate.checkClientTrusted(chain, authType);\r
+ }\r
+\r
+ @Override\r
+ public void checkServerTrusted(X509Certificate[] chain, String authType)\r
+ throws CertificateException {\r
+ delegate.checkServerTrusted(chain, authType);\r
+ }\r
+\r
+ @Override\r
+ public X509Certificate[] getAcceptedIssuers() {\r
+ return delegate.getAcceptedIssuers();\r
+ }\r
+ \r
+ protected boolean isRevoked(X509Certificate cert) {\r
+ if (!caRevocationList.exists()) {\r
+ return false;\r
+ }\r
+ read();\r
+\r
+ if (crl.isRevoked(cert)) {\r
+ // exact cert is revoked\r
+ return true;\r
+ }\r
+ \r
+ X509CRLEntry entry = crl.getRevokedCertificate(cert.getSerialNumber());\r
+ if (entry != null) {\r
+ logger.warn("Certificate issuer does not match CRL issuer, but serial number has been revoked!");\r
+ logger.warn(" cert issuer = " + cert.getIssuerX500Principal());\r
+ logger.warn(" crl issuer = " + crl.getIssuerX500Principal());\r
+ return true;\r
+ }\r
+ \r
+ return false;\r
+ }\r
+ \r
+ protected synchronized void read() {\r
+ if (lastModified.get() == caRevocationList.lastModified()) {\r
+ return;\r
+ }\r
+ logger.info("Reloading CRL from " + caRevocationList.getAbsolutePath());\r
+ InputStream inStream = null;\r
+ try {\r
+ inStream = new FileInputStream(caRevocationList);\r
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");\r
+ X509CRL list = (X509CRL)cf.generateCRL(inStream);\r
+ crl = list;\r
+ lastModified.set(caRevocationList.lastModified());\r
+ } catch (Exception e) {\r
+ } finally {\r
+ if (inStream != null) {\r
+ try {\r
+ inStream.close();\r
+ } catch (Exception e) {\r
+ }\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file