Я запускаю базу данных Postgresql с включенным ssl с помощью этой команды:
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
Я использую драйвер jdbc postgresql отсюда
org.postgresql
postgresql
42.7.8
Я использую свою собственную библиотеку, которая предоставляет утилиту для захвата сертификатов во время установления связи SSL. Его можно найти здесь: GitHub - Ayza. И декларация maven:
io.github.hakky54
ayza
10.0.2
Для создания собственного образа с помощью maven я использую следующие плагины:
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
Небольшой воспроизводимый фрагмент кода может выглядеть примерно так:
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);
}
}
Вывод этого основного метода:
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
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..
]
Я постараюсь дать некоторый контекст. Я использую диспетчер драйверов Postgres для взаимодействия с реальной базой данных. При установке соединения он создаст экземпляр sslcontext под прикрытием самой библиотеки postgres, см. здесь. Я перехватываю это, добавляя сюда специального поставщика безопасности. Этот фрагмент кода создает собственный поставщик безопасности и вставляет его в первую позицию. Под прикрытием следующие операторы кода будут выполнены первыми и вторыми.
С помощью этой логики я могу легко заменить sslcontext другой библиотеки на собственный. В этом случае я предоставляю свой собственный, который может захватывать сертификаты сервера. Это работает при работе в моей IDE и с толстой банкой. Однако он не работает при запуске с собственным исполняемым файлом, созданным с собственным образом. Хотя выше я опубликовал небольшое рабочее приложение, эта проблема связана с моими изменениями в проекте Certificate Ripper. Фактические изменения кода можно найти здесь.
Если вы хотите попробовать его с изменениями из репозитория, вы можете выполнить следующие шаги:
- git clone git@github.com:Hakky54/certificate-ripper.git
- cd certificate-ripper
- git switch feature/support-for-postgres-db
- mvn clean install -Pfat-jar
- 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
- java -jar target/crip.jar print -u=postgresql://localhost:5432/ --> распечатает сертификат
- mvn clean install -Pnative-image
- ./target/crip print -u=postgresql://localhost:5432/ --> не печатает сертификат
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
Как видите, он тоже находится на первой позиции, так что все должно работать. У меня просто такое ощущение, что во время выполнения пользовательский поставщик безопасности не используется, потому что он не находится на первом месте или, возможно, недоступен. Поскольку я не уверен, правильно ли я следовал рекомендациям, описанным здесь: https://www.graalvm.org/latest/referenc ... gistration
Я надеюсь, что кто-нибудь сможет помочь мне в отношении этой темы, как это происходит при отладке.
Обновление 1 - альтернатива без драйвера jdbc
Как предложил @Robert в разделе комментариев, я также пробовал без драйвера Postgresql jdbc и просто с простым кодом, однако тогда мне не удалось подключиться к серверу. Ниже приведен фрагмент кода, который я использовал:
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();
}
}
}
С помощью трассировки стека генерируются следующие журналы:
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 .9.L@...3Ieq...n
}
},
{
"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 nl.altindag.certificate.ripper@2.6.2-SNAPSHOT/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}
)
Подробнее здесь: https://stackoverflow.com/questions/798 ... mage-while
Мобильная версия