Der benutzerdefinierte Sicherheitsanbieter funktioniert beim Extrahieren von Postgresql-Serverzertifikaten im Fat Jar, jJava

Java-Forum
Anonymous
 Der benutzerdefinierte Sicherheitsanbieter funktioniert beim Extrahieren von Postgresql-Serverzertifikaten im Fat Jar, j

Post by Anonymous »

Ich versuche, das öffentliche Zertifikat einer beliebigen Postgresql-Datenbank zu extrahieren. Ich habe es mit einfachem Java als Bibliothek und als eigenständiges Fat Jar zum Laufen gebracht, aber nicht in einem Native-Image-Build mit GraalVM. Ich habe hier auf der Github-Seite von GraalVM einen Fehlerbericht gepostet, aber ich habe das Gefühl, dass es sich nicht um einen Fehler handelt. Ich habe das Gefühl, dass ich graalvm bei der Verwendung eines benutzerdefinierten Sicherheitsanbieters nicht richtig konfiguriert habe, bin mir jedoch nicht sicher. Darüber hinaus dauert es manchmal Jahre, bis Fehlerberichte auf ihrer Seite behoben werden können ... Die Verwendung eines benutzerdefinierten Sicherheitsanbieters ist keine Ausnahme, da einige Bouncy Castle verwenden und er nachweislich mit dem nativen GraalVM-Image funktioniert. Deshalb habe ich das Gefühl, dass ich möglicherweise eine Konfiguration übersehen habe, aber mein Wissen in diesem Bereich ist recht gering und ich hoffe, hier Hilfe von der Community zu erhalten.
Ich öffne eine Postgresql-Datenbank mit aktiviertem SSL mit diesem Befehl:

Code: Select all

docker run --rm -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:12 -c ssl=on -c ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem -c ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
Ich verwende hier den Postgresql-JDBC-Treiber

Code: Select all

org.postgresql
postgresql
42.7.8

Ich verwende meine eigene Bibliothek, die das Dienstprogramm zum Erfassen von Zertifikaten während des SSL-Handshakes bereitstellt. Es kann hier gefunden werden: GitHub – Ayza Und die Maven-Deklaration:

Code: Select all

io.github.hakky54
ayza
10.0.2

Und ich verwende die folgenden Plugins, um das native Image mit Maven zu erstellen:

Code: Select all

org.apache.maven.plugins
maven-shade-plugin
3.6.1

crip


${application-main-class}





package

shade





org.graalvm.nativeimage
native-image-maven-plugin
21.2.0



native-image

package



false

--no-fallback
-H:EnableURLProtocols=https
-H:EnableURLProtocols=http
-H:Name=crip
-march=compatibility
--future-defaults=all
-H:AdditionalSecurityProviders=nl.altindag.ssl.provider.FenixProvider
-H:AdditionalSecurityServiceTypes=nl.altindag.ssl.provider.FenixProvider



Ein kleiner reproduzierbarer Codeausschnitt würde etwa so aussehen:

Code: Select all

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.ProviderUtils;
import nl.altindag.ssl.util.TrustManagerUtils;

import javax.net.ssl.X509ExtendedTrustManager;
import java.security.cert.X509Certificate;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class App {

public static void main(String[] args) {
List capturedCertificates = new CopyOnWriteArrayList();
X509ExtendedTrustManager trustManager = TrustManagerUtils.createCertificateCapturingTrustManager(capturedCertificates);
SSLFactory sslFactory = SSLFactory.builder()
.withTrustMaterial(trustManager)
.build();

ProviderUtils.configure(sslFactory);

try (Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/")) {
// calling getConnection to trigger the SSL handshake
} catch (SQLException ignored) {

} finally {
ProviderUtils.remove();
}

System.out.println("Amount of captured certificates: "  + capturedCertificates.size());
System.out.println("Captured certificates: ");
capturedCertificates.forEach(System.out::println);
}

}
Die Ausgabe dieser Hauptmethode ist:

Code: Select all

Amount of captured certificates: 1
Captured certificates:
[
[
Version: V3
Subject: CN=localhost
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

Key:  Sun RSA public key, 2048 bits
params: null
modulus: 31211996126076596147999338027719495623709022913370591887632988720610004032097760016008345790156702342195009365368156959125712771875783177237122222689823196867653884881232812806555219009894942490288929180418493391254446706821512257672335346037892662566871554703869495753177918085413564534189604133485556381243824728959475350934112799133217319962706911642363362829383506782715080237659432175302233892295525841514632241532146162042423493679178952709219542178492548540252419613055282285791992580920922147065723236149506037158805915815773798115281679259390591323530600590552570955839294244330974808478242461235603855182171
public exponent: 65537
Validity: [From: Tue Jan 14 03:32:28 CET 2025,
To: Fri Jan 12 03:32:28 CET 2035]
Issuer: CN=localhost
SerialNumber: 26:97:cd:84:a8:93:e2:5d:d3:2c:a0:ea:40:8d:7c:93:bf:06:e4:1d

Certificate Extensions: 3
[1]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]

[2]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: localhost
]

[3]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: ED 8A 72 6D B7 87 AB 26   5E 6C 75 33 5B C9 BE E8  ..rm...&^lu3[...
0010: 04 6E 1A 06                                        .n..
]
]

]
Algorithm: [SHA256withRSA]
Signature:
0000: 00 EE 2E CB 87 F7 20 FD   B6 36 AE E1 7B 3F AA 8F  ...... ..6...?..
0010: 44 38 42 94 BB 50 77 BE   69 21 CA 2B 4A 2F 90 1B  D8B..Pw.i!.+J/..
0020: 93 B0 3D B7 FA FB DB 56   40 BA F6 20 52 78 FC 0F  ..=....V@.. Rx..
0030: EA DE F1 66 13 6F 91 30   B9 48 6A B8 2A 32 32 FE  ...f.o.0.Hj.*22.
0040: 79 DF C8 DD B2 6D 83 C7   D7 56 04 5D 0F 4B 6B 98  y....m...V.].Kk.
0050: 73 AE C3 5C A5 3F 52 3C   A3 F1 6E CF 6D AF 28 E4  s..\.?R..2.EO..D..
00D0: 47 02 D6 D4 86 34 A4 19   04 3E B2 7B 8F 72 3F 62  G....4...>...r?b
00E0: 19 02 AF F8 C6 9B 96 14   D1 36 AA D7 74 39 7F C3  .........6..t9..
00F0: AB 49 02 94 CE 96 7C B1   F2 D5 1F 5B A2 73 DE B9  .I.........[.s..

]
Ich werde versuchen, einen Kontext zu geben. Ich verwende den Postgres-Treibermanager, um mit der eigentlichen Datenbank zu interagieren. Beim Herstellen der Verbindung wird unter dem Deckmantel der Postgres-Bibliothek selbst eine SSLContext-Instanz erstellt, siehe hier. Ich fange dies ab, indem ich hier einen benutzerdefinierten Sicherheitsanbieter hinzufüge. Dieser Codeausschnitt erstellt einen benutzerdefinierten Sicherheitsanbieter und fügt ihn an der ersten Position ein. Unter dem Deckmantel werden die folgenden Codeanweisungen zuerst und dann ausgeführt
Mit dieser Logik kann ich problemlos einen SSL-Kontext einer anderen Bibliothek durch einen benutzerdefinierten ersetzen. In diesem Fall stelle ich mein eigenes zur Verfügung, das Serverzertifikate erfassen kann. Dies funktioniert, wenn es in meiner IDE und mit einem Fat-Jar ausgeführt wird. Es funktioniert jedoch nicht, wenn es mit einer nativen ausführbaren Datei ausgeführt wird, die mit einem nativen Image erstellt wurde. Obwohl ich oben eine kleine funktionierende App gepostet habe, hängt dieses Problem mit meinen Änderungen am Projekt Certificate Ripper zusammen. Die tatsächlichen Codeänderungen finden Sie hier
Wenn Sie es mit den Änderungen aus dem Repository ausprobieren möchten, können Sie die folgenden Schritte ausführen:
  • Code: Select all

    git clone [email protected]:Hakky54/certificate-ripper.git
  • Code: Select all

    cd certificate-ripper
  • Code: Select all

    git switch feature/support-for-postgres-db
  • Code: Select all

    mvn clean install -Pfat-jar
  • Code: Select all

    docker run -d --rm -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:12 -c ssl=on -c ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem -c ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
  • Code: Select all

    java -jar target/crip.jar print -u=postgresql://localhost:5432/
    --> druckt das Zertifikat
  • Code: Select all

    mvn clean install -Pnative-image
  • Code: Select all

    ./target/crip print -u=postgresql://localhost:5432/
    --> druckt das Zertifikat nicht
Es ist also tatsächlich möglich, die Zertifikate von Postgresql mit diesen Snippets zu erfassen. Es funktioniert in einfachem Java, aber nicht mehr, wenn es zu einem nativen Image kompiliert wird. Ich habe überprüft, ob der benutzerdefinierte Sicherheitsanbieter vorhanden ist, und das ist der Fall. Ich erhalte die folgende Ausgabe:

Code: Select all

Fenix version 1.0, CertificateRipper version 1.0, SUN version 25, SunRsaSign version 25, SunEC version 25, SunJSSE version 25, SunJCE version 25, SunSASL version 25, JdkLDAP version 25, JdkSASL version 25, Apple version 25
Wie Sie sehen können, befindet es sich auch an der ersten Position, also sollte es einfach funktionieren. Ich habe einfach das Gefühl, dass zur Laufzeit der benutzerdefinierte Sicherheitsanbieter nicht verwendet wird, weil er überhaupt nicht vorhanden oder möglicherweise nicht verfügbar ist. Da ich nicht sicher bin, ob ich die hier erläuterten Richtlinien korrekt befolgt habe: https://www.graalvm.org/latest/referenc ... gistration
Ich hoffe, dass mich jemand zu diesem Thema beim Debuggen anleiten kann.
Update 1 – Alternative ohne JDBC-Treiber
Wie @Robert im Kommentarbereich vorgeschlagen hat, habe ich es auch ohne den Postgresql-JDBC-Treiber und nur mit einfachem Code versucht, aber dann konnte ich keine Verbindung zum Server herstellen. Unten ist der Codeausschnitt, den ich verwendet habe:

Code: Select all

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.TrustManagerUtils;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class App {

public static void main(String[] args) {
List capturedCertificates = new CopyOnWriteArrayList();
X509ExtendedTrustManager trustManager = TrustManagerUtils.createCertificateCapturingTrustManager(capturedCertificates);
SSLFactory sslFactory = SSLFactory.builder()
.withTrustMaterial(trustManager)
.build();

try {
SSLSocket socket = (SSLSocket) sslFactory.getSslSocketFactory().createSocket("localhost", 5432);
socket.setSoTimeout(10000);
socket.setUseClientMode(true);
socket.startHandshake();
} catch (Exception e) {
e.printStackTrace();
}
}

}
Und die folgenden Protokolle werden mit dem Stacktrace generiert:

Code: Select all

javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.056 CET|SSLCipher.java:423|jdk.tls.keyLimits:  entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.066 CET|SSLCipher.java:423|jdk.tls.keyLimits:  entry =  ChaCha20-Poly1305 KeyUpdate 2^37.  CHACHA20-POLY1305:KEYUPDATE = 137438953472
javax.net.ssl|WARNING|30|main|2025-12-22 24:46:43.159 CET|ServerNameExtension.java:265|Unable to indicate server name
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.159 CET|SSLExtensions.java:272|Ignore, context unavailable extension: server_name
javax.net.ssl|INFO|30|main|2025-12-22 24:46:43.162 CET|AlpnExtension.java:174|No available application protocols
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.162 CET|SSLExtensions.java:272|Ignore, context unavailable extension: application_layer_protocol_negotiation
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.162 CET|SessionTicketExtension.java:350|Stateless resumption supported
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.166 CET|SSLExtensions.java:272|Ignore, context unavailable extension: cookie
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.235 CET|SSLExtensions.java:272|Ignore, context unavailable extension: renegotiation_info
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.235 CET|PreSharedKeyExtension.java:659|No session to resume.
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.235 CET|SSLExtensions.java:272|Ignore, context unavailable extension: pre_shared_key
javax.net.ssl|DEBUG|30|main|2025-12-22 24:46:43.246 CET|ClientHello.java:638|Produced ClientHello handshake message (
"ClientHello": {
"client version"      : "TLSv1.2",
"random"              : "2B9ADD1F533E9AFF111BBACDCF738A5FA7CA694973016903E7DE8BB38D2F2A6B",
"session id"          : "3CBEEDA9D55BFC0FA25D66DF92C37AF4C9E91B5F20607FFEFD7EB86115CDBE11",
"cipher suites"       : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_CHACHA20_POLY1305_SHA256(0x1303), TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA9), TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA8), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCAA), TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(0x009E), TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(0x00A2), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(0xC024), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(0xC028), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(0xC023), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(0xC027), TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B), TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067), TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(0x0040), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039), TLS_DHE_DSS_WITH_AES_256_CBC_SHA(0x0038), TLS_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033), TLS_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032), TLS_EMPTY_RENEGOTIATION_INFO_SCSV(0x00FF)]",
"compression methods" : "00",
"extensions"           : [
"status_request (5)": {
"certificate status type": ocsp
"OCSP status request": {
"responder_id": 
"request extensions": {

}
}
},
"supported_groups (10)": {
"named groups": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
},
"ec_point_formats (11)": {
"formats": [uncompressed]
},
"status_request_v2 (17)": {
"cert status request": {
"certificate status type": ocsp_multi
"OCSP status request": {
"responder_id": 
"request extensions": {

}
}
}
},
"extended_master_secret (23)": {

},
"session_ticket (35)": {

},
"signature_algorithms (13)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, ed25519, ed448, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
},
"supported_versions (43)": {
"versions": [TLSv1.3, TLSv1.2]
},
"psk_key_exchange_modes (45)": {
"ke_modes": [psk_dhe_ke]
},
"signature_algorithms_cert (50)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, ed25519, ed448, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
},
"key_share (51)": {
"client_shares": [
{
"named group": x25519
"key_exchange": {
0000: 9B 70 95 2B 4B 25 76 42   52 BD 2E 09 C8 DA A8 08  .p.+K%vBR.......
0010: A2 39 04 4C 40 F5 EC 86   33 49 65 71 BD 0F 9F 6E  [email protected]
}
},
{
"named group": secp256r1
"key_exchange": {
0000: 04 57 5B 93 97 52 BF 01   91 B1 4B E1 F0 E9 BB B2  .W[..R....K.....
0010: AB 6F 44 0E 2F BA 39 03   E1 3F 1B 06 E9 10 94 D3  .oD./.9..?......
0020: AB 02 2F 05 3E A2 A5 FC   4B 81 10 E5 21 76 17 7F  ../.>...K...!v..
0030: D3 7D 00 2F C4 8C DA 2D   10 C5 4B 83 D2 79 D9 6A  .../...-..K..y.j
0040: BB
}
},
]
}
]
}
)
javax.net.ssl|ERROR|30|main|2025-12-22 24:46:43.251 CET|TransportContext.java:368|Fatal (HANDSHAKE_FAILURE): Couldn't kickstart handshaking (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
at java.base/sun.security.ssl.SSLSocketImpl.handleEOF(SSLSocketImpl.java:1716)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1514)
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
at [email protected]/nl.altindag.crip.App2.main(App2.java:25)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:494)
at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:483)
at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)
... 4 more}

)

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post