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

  • Traceroute Visualisierung mittels GeoJSON

    29.05.2021

    Dieses Wochenende war es wieder einmal an der Zeit für ein Kaninchenbau-Projekt. Es gibt inzwischen einige kostenlose Geolocation-APIs im Netz und ich wurde - durch das Internet - auf die Idee gebracht, das Ergebnis eines Aufrufs von traceroute auf einer Landkarte zu visualisieren

    Weiterlesen

Neueste Artikel

  • Trusted Platform Module im Thin Client Fujitsu Futro nutzen

    Es geht hier speziell um das Modell s920. Bei Recherchen zu meinen letzten Experimenten hatte ich auch realisiert, dass ich einen solchen Thin Client mit montiertem TPM besitze - das musste natürlich ausprobiert werden!

    Weiterlesen
  • Hardwarebeschleunigung zur Berechnung des Mandelbrotfraktals mittels Nvidia

    Ich habe vor geraumer Zeit immer wieder einmal das Mandelbrotfraktal berechnet - auf der Kommandozeile genauso wie mittels Shadern auf der Graphikkarte. Nun bin ich dahin zurückgekehrt: Ich wollte wissen, ob dieser Code heute noch funktioniert und mit einer Nvidia-Karte ebenso funktioniert, wie damals mit meiner guten alten Radeon...

    Weiterlesen
  • Asymmetrische Kryptographie

    Ich habe mich mit der Idee schon länger getragen: Nochmal einen Rundumschlag zu asymmetrischer Kryptographie zu machen. Dabei werde ich mich auf Demonstrationen der einzelnen Konzepte und Operationen mit Beispielcode konzentrieren und zu jedem der vorgestellten Konzepte mehr oder weniger ausführlich bezüglich der Einsatzszenarien und Vor- und Nachteile Stellung beziehen

    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.