125 lines
4.9 KiB
Java
125 lines
4.9 KiB
Java
package dev.coph.nextusweb.server.tls;
|
|
|
|
import io.netty.buffer.ByteBufAllocator;
|
|
import io.netty.handler.ssl.SslContext;
|
|
import io.netty.handler.ssl.SslContextBuilder;
|
|
import io.netty.handler.ssl.SslHandler;
|
|
|
|
import javax.net.ssl.SSLException;
|
|
import java.io.File;
|
|
import java.io.InputStream;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Configuration that enables TLS (HTTPS / WSS) on the server. Holds a ready-built Netty
|
|
* {@link SslContext} and produces the per-connection {@link SslHandler} the pipeline installs as
|
|
* its first handler.
|
|
*
|
|
* <p>Enabling TLS is meant to be a one-liner — point the factory at a PEM certificate chain and
|
|
* private key and pass the result to {@code HttpServer.withTls(...)}:</p>
|
|
* <pre>{@code
|
|
* HttpServer.builder(443, router)
|
|
* .withTls(TlsConfig.fromPem(new File("fullchain.pem"), new File("privkey.pem")))
|
|
* .start();
|
|
* }</pre>
|
|
*
|
|
* <p>For full control (custom cipher suites, client-auth / mutual TLS, a non-default provider)
|
|
* build a Netty {@link SslContext} yourself and wrap it with {@link #fromSslContext(SslContext)}.
|
|
* The {@code SslContext} is built once and reused for every connection, so it is cheap per
|
|
* request.</p>
|
|
*/
|
|
public final class TlsConfig {
|
|
|
|
/**
|
|
* The pre-built, shareable server SSL context.
|
|
*/
|
|
private final SslContext sslContext;
|
|
|
|
private TlsConfig(SslContext sslContext) {
|
|
this.sslContext = Objects.requireNonNull(sslContext, "sslContext");
|
|
}
|
|
|
|
/**
|
|
* Builds a TLS configuration from a PEM-encoded certificate chain and an unencrypted
|
|
* PKCS#8 private key.
|
|
*
|
|
* @param certificateChain the PEM file containing the certificate (chain)
|
|
* @param privateKey the PEM file containing the PKCS#8 private key
|
|
* @return a TLS configuration
|
|
* @throws IllegalStateException if the certificate/key cannot be loaded
|
|
*/
|
|
public static TlsConfig fromPem(File certificateChain, File privateKey) {
|
|
return fromPem(certificateChain, privateKey, null);
|
|
}
|
|
|
|
/**
|
|
* Builds a TLS configuration from a PEM-encoded certificate chain and a (optionally
|
|
* password-protected) PKCS#8 private key.
|
|
*
|
|
* @param certificateChain the PEM file containing the certificate (chain)
|
|
* @param privateKey the PEM file containing the PKCS#8 private key
|
|
* @param keyPassword the password protecting {@code privateKey}, or {@code null} if none
|
|
* @return a TLS configuration
|
|
* @throws IllegalStateException if the certificate/key cannot be loaded
|
|
*/
|
|
public static TlsConfig fromPem(File certificateChain, File privateKey, String keyPassword) {
|
|
Objects.requireNonNull(certificateChain, "certificateChain");
|
|
Objects.requireNonNull(privateKey, "privateKey");
|
|
try {
|
|
return new TlsConfig(SslContextBuilder.forServer(certificateChain, privateKey, keyPassword).build());
|
|
} catch (SSLException | RuntimeException e) {
|
|
throw new IllegalStateException("Failed to initialise TLS from PEM files", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds a TLS configuration from PEM-encoded streams, for certificates/keys loaded from the
|
|
* classpath or another non-file source. The caller retains ownership of the streams.
|
|
*
|
|
* @param certificateChain a stream of the PEM certificate (chain)
|
|
* @param privateKey a stream of the PEM PKCS#8 private key
|
|
* @param keyPassword the password protecting {@code privateKey}, or {@code null} if none
|
|
* @return a TLS configuration
|
|
* @throws IllegalStateException if the certificate/key cannot be loaded
|
|
*/
|
|
public static TlsConfig fromPem(InputStream certificateChain, InputStream privateKey, String keyPassword) {
|
|
Objects.requireNonNull(certificateChain, "certificateChain");
|
|
Objects.requireNonNull(privateKey, "privateKey");
|
|
try {
|
|
return new TlsConfig(SslContextBuilder.forServer(certificateChain, privateKey, keyPassword).build());
|
|
} catch (SSLException | RuntimeException e) {
|
|
throw new IllegalStateException("Failed to initialise TLS from PEM streams", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wraps a fully configured Netty {@link SslContext}, for advanced setups such as custom cipher
|
|
* suites or mutual TLS.
|
|
*
|
|
* @param sslContext a server-mode SSL context
|
|
* @return a TLS configuration backed by the given context
|
|
*/
|
|
public static TlsConfig fromSslContext(SslContext sslContext) {
|
|
return new TlsConfig(sslContext);
|
|
}
|
|
|
|
/**
|
|
* Creates a new per-connection {@link SslHandler} from the shared context.
|
|
*
|
|
* @param alloc the channel's buffer allocator
|
|
* @return a fresh TLS handler for one connection
|
|
*/
|
|
public SslHandler newHandler(ByteBufAllocator alloc) {
|
|
return sslContext.newHandler(alloc);
|
|
}
|
|
|
|
/**
|
|
* Returns the underlying Netty SSL context.
|
|
*
|
|
* @return the shared server SSL context
|
|
*/
|
|
public SslContext sslContext() {
|
|
return sslContext;
|
|
}
|
|
}
|