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();;
}
}
}
Raspi Linkdump 2021 I
25.04.2021
Ein Linkdump rund um den Raspi
WeiterlesenAndroid Basteln C und C++ Chaos Datenbanken Docker dWb+ ESP Wifi Garten Geo Go GUI Hardware Java Jupyter JupyterBinder Komponenten Links Linux Markdown Markup Music Numerik OpenSource PKI-X.509-CA Präsentationen Python QBrowser Rants Raspi Revisited Security Software-Test sQLshell TeleGrafana Verschiedenes Video Virtualisierung Windows Upcoming...
Asymmetrische KryptographieIch 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
WeiterlesenWie bereits angekündigt werde ich in den nächsten Wochen einige Aspekte asymmetrischer Kryptographie beschreiben. Der vorliegende Artikel erläutert die Absicherung von Javas Remote Method Invocation (RMI).
WeiterlesenEine neue Version der sQLshell ist verfügbar!
WeiterlesenManche 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.