Nucleus
Performance & native

SSL natif — les racines de confiance depuis l'OS, pas le JRE

Un X509TrustManager qui merge les défauts JVM avec le keychain / cert store de l'OS, pour que les racines d'entreprise et CAs installés par MDM marchent simplement.

Une app JVM livrée avec un JRE embarqué ne fait confiance qu'aux cacerts cuits dans ce JRE. Les CA racines installées par une politique IT d'entreprise, un MDM, ou un proxy d'inspection SSL sont invisibles — chaque handshake HTTPS vers ces hôtes échoue. native-ssl lit les certificats trustés directement depuis le store de l'OS sur les trois plateformes et les merge avec les défauts JVM dans un seul X509ExtendedTrustManager.

TL;DR

  • Un seul singleton : NativeTrustManager. Lazy, thread-safe.
  • macOS : framework Security via JNI — ancres système + domaine user/admin avec évaluation complète des trust settings.
  • Windows : Crypt32 via JNI — stores ROOT et CA sur les scopes user, machine, group policy, enterprise.
  • Linux : pure-JVM — lit les bundles PEM que crypto/x509 lit.
  • HTTPS vers les hôtes corporate, MDM ou inspectés par proxy fonctionne sans câblage manuel du trust store.

Install

dependencies {
    implementation("dev.nucleusframework:nucleus.native-ssl:<version>")
}

Quickstart

import dev.nucleusframework.nativessl.NativeTrustManager
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager

val trustManager: X509TrustManager = NativeTrustManager.trustManager
val sslContext: SSLContext = NativeTrustManager.sslContext
val sslSocketFactory: SSLSocketFactory = NativeTrustManager.sslSocketFactory

Passe-les à le client HTTP. Si ce client est java.net.http, OkHttp ou Ktor, utilise plutôt les adaptateurs HTTP natif — ils s'occupent du câblage.

Comment ça marche

macOS

Framework Security via libnucleus_ssl.dylib (embarqué dans le JAR pour arm64 et x86_64). Deux passes :

  1. Ancres systèmeSecTrustCopyAnchorCertificates() renvoie chaque racine livrée par Apple.
  2. Domaines user & adminSecTrustSettingsCopyCertificates() énumère tout ce qui a des trust settings explicites. Chaque candidat est évalué via une logique miroir de JetBrains jvm-native-trusted-roots :
    • Trust settings user d'abord, puis admin.
    • Pas de trust settings → évaluation live via SecTrustEvaluateWithError.
    • Trust settings array vide → trusté (selon les docs Apple).
    • kSecTrustSettingsResult doit être TrustRoot ; le cert doit être auto-signé (égalité de DN et signature vérifiée contre sa propre clé).
    • kSecTrustSettingsPolicy, si présent, doit être kSecPolicyAppleSSL.
    • Les clés de contrainte inconnues rejettent le certificat — mieux vaut échouer fermé que relâcher silencieusement les règles.

Windows

Crypt32 via nucleus_ssl.dll (embarqué pour x64 et ARM64). Scanne cinq emplacements de stores sur deux types :

Type de storeRègle d'inclusion
ROOTCAs racines trustées — incluses inconditionnellement
CACAs intermédiaires — validées via CertGetCertificateChain + CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE) avec révocation cache-only

Emplacements : CURRENT_USER, LOCAL_MACHINE, CURRENT_USER_GROUP_POLICY, LOCAL_MACHINE_GROUP_POLICY, LOCAL_MACHINE_ENTERPRISE. Les certs distribués par Group Policy et Active Directory atterrissent dans des stores que SunMSCAPI n'atteint pas.

La déduplication se fait par empreinte SHA-1 (CERT_HASH_PROP_ID), l'identité standard de Windows. Si la lib JNI échoue à se charger, le module retombe sur les keystores Windows-ROOT, Windows-CA, Windows-MY de SunMSCAPI.

Linux

Pure-JVM — pas de lib partagée. Lit les mêmes chemins que crypto/x509 de Go :

CheminDistribution
/etc/ssl/certs/ca-certificates.crtDebian, Ubuntu, Gentoo
/etc/pki/tls/certs/ca-bundle.crtFedora, RHEL 6
/etc/ssl/ca-bundle.pemopenSUSE
/etc/pki/tls/cacert.pemOpenELEC
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pemCentOS, RHEL 7
/etc/ssl/cert.pemAlpine
/etc/ssl/certs/ (dir)SLES10/SLES11
/etc/pki/tls/certs/ (dir)Fedora, RHEL
/system/etc/security/cacerts/ (dir)Android

Déduplication par contenu DER entre toutes les sources.

Référence

APIDescription
NativeTrustManager.trustManager: X509ExtendedTrustManagerTrust manager combiné (défauts JVM + natif OS)
NativeTrustManager.sslContext: SSLContextContexte TLS initialisé avec le trust manager
NativeTrustManager.sslSocketFactory: SSLSocketFactoryDérivé de sslContext
NativeTrustManager.isAvailable(): Booleanfalse seulement si le bridge JNI a échoué sous macOS/Windows
interface NativeCertificateProviderSPI si l'on veut brancher une source de certs custom

Cas d'usage

  • Réseaux d'entreprise avec des CAs racines internes déployées via Group Policy ou MDM.
  • Proxies d'inspection SSL (Zscaler, Netskope, Blue Coat, …) qui re-signent chaque cert avec une racine custom.
  • PKI auto-hébergée pour les environnements de staging où l'utilisateur a installé la racine de dev en local.
  • Tout ce à quoi keytool -import était historiquement la réponse.

Notes

ProGuard

Les classes du bridge JNI doivent être préservées sous macOS et Windows. Le plugin Gradle Nucleus ajoute les règles automatiquement ; si l'on maintient la config ProGuard à la main :

-keep class dev.nucleusframework.nativessl.mac.NativeSslBridge {
    native <methods>;
}
-keep class dev.nucleusframework.nativessl.windows.WindowsSslBridge {
    native <methods>;
}

Logging

Les logs debug sont taggés NativeCertificateProvider, NativeSslBridge, WindowsCertificateProvider, LinuxCertificateProvider, etc. Coupés par défaut. Active-les avec :

import dev.nucleusframework.core.runtime.tools.allowNucleusRuntimeLogging

allowNucleusRuntimeLogging = true