Arndt Schönbergs Weblog

Dienstag Juni 23, 2020

Wildfly – JAX RS und selbst signierte Zertifikate

In den meisten Entwicklungsumgebungen kommen selbst signierte Zertifikate zum Einsatz. Will man mit den Jackson, die Wildfly bereits bereitstellt auf solche Server zugreifen, ist einiges zu beachten. Zu Beginn sollten die Jackson Bibliotheken aus dem Wildfly in den Build eingebunden werden (grundsätzlich ist es eine gute Idee, die Bibliotheken von Wildfly auch in den Projekten zu verwenden und nicht immer neue Varianten zu deployen).

Der erste Ansatz für die Umsetztung wäre es dem ClientBuilder einen entsprechenden SSLContext und einen Hostname Verifier zu übergeben, um alle Zertifikate zu akzeptieren.
try {
    SSLContext sslContext = SSLContext.getInstance(AbstractNetHelper.TLS_PROTOCOL_NAME);
    TrustManager[] trustAllCertsManager = {new TrustMangerTrustAllCerts()};
    sslContext.init(null, trustAllCertsManager, new SecureRandom());
    HostnameVerifier hostnameVerifier = null;
    if (ignoreServerNameInCertificate) {
        hostnameVerifier = new HostnameVerifierNoCheck();
    } else {
        hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
    }
    return ClientBuilder.newBuilder().hostnameVerifier(hostnameVerifier).sslContext(sslContext).newClient();
} catch (NoSuchAlgorithmException | KeyManagementException ex) {
    throw new RestException(ex);
}
Diese Implementierung ist syntaktisch korrekt führt aber nicht dazu, dass keine Prüfung der Zertifikate stattfindet. In der Dokumentation RestEasy Doku. findet sich folgendes:

Network communication between the client and server is handled in RESTEasy, by default, by HttpClient (4.x) from the Apache HttpComponents project.

...

RESTEasy and HttpClient make reasonable default decisions so that it is possible to use the client framework without ever referencing HttpClient, but for some applications it may be necessary to drill down into the HttpClient details. ApacheHttpClient4Engine can be supplied with an instance of org.apache.http.client.HttpClient and an instance of org.apache.http.protocol.HttpContext, which can carry additional configuration details into the HttpClient layer.

...

Es muss also ein angepasster Apache HttpClient verwendet werden. Der HttpClient findet sich in dem Modul
org.apache.httpcomponents
Dieses Modul des WIldfly muss zur Nutzung allerdings über einen deployment-descriptor freigegeben werden. Also muss der folgende Descriptor in das Projekt aufgenommen werden (z.B. im META-INF des EAR).
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0" >
    <deployment>
        <dependencies>
            <module name="org.apache.httpcomponents" export="true" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>
Im Folgenden wird ein TrustMangerTrustAllCerts angelegt (hier gibt es diverse Beispiele im Netz) und ein HostnameVerifierNoCheck (ebenfalls Beispiele im Netz). Dann werden diese dem HttpClient zugewiesen und letztendlich ein Client abgeleitet.
public Client createHttpsClientWithTimeout() {
    TrustManager[] trustAllCertsManager = {new TrustMangerTrustAllCerts()};
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustAllCertsManager, new SecureRandom());
    HostnameVerifier hostnameVerifier = new HostnameVerifierNoCheck();

    HttpClient httpClient = HttpClientBuilder.create()
        .setSSLContext(sslContext)
        .setSSLHostnameVerifier(new HostnameVerifierNoCheck())
        .build();
    ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
    return new ResteasyClientBuilder().httpEngine(engine).hostnameVerifier(hostnameVerifier).build();
}
Der so erzeugte Client ignoriert die Prüfung der SSL/TLS Zertifikate. Aber natürlich sollte im Betrieb außerhalb des internen Netzes und in Produktion sowieso immer ein "offiziell" signiertes Zertifikat verwendet werden. Siehe z.B. Zertifikate mit Let's encrypt.

Dienstag Juni 02, 2020

JSF Kontextparameter Project Stage - Debugging

Der Parameter „Project Stage“ schaltet über die Werte „Development“ und „Production“ einige interne Optimierungen wie beispielsweise die Prüfung, ob neue xhtml Seiten compiliert werden müssen, ein oder aus (weitere Werte sind „SystemTest“, „UnitTest“).

<context-param>
    <param-name>javax.faces.validator.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
</context-param>
Auch eigene Auswertungen können hierüber gesteuert werden. Innerhalb von Java kann mit
Application.getProjectStage()
Auf den Wert zugegriffen werden. In xhtml Seiten kann der Wert z.B. zum rendern der Debug Informationen genutzt werden.
<ui:debug hotkey="L" rendered="#{facesContext.application.projectStage == 'Development'}" />
Nun möchte man allerdings im Allgemeinen keinen statischen Wert in der web.xml haben, sondern in Abhängigkeit von dem Build zwischen „Development“ und „Production“ umschalten. Hierfür definiert man Profile in Maven (integrations- und Release-Profile)
        <profile>
            <id>build-int</id>
            <properties>
                <jsfProjectStage>Development</jsfProjectStage>
            </properties>
        </profile>
	 
        <profile>
            <id>build-rel</id>
            <properties>
                <jsfProjectStage>Production</jsfProjectStage>
            </properties>
        </profile>
Diese definieren eine Variable mit der Stage Bezeichnung, die in der web.xml eingebunden wird
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>${jsfProjectStage}</param-value>
    </context-param>
Als letzter Schritt fehlt nun noch, dass im maven-war-plugin die „Filterung“ eingeschaltet wird. Dieser hier etwas unglückliche Begriff führt dazu, dass Variablen in der web.xml ersetzt werden.
...
<webResources>
    <resource>
        <filtering>true</filtering>
        <directory>web/WEB-INF</directory>
        <targetPath>WEB-INF</targetPath>
        <includes>
            <include>**/web.xml</include>
        </includes>
        </resource>
...

(SSL) Zertifikate in Java importieren

Selbst signierte Zertifikate (oder neue) können in den Java Keystore gelegt werden, damit sie z.B. für SSL Verbindungen ordentlich erkannt werden. Dafür wird das Tool „keytool“ verwendet, das sich im bin Verzeichnis des JDK befindet. Es aktualisiert die Dateien

lib\security\cacerts
Initial ist das Passwort für den Key-Store „changeit“. Wie folgt kann das Zertifikat „SelfSigned.cer“ mit dem Alias „AliasIfNeeded“ in den Store aufgenommen werden.
<Pfad zur Java Installation>/jdk11/jdk11.0.6_10/bin/keytool" -import -cacerts -file SelfSigned.cer -storepass changeit -alias AliasIfNeeded -noprompt

Calendar

Feeds

Search

Links

Navigation