Wie bereits angekündigt werde ich in den nächsten Wochen erläutern, wie man im Java-Ökosystem RMI sicher betrieben kann.

Hier soll es um eine Möglichkeit gehen, Klassen für eine JVM zur Verfügung zu stellen. Normalerweise liegen die Klassen, die eine JVM benutzen soll Form von Dateien lokal auf dem Rechner, auf dem die JVM gestartet werden soll vor. Dabei kann es sich um individuelle .class-Dateien ebenso handeln, wie um sogenannte Java ARchives - ZIP-Archive, die .class-Dateien angereichert mit Metadaten enthalten.

Es existiert in Java das Konzept der ClassLoader. Es existieren vershiednee Implementierungen des allgemeinen Kontrakts, der besagt, dass eine solche Implementierung den ByteCode der Klasse zurückgeben muss, wenn sie für die Klasse des als Parameter angegebenen NAmen verantwortlich ist. Der Kontrakt hat noch mehr Punkte, aber für unseren Zweck reicht der angegebene erst einmal aus.

Eine verfügbare Implementation ist der URLClassLoader. Diese Implementierung erlaubt es, über die Nutzung von lokalen Dateien hinauszugehen und JAR-Dateien als URLs anzugeben - so wäre es theoretisch sogar möglich, JARs aus LDAP-Verzeichnissen zu beziehen - aber eben auch via HTTP(S).

Hierbei ist es aber so, dass die jeweiligen Archive komplett in die JVM geladen werden und dort entpackt bzw. analysiert - beim tatsächlichen Laden einer Klasse findet kein Remote-Zugriff mehr statt - statt dessen wird die entsprechende Klasse aus den lokal vorgehaltenen Kopien der heruntergeladenen Archiven extrahiert und zurückgegeben.

Alternativ besteht die Möglichkeit, Klassen einzeln direkt zu laden. In diesem Fall wird ein ClassLoader mit einer Document-Root-URL konfiguriert, an die die (voll qualifizierten) Namen der Klassen angehängt werden, um die einzelnen Klassen herunterzuladen und anschließend der JVM zur Verfügung zu stellen.

RMI ist ein Aspekt der Java-Technologie, der genau so arbeitet.

Um Klassen, die auf anderen Rechnern benötigt werden zur Verfügung zu stellen kann man die .class-Dateien einfach in geeigneter Form in einem Dateisystem ablegen, auf das ein von-der-Stange-HTTP(S)-Server Zugriff haben und diese einfach als Dateien ausliefern.

Man kann alternativ dazu auch einfach eine JVM starten, die wiederum durch ihre eigenen ClassLoader Zugriff auf alle benötigten Klassen hat und in dieser JVM einen HTTPS-Server starten. In diesem Prozess würde dann der Name der Klasse, die bei einem Request relativ zum Document-Root in der URL enthalten ist ausgewertet und mit diese - bzw. deren ByteCode - mit den Java-Mechanismen geladen und als Response zu dem Request zurückgesendet. Ein Beispiel für eine solche Implementierung ist hier abgebildet. Dieses Beispiel enthält bereits die benötigten Teile, um nicht nur einfach HTTP, sondern auch HTTPS anbieten zu können.

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManagerFactory;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class ClassPathServer extends java.lang.Object { private final static org.slf4j.Logger CLASS_LOGGER =org.slf4j.LoggerFactory.getLogger(ClassPathServer.class); private final static org.slf4j.Logger EXCEPTION_LOGGER =org.slf4j.LoggerFactory.getLogger("ExceptionCatcher"); private final de.elbosso.util.ClassLoaderProvider classLoaderProvider;

public ClassPathServer(de.elbosso.util.ClassLoaderProvider classLoaderProvider, int port)throws java.lang.Exception { super(); this.classLoaderProvider=classLoaderProvider; HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

server.createContext("/", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); } public ClassPathServer(de.elbosso.util.ClassLoaderProvider classLoaderProvider,int port,java.security.KeyStore ks, char[] passwd)throws java.lang.Exception { super(); this.classLoaderProvider=classLoaderProvider;

// setup the key manager factory KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passwd);

// setup the trust manager factory TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ks);

// setup the HTTPS context and parameters SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 0);

server.setHttpsConfigurator(new HttpsConfigurator(sslContext) { public void configure(HttpsParameters params) { try { // initialise the SSL context SSLContext context = getSSLContext(); SSLEngine engine = context.createSSLEngine(); params.setNeedClientAuth(false); params.setCipherSuites(engine.getEnabledCipherSuites()); params.setProtocols(engine.getEnabledProtocols());

// Set the SSL parameters SSLParameters sslParameters = context.getSupportedSSLParameters(); params.setSSLParameters(sslParameters);

} catch (Exception ex) { EXCEPTION_LOGGER.error(ex.getMessage(), ex); } } });

server.createContext("/", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); } class MyHandler implements HttpHandler { @Override public void handle(HttpExchange t) throws IOException { CLASS_LOGGER.debug(t.getRequestURI().toString()); try { java.lang.String resNm=t.getRequestURI().toString().substring(1); CLASS_LOGGER.debug("trying to fetch resource "+resNm); java.io.InputStream is=classLoaderProvider.getClassLoader().getResourceAsStream(resNm); java.io.ByteArrayOutputStream baos=new java.io.ByteArrayOutputStream(); de.elbosso.util.io.Utilities.copyBetweenStreams(is,baos,true); t.sendResponseHeaders(200, baos.toByteArray().length); is=new java.io.ByteArrayInputStream(baos.toByteArray()); de.elbosso.util.io.Utilities.copyBetweenStreams(is,t.getResponseBody(),false); } catch(java.lang.Throwable th) { EXCEPTION_LOGGER.error(th.getMessage(),th); String response = th.getMessage(); t.sendResponseHeaders(404, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); } t.getResponseBody().close();; } } }

Alle Artikel rss Wochenübersicht Monatsübersicht Codeberg Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

  • Styles für GeoJSON in EBMap4D

    22.08.2021

    Ich habe bereits darüber berichtet, dass EBMap4D jetzt über die Möglichkeit verfügt, beliebige GeoJSON-Layer darzustellen. Nun ist die Anwendung um die Möglichkeit erweitert worden, in diesen GeoJSON-Daten enthaltene Stilinformationen für die Darstellung zu nutzen.

    Weiterlesen

Neueste Artikel

  • Linux-System SBOM visualisiert als Graph - für YUM!

    Ich schrieb vor einiger Zeit über die Visualisierung und Analyse von Abhängigkeiten in Linux-Distributionen, die auf Debian aufbauen. Damals habe ich noch behauptet, dass andere Distributionen, die zum Beispiel auf yum als Paketmanagement-Lösung setzen nur schwer in dieser Art und Weise analysierbar sind. Ich habe weiter recherchiert und nun auch dafür eine Lösung gefunden.

    Weiterlesen
  • 35 Jahre Antipatterns in Datenmodellen

    Ich habe inzwischen 35 Jahre Softwareentwicklung auf dem Buckel - viele davon für Geld...

    Weiterlesen
  • Highlights der DefCon 34

    Hier meine Highlights der DefCon 34 in Las Vegas

    Weiterlesen

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen Codeberg-Repositories findet man hier.