Cuando tenemos un proyecto Java de Spring boot que se ejecuta a través de un servidor tomcat embebido, las configuraciones las podemos agregar al archivo properties o en su defecto, debemos crear una clase Java de configuración para poder sobrescribir la que viene por default.
En este caso para poder agregar los certificados SSL al tomcat embebido creé una clase Java, la cual se debe anotar con la etiqueta @Configuration.
@Configuration
public class MyConfiguration {
//TODO
}
En esta clase usaré 4 propiedades:
- port: Puerto sobre el que se ejecutará el tomcat.
- cerFile: La ruta hacía mi archivo .cer.
- keyFile: La ruta hacía mi archivo .key.
- isProd: True si es ambiente productivo (para usar ssl) , false para local (sin ssl).
Estas propiedades las podemos poner desde el archivo properties, para ello debemos anotarlas con la etiqueta @Value(“${nombre.propiedad}”), en mi caso, en las tres primeras el valor lo deje por defecto en la declaración de las mismas, la única que se obtiene desde el archivo de propiedades es isProd.
final int port = 8005;
final String cerFile = "ssl/archivo.cer";
final String keyFile = "ssl/archivo.key";
@Value("${enviroment.prod}")
private boolean isProd;
Ahora solo debemos crear un bean del tipo TomcatServletWebServerFactory, el cual llamaremos containerFactory.
@Bean
public TomcatServletWebServerFactory containerFactory() {
//TODO
}
Dentro del método que genera nuestro bean, debemos crear una instancia de TomcatServletWebServerFactory y sobrescribir su método customizeConnector, en ese método debemos validar si se esta en productivo, si es así vamos a modificar la configuración del tomcat.
new TomcatServletWebServerFactory() {
protected void customizeConnector(Connector connector) {
super.customizeConnector(connector);
if(isProd) {
//TODO
}
}
}
Después de llamar el super, obtenemos el Http11NioProtocol del connector y procedemos a realizar la modificación para que use SSL.
Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler();
Debemos habilitar el SSL y decirle cuales protocolos se van a habilitar, en el caso de que no queramos solicitar un certificado de cliente, debemos deshabilitarlo mediante el SSLVerifyClient, también debemos deshabilitar el ClientAuth, de lo contrario se le va a pedir a los usuarios que se autentiquen y no podrán acceder a nuestros recursos (en el caso de ser un API Rest).
proto.setSSLEnabled(true);
proto.setSslEnabledProtocols(“TLSv1,TLSv1.1,TLSv1.2");
proto.setSSLVerifyClient(CertificateVerification.OPTIONAL.toString());
proto.setClientAuth(CertificateVerification.NONE.toString());
Por último debemos pasar la ruta a nuestros archivos correspondientes al certificado SSL.
proto.setSSLCertificateFile(cerFile);
proto.setSSLCertificateKeyFile(keyFile);
Ya solo queda decirle que schema usar y establecer la marca de conexión segura que se asignará a las solicitudes en true.
connector.setScheme("https");
connector.setSecure(true);
De esta forma ya podemos ejecutar nuestro proyecto sobre SSL únicamente cambiando una propiedad en nuestro application.properties.
enviroment.prod=true
*Nota: les comparto el código completo de la clase:
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.tomcat.util.net.SSLHostConfig.CertificateVerification;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfiguration {
final int port = 8005;
final String cerFile = "ssl/archivo.cer";
final String keyFile = "ssl/archivo.key";
@Value("${enviroment.prod}")
private boolean isProd;
@Bean
public TomcatServletWebServerFactory containerFactory() {
return new TomcatServletWebServerFactory() {
protected void customizeConnector(Connector connector) {
super.customizeConnector(connector);
if(isProd) {
int maxSize = 50000000;
connector.setMaxPostSize(maxSize);
connector.setMaxSavePostSize(maxSize);
Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler();
proto.setSSLEnabled(true);
proto.setAcceptCount(100);
proto.setSslEnabledProtocols("TLSv1,TLSv1.1,TLSv1.2");
proto.setMaxThreads(25);
proto.setSSLVerifyClient(CertificateVerification.OPTIONAL.toString());
proto.setClientAuth(CertificateVerification.NONE.toString());
proto.setSSLCertificateFile(cerFile);
proto.setSSLCertificateKeyFile(keyFile);
connector.setScheme("https");
connector.setSecure(true);
}
}
};
}
}