Skip to content

Commit ca78a52

Browse files
committed
Improved performance of extracting siblings
1 parent 10bfb5a commit ca78a52

26 files changed

Lines changed: 99 additions & 211 deletions

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
<properties>
3434
<version.java>21</version.java>
35-
<version.ayza>10.0.3</version.ayza>
35+
<version.ayza>10.0.4</version.ayza>
3636
<version.sude>2.0.2</version.sude>
3737
<version.slf4j>2.0.17</version.slf4j>
3838
<version.picocli>4.7.7</version.picocli>

src/main/java/nl/altindag/crip/client/CertificateRipperClient.java

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,8 @@
3939
import java.time.temporal.ChronoUnit;
4040
import java.util.ArrayList;
4141
import java.util.HashMap;
42-
import java.util.LinkedHashMap;
4342
import java.util.List;
4443
import java.util.Map;
45-
import java.util.Objects;
46-
import java.util.Optional;
4744
import java.util.function.UnaryOperator;
4845
import java.util.stream.Collectors;
4946

@@ -59,55 +56,42 @@ public class CertificateRipperClient {
5956
private static final String SYSTEM = "system";
6057

6158
private final ClientConfig clientConfig;
59+
private final CertificateExtractingClient client;
6260

6361
public CertificateRipperClient(ClientConfig clientConfig) {
6462
this.clientConfig = clientConfig;
63+
this.client = createClient().build();
6564
}
6665

6766
public CertificateHolder getCertificateHolder() {
6867
List<String> resolvedUrls = getUniqueUrls(clientConfig.getUrls());
69-
Map<String, List<X509Certificate>> urlsToCertificates = getCertificates(resolvedUrls);
68+
pingUrls(resolvedUrls);
7069

71-
addSiblingsIfNeeded(urlsToCertificates);
72-
urlsToCertificates = filterCertificatesIfNeeded(urlsToCertificates, clientConfig.getCertificateType());
70+
pingSiblings(client.getCertificatesCollector());
71+
Map<String, List<X509Certificate>> urlsToCertificates = filterCertificatesIfNeeded(client.getCertificatesCollector(), clientConfig.getCertificateType());
7372
addSystemCertificatesIfNeeded(urlsToCertificates);
7473

7574
return new CertificateHolder(urlsToCertificates);
7675
}
7776

78-
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-
}
95-
}
96-
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-
}
77+
private void pingUrls(List<String> urls) {
78+
urls.stream().parallel().forEach(url -> {
79+
var clientRunnable = switch (URI.create(url).getScheme()) {
80+
case "wss" -> WebSocketClientRunnable.getInstance();
81+
case "ftps" -> FtpsClientRunnable.getInstance();
82+
case "smtps" -> SmtpClientRunnable.getInstance();
83+
case "imaps" -> ImapClientRunnable.getInstance();
84+
case "postgresql" -> PostgresClientRunnable.getInstance();
85+
case "mysql" -> MySQLClientRunnable.getInstance();
86+
default -> null;
87+
};
10988

110-
return clientBuilder;
89+
try {
90+
client.call(url, clientRunnable);
91+
} catch (Exception e) {
92+
LOGGER.debug(String.format("Could not extract from %s", url), e);
93+
}
94+
});
11195
}
11296

11397
private CertificateExtractingClient.Builder createClient() {
@@ -156,7 +140,7 @@ private List<String> getUniqueUrls(List<String> urls) {
156140
return uniqueUrls;
157141
}
158142

159-
private void addSiblingsIfNeeded(Map<String, List<X509Certificate>> urlsToCertificates) {
143+
private void pingSiblings(Map<String, List<X509Certificate>> urlsToCertificates) {
160144
if (!clientConfig.getResolveSiblings()) {
161145
return;
162146
}
@@ -167,24 +151,20 @@ private void addSiblingsIfNeeded(Map<String, List<X509Certificate>> urlsToCertif
167151
.setStyle(ProgressBarStyle.COLORFUL_UNICODE_BAR)
168152
.setTaskName("Resolving sibling certificates").showSpeed();
169153

170-
List<String> urls = urlsToCertificates.values().stream().parallel()
171-
.flatMap(certificates -> UriUtils.getDnsNames(certificates).stream())
154+
List<String> urls = urlsToCertificates.values().stream()
155+
.flatMap(certificates -> UriUtils.extractHostsFromSAN(certificates).stream())
172156
.distinct()
173157
.toList();
174158

175-
CertificateExtractingClient client = createClient().build();
176-
Map<String, List<X509Certificate>> siblings = ProgressBar.wrap(urls.stream(), pbb)
177-
.map(url -> {
159+
ProgressBar.wrap(urls.stream(), pbb)
160+
.parallel()
161+
.forEach(url -> {
178162
try {
179-
return Map.entry(url, client.get(url));
163+
client.call(url);
180164
} catch (Exception e) {
181-
return null;
165+
LOGGER.debug(String.format("Could not extract sibling certificate from %s", url), e);
182166
}
183-
})
184-
.filter(Objects::nonNull)
185-
.collect(Collectors.collectingAndThen(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (key1, key2) -> key1, LinkedHashMap::new), HashMap::new));
186-
187-
urlsToCertificates.putAll(siblings);
167+
});
188168
}
189169

190170
private void addSystemCertificatesIfNeeded(Map<String, List<X509Certificate>> urlsToCertificates) {
@@ -200,7 +180,7 @@ private void addSystemCertificatesIfNeeded(Map<String, List<X509Certificate>> ur
200180

201181
Map<String, List<X509Certificate>> filterCertificatesIfNeeded(Map<String, List<X509Certificate>> urlsToCertificates, CertificateType type) {
202182
return switch (type) {
203-
case ALL -> urlsToCertificates;
183+
case ALL -> new HashMap<>(urlsToCertificates);
204184
case LEAF -> filterCertificates(urlsToCertificates, certificates -> List.of(certificates.getFirst()));
205185
case ROOT -> filterCertificates(urlsToCertificates, certificates -> List.of(certificates.getLast()));
206186
case INTER -> filterCertificates(urlsToCertificates, certificates -> {

src/main/java/nl/altindag/crip/client/ftp/FtpsClientRunnable.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
public final class FtpsClientRunnable implements ClientRunnable {
3030

3131
private static final Logger LOGGER = LoggerFactory.getLogger(FtpsClientRunnable.class);
32+
private static final FtpsClientRunnable INSTANCE = new FtpsClientRunnable();
3233

3334
@Override
3435
public void run(ClientConfig clientConfig, URI uri) {
@@ -59,4 +60,8 @@ public void run(ClientConfig clientConfig, URI uri) {
5960
}
6061
}
6162

63+
public static FtpsClientRunnable getInstance() {
64+
return INSTANCE;
65+
}
66+
6267
}

src/main/java/nl/altindag/crip/client/imap/ImapClientRunnable.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
public final class ImapClientRunnable implements ClientRunnable {
2929

3030
private static final Logger LOGGER = LoggerFactory.getLogger(ImapClientRunnable.class);
31+
private static final ImapClientRunnable INSTANCE = new ImapClientRunnable();
3132

3233
@Override
3334
public void run(ClientConfig clientConfig, URI uri) {
@@ -45,4 +46,8 @@ public void run(ClientConfig clientConfig, URI uri) {
4546
}
4647
}
4748

49+
public static ImapClientRunnable getInstance() {
50+
return INSTANCE;
51+
}
52+
4853
}

src/main/java/nl/altindag/crip/client/mysql/MySQLClientRunnable.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
public final class MySQLClientRunnable implements ClientRunnable {
3636

3737
private static final Logger LOGGER = LoggerFactory.getLogger(MySQLClientRunnable.class);
38+
private static final MySQLClientRunnable INSTANCE = new MySQLClientRunnable();
3839

3940
private static final int SSL_FLAG = 0x800;
4041
private static final byte[] SSL_REQUEST = {
@@ -122,4 +123,8 @@ private static int getCapabilityFlags(int bytesRead, byte[] buffer) throws IOExc
122123
return (buffer[pos] & 0xFF) + ((buffer[pos + 1] & 0xFF) << 8);
123124
}
124125

126+
public static MySQLClientRunnable getInstance() {
127+
return INSTANCE;
128+
}
129+
125130
}

src/main/java/nl/altindag/crip/client/postgres/PostgresClientRunnable.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
public final class PostgresClientRunnable implements ClientRunnable {
3737

3838
private static final Logger LOGGER = LoggerFactory.getLogger(PostgresClientRunnable.class);
39+
private static final PostgresClientRunnable INSTANCE = new PostgresClientRunnable();
3940

4041
private static final int SSL_REQUEST_MESSAGE_LENGTH = 8;
4142
private static final int SSL_REQUEST_CODE = 80877103;
@@ -73,4 +74,8 @@ public void run(ClientConfig clientConfig, URI uri) {
7374
}
7475
}
7576

77+
public static PostgresClientRunnable getInstance() {
78+
return INSTANCE;
79+
}
80+
7681
}

src/main/java/nl/altindag/crip/client/smtp/SmtpClientRunnable.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
public final class SmtpClientRunnable implements ClientRunnable {
2929

3030
private static final Logger LOGGER = LoggerFactory.getLogger(SmtpClientRunnable.class);
31+
private static final SmtpClientRunnable INSTANCE = new SmtpClientRunnable();
3132

3233
@Override
3334
public void run(ClientConfig clientConfig, URI uri) {
@@ -60,4 +61,8 @@ public void run(ClientConfig clientConfig, URI uri) {
6061
}
6162
}
6263

64+
public static SmtpClientRunnable getInstance() {
65+
return INSTANCE;
66+
}
67+
6368
}

src/main/java/nl/altindag/crip/client/websocket/WebSocketClientRunnable.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
public final class WebSocketClientRunnable implements ClientRunnable {
3434

35+
private static final WebSocketClientRunnable INSTANCE = new WebSocketClientRunnable();
36+
3537
@Override
3638
public void run(ClientConfig clientConfig, URI uri) {
3739
HttpClient.Builder clientBuilder = HttpClient.newBuilder()
@@ -69,4 +71,8 @@ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
6971
};
7072
}
7173

74+
public static WebSocketClientRunnable getInstance() {
75+
return INSTANCE;
76+
}
77+
7278
}

src/main/java/nl/altindag/crip/command/print/PrintCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void run() {
6464
urlsToCertificates.entrySet().stream()
6565
.filter(entry -> !entry.getValue().isEmpty())
6666
.forEach(entry ->
67-
System.out.printf("Certificates for url = %s%n%n%s%n%n%n",
67+
System.out.printf("Certificates for host = %s%n%n%s%n%n%n",
6868
entry.getKey(),
6969
entry.getValue().stream()
7070
.map(X509Certificate::toString)
@@ -76,7 +76,7 @@ public void run() {
7676
.filter(entry -> !entry.getValue().isEmpty())
7777
.map(entry -> new SimpleImmutableEntry<>(entry.getKey(), CertificateUtils.convertToPem(entry.getValue())))
7878
.forEach(entry ->
79-
System.out.printf("Certificates for url = %s%n%n%s%n%n",
79+
System.out.printf("Certificates for host = %s%n%n%s%n%n",
8080
entry.getKey(),
8181
String.join(String.format(CERTIFICATE_DELIMITER, entry.getKey()), entry.getValue())));
8282
break;

src/main/java/nl/altindag/crip/util/UriUtils.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ public static int extractPort(String value) {
5454
}
5555
}
5656

57-
public static List<String> getDnsNames(List<X509Certificate> certificates) {
57+
/**
58+
* Extracts the DNS Names from the Subject Alternative Name extension of the provided certificates.
59+
* And appends "https://" prefix to each DNS name.
60+
*/
61+
public static List<String> extractHostsFromSAN(List<X509Certificate> certificates) {
5862
List<String> dnsNames = new ArrayList<>();
5963
for (X509Certificate certificate : certificates) {
6064
try {

0 commit comments

Comments
 (0)