Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 65 additions & 22 deletions core/src/main/cfml/context/admin/services.certificates.cfm
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
<cflocation url="#request.self#" addtoken="no">
</cfif>



<!---
<!---
Defaults --->
<cfparam name="url.action2" default="list">
<cfparam name="form.mainAction" default="none">
Expand All @@ -24,34 +22,33 @@ Defaults --->
<cfset _port=session.certPort>

<cfscript>
LuceeTrustStore = false;
if ((server.system.properties["lucee.use.lucee.SSL.TrustStore"]?: false)
|| (server.system.environment["lucee_use_lucee_SSL_TrustStore"]?: false)){
LuceeTrustStore = true;
};

customCaCertsEnabled = !(server.system.properties["lucee.ssl.customcacerts.enabled"]?: "true").equalsIgnoreCase("false");
</cfscript>

<cfif !LuceeTrustStore>
<cfif !customCaCertsEnabled>
<p>
<b>As Lucee is currently using the JVM TrustStore/cacerts file, this functionality isn't available.</b>
<b>Custom CA certificates are disabled.</b>
<br><br>
Set the following System or Environment variables to enable: <code>lucee.use.lucee.SSL.TrustStore = true;</code>
Set the following System or Environment variable to enable: <code>lucee.ssl.customcacerts.enabled=true</code>
</p>
</cfif>

<cftry>
<cfswitch expression="#form.mainAction#">
<!--- UPDATE --->

<!--- INSTALL --->
<cfcase value="#stText.services.certificate.install#">
<cfadmin
<cfadmin
type="#request.adminType#"
password="#session["password"&request.adminType]#"
action="updatesslcertificate" host="#form.host#" port="#form.port#">


</cfcase>
</cfcase>
<!--- REMOVE --->
<cfcase value="Remove">
<cfadmin
type="#request.adminType#"
password="#session["password"&request.adminType]#"
action="removesslcertificate" alias="#form.alias#">
</cfcase>
</cfswitch>
<cfcatch>
<cfset error.message=cfcatch.message>
Expand All @@ -61,13 +58,13 @@ Defaults --->
</cftry>


<!---
<!---
Redirtect to entry --->
<cfif cgi.request_method EQ "POST" and error.message EQ "">
<cflocation url="#request.self#?action=#url.action#" addtoken="no">
</cfif>

<!---
<!---
Error Output --->
<cfset printError(error)>
<cfoutput>
Expand Down Expand Up @@ -104,9 +101,55 @@ Error Output --->
</table>
</cfformClassic>

<!--- Installed certificates in custom-cacerts --->
<cfif customCaCertsEnabled>
<cftry>
<cfadmin
type="#request.adminType#"
password="#session["password"&request.adminType]#"
action="getallsslcertificate" returnvariable="installedCerts">

<h2>Installed Certificates</h2>
<cfif installedCerts.recordcount>
<table class="maintbl">
<thead>
<tr>
<th>#stText.services.certificate.subject#</th>
<th>#stText.services.certificate.issuer#</th>
<th>Alias</th>
<th></th>
</tr>
</thead>
<tbody>
<cfloop query="installedCerts">
<tr>
<td>#installedCerts.subject#</td>
<td>#installedCerts.issuer#</td>
<td>#installedCerts.alias#</td>
<td>
<form action="#request.self#?action=#url.action#" method="post" style="display:inline">
<input type="hidden" name="alias" value="#installedCerts.alias#">
<input type="hidden" name="mainAction" value="Remove">
<input class="button small" type="submit" value="Remove" onclick="return confirm('Remove certificate #JSStringFormat(installedCerts.alias)#?')">
</form>
</td>
</tr>
</cfloop>
</tbody>
</table>
<cfelse>
<p>No certificates installed in custom-cacerts.</p>
</cfif>
<cfcatch>
<div class="error">#cfcatch.message# #cfcatch.detail#</div>
</cfcatch>
</cftry>
</cfif>

<!--- Preview certs from remote host --->
<cfif len(_host) and len(_port)>
<cftry>
<cfadmin
<cfadmin
type="#request.adminType#"
password="#session["password"&request.adminType]#"
action="getsslcertificate" host="#_host#" port="#_port#" returnvariable="qry">
Expand Down Expand Up @@ -143,4 +186,4 @@ Error Output --->
</cfcatch>
</cftry>
</cfif>
</cfoutput>
</cfoutput>
6 changes: 3 additions & 3 deletions core/src/main/java/lucee/commons/net/http/HTTPDownloader.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public static InputStream get( URL url, String username, String password, long c

try {
// Get configured HttpClientBuilder (with connection pooling, true = use pooling)
HttpClientBuilder builder = HTTPEngine4Impl.getHttpClientBuilder( true, null, null, "true" );
HttpClientBuilder builder = HTTPEngine4Impl.getHttpClientBuilder( true, null, null, null, null, true, "true" );

// Create HTTP GET request
HttpGet request = new HttpGet( url.toString() );
Expand Down Expand Up @@ -240,7 +240,7 @@ public static HTTPResponse head( URL url, long connectTimeout, long readTimeout,

try {
// Get configured HttpClientBuilder (with connection pooling, true = use pooling)
HttpClientBuilder builder = HTTPEngine4Impl.getHttpClientBuilder( true, null, null, "true" );
HttpClientBuilder builder = HTTPEngine4Impl.getHttpClientBuilder( true, null, null, null, null, true, "true" );

// Create HTTP HEAD request
HttpHead request = new HttpHead( url.toString() );
Expand Down Expand Up @@ -283,7 +283,7 @@ public static boolean exists( URL url ) {
public static boolean exists( URL url, long connectTimeout, long readTimeout ) {
try {
// Get configured HttpClientBuilder (with connection pooling, true = use pooling)
HttpClientBuilder builder = HTTPEngine4Impl.getHttpClientBuilder( true, null, null, "true" );
HttpClientBuilder builder = HTTPEngine4Impl.getHttpClientBuilder( true, null, null, null, null, true, "true" );

// Create HTTP HEAD request
HttpHead request = new HttpHead( url.toString() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,13 @@
**/
package lucee.commons.net.http.httpclient;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
Expand All @@ -41,7 +35,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import org.apache.http.Header;
Expand Down Expand Up @@ -73,6 +67,7 @@
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
Expand Down Expand Up @@ -109,6 +104,7 @@
import lucee.runtime.PageContextImpl;
import lucee.runtime.engine.ThreadLocalPageContext;
import lucee.runtime.net.http.ReqRspUtil;
import lucee.runtime.net.http.SSLUtil;
import lucee.runtime.net.http.sni.DefaultHostnameVerifierImpl;
import lucee.runtime.net.http.sni.DefaultHttpClientConnectionOperatorImpl;
import lucee.runtime.net.http.sni.SSLConnectionSocketFactoryImpl;
Expand Down Expand Up @@ -271,9 +267,9 @@ private static Header toHeader(lucee.commons.net.http.Header header) {
return new HeaderImpl(header.getName(), header.getValue());
}

public static HttpClientBuilder getHttpClientBuilder(boolean pooling, String clientCert, String clientCertPassword, String redirect) throws GeneralSecurityException, IOException {
String key = clientCert + ":" + clientCertPassword;
Registry<ConnectionSocketFactory> reg = StringUtil.isEmpty(clientCert, true) ? createRegistry() : createRegistry(clientCert, clientCertPassword);
public static HttpClientBuilder getHttpClientBuilder(boolean pooling, String clientCert, String clientCertPassword, String trustStore, String trustStorePassword, boolean sslVerify, String redirect) throws GeneralSecurityException {
String key = clientCert + ":" + clientCertPassword + ":" + trustStore + ":" + trustStorePassword + ":" + sslVerify;
Registry<ConnectionSocketFactory> reg = createRegistry( clientCert, clientCertPassword, trustStore, trustStorePassword, sslVerify );

if (!pooling) {
HttpClientBuilder builder = HttpClients.custom();
Expand Down Expand Up @@ -328,31 +324,43 @@ public static void setTimeout(HttpClientBuilder builder, TimeSpan timeout) {
builder.setDefaultRequestConfig(rcBuilder.build());
}

private static Registry<ConnectionSocketFactory> createRegistry() throws GeneralSecurityException {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, new java.security.SecureRandom());
SSLConnectionSocketFactory defaultsslsf = new SSLConnectionSocketFactoryImpl(sslcontext, new DefaultHostnameVerifierImpl());
/* Register connection handlers */
return RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", defaultsslsf).build();
private static Registry<ConnectionSocketFactory> createRegistry( String clientCert, String clientCertPassword, String trustStore, String trustStorePassword, boolean sslVerify ) throws GeneralSecurityException {
SSLContext sslContext;
HostnameVerifier hostnameVerifier;

}
try {
Path clientCertPath = StringUtil.isEmpty( clientCert, true ) ? null : Paths.get( clientCert );
char[] clientPassword = clientCertPassword != null ? clientCertPassword.toCharArray() : null;

if ( !sslVerify ) {
// Disable all SSL verification (like curl -k)
sslContext = SSLUtil.createUnsafeSSLContext( clientCertPath, clientPassword );
hostnameVerifier = NoopHostnameVerifier.INSTANCE;
}
else if ( !StringUtil.isEmpty( trustStore, true ) ) {
// Use custom trust store
Path trustStorePath = Paths.get( trustStore );
char[] trustPassword = trustStorePassword != null ? trustStorePassword.toCharArray() : "changeit".toCharArray();
List<SSLUtil.TrustStoreConfig> additionalTrustStores = new ArrayList<>();
additionalTrustStores.add( new SSLUtil.TrustStoreConfig( trustStorePath, trustPassword ) );
sslContext = SSLUtil.createSSLContext( clientCertPath, clientPassword, additionalTrustStores );
hostnameVerifier = new DefaultHostnameVerifierImpl();
}
else {
// Standard mode with JVM + custom-cacerts
sslContext = SSLUtil.createSSLContext( clientCertPath, clientPassword );
hostnameVerifier = new DefaultHostnameVerifierImpl();
}
}
catch ( IOException e ) {
throw new GeneralSecurityException( "Failed to create SSL context", e );
}

private static Registry<ConnectionSocketFactory> createRegistry(String clientCert, String clientCertPassword)
throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException {
// Currently, clientCert force usePool to being ignored
if (clientCertPassword == null) clientCertPassword = "";
// Load the client cert
File ksFile = new File(clientCert);
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(ksFile), clientCertPassword.toCharArray());
// Prepare the keys
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, clientCertPassword.toCharArray());
SSLContext sslcontext = SSLContext.getInstance("TLS");
// Configure the socket factory
sslcontext.init(kmf.getKeyManagers(), null, new java.security.SecureRandom());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactoryImpl(sslcontext, new DefaultHostnameVerifierImpl());
return RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactoryImpl( sslContext, hostnameVerifier );
return RegistryBuilder.<ConnectionSocketFactory>create()
.register( "http", PlainConnectionSocketFactory.getSocketFactory() )
.register( "https", sslsf )
.build();
}

public static void releaseConnectionManager() {
Expand Down Expand Up @@ -392,7 +400,7 @@ private static HTTPResponse invoke(URL url, HttpUriRequest request, String usern
CloseableHttpClient client;
proxy = ProxyDataImpl.validate(proxy, url.getHost());

HttpClientBuilder builder = getHttpClientBuilder(pooling, null, null, String.valueOf(redirect));
HttpClientBuilder builder = getHttpClientBuilder(pooling, null, null, null, null, true, String.valueOf(redirect));

HttpHost hh = new HttpHost(url.getHost(), url.getPort());
setHeader(request, headers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
Expand Down Expand Up @@ -158,6 +159,7 @@
import lucee.runtime.monitor.RequestMonitorProImpl;
import lucee.runtime.monitor.RequestMonitorWrap;
import lucee.runtime.net.http.ReqRspUtil;
import lucee.runtime.net.http.SSLUtil;
import lucee.runtime.net.mail.Server;
import lucee.runtime.net.mail.ServerImpl;
import lucee.runtime.net.proxy.ProxyData;
Expand Down Expand Up @@ -328,6 +330,7 @@ public static ConfigServerImpl newInstanceServer(CFMLEngineImpl engine, Map<Stri
config.setRoot(root);
// admin mode
load(config, root, false, doNew, essentialOnly);
SSLUtil.init( Paths.get( config.getConfigDir().getAbsolutePath(), "security" ) );

if (!essentialOnly) {
createContextFiles(configDir, config, doNew);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,46 @@
**/
package lucee.runtime.functions.other;

import lucee.commons.io.SystemUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.net.http.httpclient.HTTPEngine4Impl;
import lucee.runtime.PageContext;
import lucee.runtime.exp.ApplicationException;
import lucee.runtime.exp.PageException;
import lucee.runtime.ext.function.Function;
import lucee.commons.io.res.Resource;
import lucee.runtime.net.http.CertificateInstaller;
import lucee.runtime.op.Caster;

public final class SSLCertificateInstall implements Function {

private static final long serialVersionUID = -831759073098524176L;

public static String call(PageContext pc, String host) throws PageException {
return call(pc, host, 443);
public static String call( PageContext pc, String host ) throws PageException {
return call( pc, host, 443 );
}

public static String call(PageContext pc, String host, Number port) throws PageException {
return call(pc, host, port, null, null);
public static String call( PageContext pc, String host, Number port ) throws PageException {
return call( pc, host, port, null, null );
}

public static String call(PageContext pc, String host, Number port, Object cacerts) throws PageException {
return call(pc, host, port, cacerts, null);
public static String call( PageContext pc, String host, Number port, Object cacerts ) throws PageException {
return call( pc, host, port, cacerts, null );
}

public static String call(PageContext pc, String host, Number port, Object cacerts, String password) throws PageException {
CertificateInstaller installer;
Resource _cacerts;
public static String call( PageContext pc, String host, Number port, Object cacerts, String password ) throws PageException {
try {
if (cacerts == null) {
if (!SystemUtil.getSystemPropOrEnvVar("lucee.use.lucee.SSL.TrustStore", "").equalsIgnoreCase("true"))
throw new ApplicationException("Using JVM cacerts, set lucee.use.lucee.SSL.TrustStore=true to enable"); // LDEV-917
_cacerts = pc.getConfig().getSecurityDirectory();
} else {
_cacerts = Caster.toResource(pc, cacerts, true);
if ( cacerts == null ) {
CertificateInstaller.installToCustomCaCerts( host, Caster.toIntValue( port ) );
}
if (password == null) installer = new CertificateInstaller(_cacerts, host, Caster.toIntValue(port)); // use default password changeit
else installer = new CertificateInstaller(_cacerts, host, Caster.toIntValue(port), password.toCharArray());
installer.installAll(true);
} catch (Exception e){
throw Caster.toPageException(e);
else {
Resource keystore = Caster.toResource( pc, cacerts, true );
CertificateInstaller installer = password == null
? new CertificateInstaller( keystore, host, Caster.toIntValue( port ) )
: new CertificateInstaller( keystore, host, Caster.toIntValue( port ), password.toCharArray() );
installer.installAll( true );
}
HTTPEngine4Impl.releaseConnectionManager();
}
catch ( Exception e ) {
throw Caster.toPageException( e );
}
return "";
}
Expand Down
Loading
Loading