3838import java .time .Duration ;
3939import java .time .temporal .ChronoUnit ;
4040import java .util .ArrayList ;
41+ import java .util .Collections ;
4142import java .util .HashMap ;
42- import java .util .LinkedHashMap ;
4343import java .util .List ;
4444import java .util .Map ;
4545import java .util .Objects ;
46- import java .util .Optional ;
4746import java .util .function .UnaryOperator ;
4847import java .util .stream .Collectors ;
4948
5049import nl .altindag .ssl .util .CertificateUtils ;
50+ import nl .altindag .ssl .util .ClientRunnable ;
5151import nl .altindag .sude .Logger ;
5252import nl .altindag .sude .LoggerFactory ;
5353
@@ -66,48 +66,45 @@ public CertificateRipperClient(ClientConfig clientConfig) {
6666
6767 public CertificateHolder getCertificateHolder () {
6868 List <String > resolvedUrls = getUniqueUrls (clientConfig .getUrls ());
69- Map <String , List <X509Certificate >> urlsToCertificates = getCertificates (resolvedUrls );
69+ Map <String , List <X509Certificate >> certificates = getCertificates (resolvedUrls );
70+ Map <String , List <X509Certificate >> siblings = getSiblings (certificates );
71+ Map <String , List <X509Certificate >> systemCertificates = getSystemCertificates ();
7072
71- addSiblingsIfNeeded (urlsToCertificates );
72- urlsToCertificates = filterCertificatesIfNeeded (urlsToCertificates , clientConfig .getCertificateType ());
73- addSystemCertificatesIfNeeded (urlsToCertificates );
73+ Map <String , List <X509Certificate >> urlsToCertificates = mergeMaps (certificates , siblings , systemCertificates );
74+ urlsToCertificates = filterCertificates (urlsToCertificates , clientConfig .getCertificateType ());
7475
7576 return new CertificateHolder (urlsToCertificates );
7677 }
7778
7879 private Map <String , List <X509Certificate >> getCertificates (List <String > urls ) {
79- return urls .stream ().distinct ().parallel ()
80- .map (this ::getCertificates )
81- .filter (Optional ::isPresent )
82- .map (Optional ::get )
83- .collect (Collectors .collectingAndThen (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue , (key1 , key2 ) -> key1 , LinkedHashMap ::new ), HashMap ::new ));
84- }
85-
86- private Optional <Map .Entry <String , List <X509Certificate >>> getCertificates (String url ) {
87- try {
88- CertificateExtractingClient client = createClient (url ).build ();
89- List <X509Certificate > certificates = client .get (url );
90- return Optional .of (Map .entry (url , certificates ));
91- } catch (Exception e ) {
92- LOGGER .debug (String .format ("Could not extract from %s" , url ), e );
93- return Optional .empty ();
94- }
80+ return urls .stream ().parallel ()
81+ .map (url -> {
82+ try {
83+ var client = createClient (url );
84+ List <X509Certificate > certificates = client .get (url );
85+ return Map .entry (url , certificates );
86+ } catch (Exception e ) {
87+ LOGGER .debug (String .format ("Could not extract from %s" , url ), e );
88+ return null ;
89+ }})
90+ .filter (Objects ::nonNull )
91+ .collect (Collectors .collectingAndThen (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ), HashMap ::new ));
9592 }
9693
97- private CertificateExtractingClient .Builder createClient (String url ) {
98- CertificateExtractingClient .Builder clientBuilder = createClient ();
99- URI uri = URI .create (url );
100- switch (uri .getScheme ()) {
101- case "wss" -> clientBuilder .withClientRunnable (new WebSocketClientRunnable ());
102- case "ftps" -> clientBuilder .withClientRunnable (new FtpsClientRunnable ());
103- case "smtps" -> clientBuilder .withClientRunnable (new SmtpClientRunnable ());
104- case "imaps" -> clientBuilder .withClientRunnable (new ImapClientRunnable ());
105- case "postgresql" -> clientBuilder .withClientRunnable (new PostgresClientRunnable ());
106- case "mysql" -> clientBuilder .withClientRunnable (new MySQLClientRunnable ());
107- default -> {}
108- }
94+ private CertificateExtractingClient createClient (String url ) {
95+ ClientRunnable clientRunnable = switch (URI .create (url ).getScheme ()) {
96+ case "wss" -> WebSocketClientRunnable .getInstance ();
97+ case "ftps" -> FtpsClientRunnable .getInstance ();
98+ case "smtps" -> SmtpClientRunnable .getInstance ();
99+ case "imaps" -> ImapClientRunnable .getInstance ();
100+ case "postgresql" -> PostgresClientRunnable .getInstance ();
101+ case "mysql" -> MySQLClientRunnable .getInstance ();
102+ default -> null ;
103+ };
109104
110- return clientBuilder ;
105+ return createClient ()
106+ .withClientRunnable (clientRunnable )
107+ .build ();
111108 }
112109
113110 private CertificateExtractingClient .Builder createClient () {
@@ -156,9 +153,9 @@ private List<String> getUniqueUrls(List<String> urls) {
156153 return uniqueUrls ;
157154 }
158155
159- private void addSiblingsIfNeeded (Map <String , List <X509Certificate >> urlsToCertificates ) {
156+ private Map < String , List < X509Certificate >> getSiblings (Map <String , List <X509Certificate >> urlsToCertificates ) {
160157 if (!clientConfig .getResolveSiblings ()) {
161- return ;
158+ return Collections . emptyMap () ;
162159 }
163160
164161 ProgressBarBuilder pbb = new ProgressBarBuilder ()
@@ -167,38 +164,40 @@ private void addSiblingsIfNeeded(Map<String, List<X509Certificate>> urlsToCertif
167164 .setStyle (ProgressBarStyle .COLORFUL_UNICODE_BAR )
168165 .setTaskName ("Resolving sibling certificates" ).showSpeed ();
169166
170- List <String > urls = urlsToCertificates .values ().stream (). parallel ()
171- .flatMap (certificates -> UriUtils .getDnsNames (certificates ).stream ())
167+ List <String > urls = urlsToCertificates .values ().stream ()
168+ .flatMap (certificates -> UriUtils .extractHostsFromSAN (certificates ).stream ())
172169 .distinct ()
173170 .toList ();
174171
175172 CertificateExtractingClient client = createClient ().build ();
176- Map <String , List <X509Certificate >> siblings = ProgressBar .wrap (urls .stream (), pbb )
177- .map (url -> {
173+ ProgressBar .wrap (urls .stream (), pbb )
174+ .parallel ()
175+ .forEach (url -> {
178176 try {
179- return Map . entry ( url , client .get (url ) );
177+ client .call (url );
180178 } catch (Exception e ) {
181- return null ;
179+ LOGGER . debug ( String . format ( "Could not extract sibling certificate from %s" , url ), e ) ;
182180 }
183- })
184- .filter (Objects ::nonNull )
185- .collect (Collectors .collectingAndThen (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue , (key1 , key2 ) -> key1 , LinkedHashMap ::new ), HashMap ::new ));
181+ });
186182
187- urlsToCertificates .putAll (siblings );
183+ return client .getCertificatesCollector ().entrySet ().stream ()
184+ .map (entry -> Map .entry ("https://" + entry .getKey (), entry .getValue ()))
185+ .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ));
188186 }
189187
190- private void addSystemCertificatesIfNeeded ( Map <String , List <X509Certificate >> urlsToCertificates ) {
188+ private Map <String , List <X509Certificate >> getSystemCertificates ( ) {
191189 if (clientConfig .getUrls ().contains (SYSTEM )) {
192190 try {
193191 List <X509Certificate > systemTrustedCertificates = CertificateUtils .getSystemTrustedCertificates ();
194- urlsToCertificates . put (SYSTEM , systemTrustedCertificates );
192+ return Map . of (SYSTEM , systemTrustedCertificates );
195193 } catch (UnsatisfiedLinkError error ) {
196194 LOGGER .debug (String .format ("Unable to extract system certificates for %s" , System .getProperty ("os.name" )));
197195 }
198196 }
197+ return Collections .emptyMap ();
199198 }
200199
201- Map <String , List <X509Certificate >> filterCertificatesIfNeeded (Map <String , List <X509Certificate >> urlsToCertificates , CertificateType type ) {
200+ Map <String , List <X509Certificate >> filterCertificates (Map <String , List <X509Certificate >> urlsToCertificates , CertificateType type ) {
202201 return switch (type ) {
203202 case ALL -> urlsToCertificates ;
204203 case LEAF -> filterCertificates (urlsToCertificates , certificates -> List .of (certificates .getFirst ()));
@@ -221,4 +220,13 @@ private Map<String, List<X509Certificate>> filterCertificates(Map<String, List<X
221220 .collect (Collectors .collectingAndThen (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ), HashMap ::new ));
222221 }
223222
223+ @ SafeVarargs
224+ private static <T , U > Map <T , U > mergeMaps (Map <T , U >... maps ) {
225+ Map <T , U > mergedMap = new HashMap <>();
226+ for (Map <T , U > map : maps ) {
227+ mergedMap .putAll (map );
228+ }
229+ return mergedMap ;
230+ }
231+
224232}
0 commit comments