Skip to content

Commit 1a8ce1c

Browse files
author
tkb16
committed
initial commit
0 parents  commit 1a8ce1c

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

java/SSLCertificateFetcher.java

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import java.io.BufferedReader;
2+
import java.io.File;
3+
import java.io.FileInputStream;
4+
import java.io.FileOutputStream;
5+
import java.io.IOException;
6+
import java.io.InputStream;
7+
import java.io.InputStreamReader;
8+
import java.io.OutputStream;
9+
import java.net.Authenticator;
10+
import java.net.HttpURLConnection;
11+
import java.net.InetSocketAddress;
12+
import java.net.PasswordAuthentication;
13+
import java.net.Proxy;
14+
import java.net.SocketAddress;
15+
import java.net.URL;
16+
import java.security.KeyStore;
17+
import java.security.KeyStoreException;
18+
import java.security.MessageDigest;
19+
import java.security.NoSuchAlgorithmException;
20+
import java.security.cert.CertificateException;
21+
import java.security.cert.X509Certificate;
22+
import java.text.SimpleDateFormat;
23+
import java.util.Date;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
import java.util.Objects;
27+
28+
import javax.naming.InvalidNameException;
29+
import javax.naming.ldap.LdapName;
30+
import javax.naming.ldap.Rdn;
31+
import javax.net.ssl.HttpsURLConnection;
32+
import javax.net.ssl.SSLContext;
33+
import javax.net.ssl.SSLException;
34+
import javax.net.ssl.TrustManager;
35+
import javax.net.ssl.TrustManagerFactory;
36+
import javax.net.ssl.X509TrustManager;
37+
38+
/**
39+
* CSSLCertificateFetcher
40+
*/
41+
public class SSLCertificateFetcher {
42+
43+
private static final String ANSI_RESET = "\u001B[0m";
44+
private static final String ANSI_BLACK = "\u001B[30m";
45+
private static final String ANSI_RED = "\u001B[31m";
46+
private static final String ANSI_GREEN = "\u001B[32m";
47+
private static final String ANSI_YELLOW = "\u001B[33m";
48+
private static final String ANSI_BLUE = "\u001B[34m";
49+
private static final String ANSI_PURPLE = "\u001B[35m";
50+
private static final String ANSI_CYAN = "\u001B[36m";
51+
private static final String ANSI_WHITE = "\u001B[37m";
52+
53+
private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
54+
private static final String PROXY_HOST = "proxyHost";
55+
private static final String PROXY_PORT = "proxyPort";
56+
private static final String PASSPHRASE = "passphrase";
57+
private static final String PROXY_USER = "proxyUser";
58+
private static final String PROXY_PASSWORD = "proxyPassword";
59+
private static final String URL = "url";
60+
private static final String TRUSTSTORE = "truststore";
61+
62+
public static void main(final String[] args) throws Exception {
63+
SSLCertificateFetcher fetcher = new SSLCertificateFetcher();
64+
try {
65+
boolean valid = fetcher.initialize(args);
66+
67+
if (valid) {
68+
fetcher.fetch();
69+
} else {
70+
readme();
71+
}
72+
} catch (Exception e) {
73+
readme();
74+
}
75+
}
76+
77+
private static void readme() {
78+
System.out.println("Usage: " + SSLCertificateFetcher.class.getName() + " " + URL + "=[url*] " + TRUSTSTORE + "=[truststore*] " + PASSPHRASE + "=[passphrase] " + PROXY_HOST
79+
+ "=[proxyHost] " + PROXY_PORT + "=[proxyPort] " + PROXY_USER + "=[proxyUser] " + PROXY_PASSWORD + "=[proxyPassword]\n"
80+
+ "passphrase, proxy details are not mandatory. Can be used default " + "in case when they are " + "not specified");
81+
}
82+
83+
private String proxyHost = null;
84+
private Integer proxyPort = null;
85+
private String remoteUrl;
86+
private char[] passphrase;
87+
private Proxy proxy = null;
88+
private String truststore;
89+
90+
private boolean initialize(String... args) {
91+
Map<String, String> paramsMap = new HashMap<>();
92+
// parsing all passed parameters and storing them in the map
93+
for (String arg : args) {
94+
paramsMap.put(arg.split("=")[0].replaceAll(" ", ""), arg.split("=")[1].replaceAll(" ", ""));
95+
}
96+
// Writing out all parameters that were passed to java
97+
paramsMap.forEach((k, v) -> System.out.println(k + " = " + (k.toLowerCase().contains("pass") ? "*******" : v)));
98+
99+
// check if proxy parameters were passed in. If yes, then enable Proxy
100+
if (paramsMap.containsKey(PROXY_HOST)) {
101+
proxyHost = paramsMap.get(PROXY_HOST);
102+
}
103+
if (paramsMap.containsKey(PROXY_PORT)) {
104+
proxyPort = Integer.parseInt(paramsMap.get(PROXY_PORT));
105+
}
106+
if (proxyHost != null && proxyPort != null) {
107+
SocketAddress addr = new InetSocketAddress(proxyHost, proxyPort);
108+
proxy = new Proxy(Proxy.Type.HTTP, addr);
109+
} else {
110+
System.out.println("Connecting to address without enabled proxy settings. ");
111+
}
112+
113+
// now checking if host,port and passphrase parameters were passed succesfully
114+
if (paramsMap.containsKey(URL)) {
115+
remoteUrl = paramsMap.get(URL);
116+
} else {
117+
return false;
118+
}
119+
120+
final String p = paramsMap.getOrDefault(PASSPHRASE, "changeit");
121+
passphrase = p.toCharArray();
122+
123+
if (paramsMap.containsKey(PROXY_USER) && paramsMap.containsKey(PROXY_PASSWORD)) {
124+
Authenticator.setDefault(new Authenticator() {
125+
126+
@Override
127+
public PasswordAuthentication getPasswordAuthentication() {
128+
return new PasswordAuthentication(paramsMap.get(PROXY_USER), paramsMap.get(PROXY_PASSWORD).toCharArray());
129+
}
130+
});
131+
}
132+
133+
truststore = paramsMap.get(TRUSTSTORE);
134+
if (truststore == null) {
135+
System.out.println("'" + TRUSTSTORE + "' must be defined.");
136+
return false;
137+
}
138+
return true;
139+
}
140+
141+
private void fetch() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, InvalidNameException {
142+
143+
File file = new File(truststore);
144+
145+
final KeyStore ks = getKeyStore(passphrase, file);
146+
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
147+
tmf.init(ks);
148+
149+
// Create a new trust manager that trust all certificates
150+
final X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
151+
final SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
152+
TrustManager[] trustAllCerts = new TrustManager[]{tm};
153+
154+
// Activate the new trust manager
155+
try {
156+
SSLContext sc = SSLContext.getInstance("TLS");
157+
sc.init(null, trustAllCerts, new java.security.SecureRandom());
158+
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
159+
} catch (Exception e) {
160+
e.printStackTrace();
161+
return;
162+
}
163+
164+
URL url = new URL(remoteUrl);
165+
final HttpURLConnection conn;
166+
if (proxy != null) {
167+
conn = (HttpURLConnection) url.openConnection(proxy);
168+
} else {
169+
conn = (HttpURLConnection) url.openConnection();
170+
}
171+
conn.setReadTimeout(10_000);
172+
System.out.println("Starting SSL handshake...");
173+
174+
String defaultSelection;
175+
176+
try {
177+
conn.connect();
178+
System.out.println();
179+
printSuccess("Connection could successfully be established, certificate is already trusted");
180+
defaultSelection = "q";
181+
} catch (SSLException e) {
182+
e.printStackTrace(System.out);
183+
System.out.println();
184+
printError("Error, certificate is NOT trusted");
185+
defaultSelection = "1";
186+
}
187+
188+
final X509Certificate[] chain = tm.chain;
189+
if (chain == null) {
190+
printError("Could not obtain server certificate chain");
191+
return;
192+
}
193+
194+
final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
195+
196+
System.out.println();
197+
System.out.println("Server sent " + chain.length + " certificate(s):");
198+
System.out.println();
199+
final MessageDigest sha1 = MessageDigest.getInstance("SHA1");
200+
final MessageDigest md5 = MessageDigest.getInstance("MD5");
201+
for (int i = 0; i < chain.length; i++) {
202+
final X509Certificate cert = chain[i];
203+
System.out.println(" " + (i + 1) + " Subject " + cert.getSubjectDN());
204+
System.out.println(" Issuer " + cert.getIssuerDN());
205+
sha1.update(cert.getEncoded());
206+
System.out.println(" sha1 " + toHexString(sha1.digest()));
207+
md5.update(cert.getEncoded());
208+
System.out.println(" md5 " + toHexString(md5.digest()));
209+
System.out.println();
210+
}
211+
212+
System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [" + defaultSelection + "]");
213+
final String line = Objects.toString(reader.readLine(), "").trim();
214+
int k;
215+
try {
216+
String option = (line.length() == 0) ? defaultSelection : line;
217+
k = Integer.parseInt(option) - 1;
218+
} catch (final NumberFormatException e) {
219+
System.out.println("KeyStore not changed");
220+
return;
221+
}
222+
223+
final X509Certificate cert = chain[k];
224+
225+
final String alias = getTruststoreAlias(url, k, cert);
226+
227+
String backup = null;
228+
229+
if (ks.containsAlias(alias)) {
230+
printSuccess("Certificate is already in the keystore.");
231+
} else {
232+
if (ks.size() > 0) {
233+
backup = file.getAbsolutePath() + "." + new SimpleDateFormat("yyyyMMdd").format(new Date());
234+
try (OutputStream out = new FileOutputStream(backup)) {
235+
ks.store(out, passphrase);
236+
}
237+
}
238+
239+
ks.setCertificateEntry(alias, cert);
240+
try (OutputStream out = new FileOutputStream(file)) {
241+
ks.store(out, passphrase);
242+
}
243+
244+
System.out.println();
245+
System.out.println(cert);
246+
System.out.println();
247+
if (backup != null) {
248+
System.out.println("Created backup of keystore " + backup);
249+
}
250+
printSuccess("Added certificate to keystore '" + file.getAbsolutePath() + "' using alias '" + alias + "'");
251+
}
252+
253+
}
254+
255+
private KeyStore getKeyStore(char[] passphrase, File file) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
256+
final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
257+
if (file.isFile() && file.exists()) {
258+
System.out.println("Loading KeyStore " + file + "...");
259+
try (InputStream in = new FileInputStream(file)) {
260+
ks.load(in, passphrase);
261+
}
262+
} else {
263+
ks.load(null, passphrase);
264+
System.out.println("Creating empty truststore " + file + "...");
265+
}
266+
return ks;
267+
}
268+
269+
private String getTruststoreAlias(URL url, int k, X509Certificate cert) throws InvalidNameException {
270+
String cn = getCN(cert);
271+
String alias = url.getHost();
272+
if (cn != null) {
273+
alias += " (" + cn + ")";
274+
}
275+
alias += " (" + SSLCertificateFetcher.class.getSimpleName() + " chain index: " + (k + 1) + ")";
276+
return alias;
277+
}
278+
279+
private String getCN(X509Certificate cert) throws InvalidNameException {
280+
String dn = cert.getSubjectX500Principal().getName();
281+
LdapName ldapDN = new LdapName(dn);
282+
for (Rdn rdn : ldapDN.getRdns()) {
283+
if ("CN".equals(rdn.getType())) {
284+
return Objects.toString(rdn.getValue(), null);
285+
}
286+
}
287+
return null;
288+
}
289+
290+
private String toHexString(final byte[] bytes) {
291+
final StringBuilder sb = new StringBuilder(bytes.length * 3);
292+
293+
for (byte aByte : bytes) {
294+
int b = aByte & 0xff;
295+
sb.append(HEXDIGITS[b >> 4]);
296+
sb.append(HEXDIGITS[b & 15]);
297+
sb.append(' ');
298+
}
299+
return sb.toString();
300+
}
301+
302+
private void printError(String message) {
303+
System.out.println(ANSI_RED + message + ANSI_RESET);
304+
}
305+
306+
private void printSuccess(String message) {
307+
System.out.println(ANSI_GREEN + message + ANSI_RESET);
308+
}
309+
310+
private static final class SavingTrustManager implements X509TrustManager {
311+
312+
private final X509TrustManager tm;
313+
private X509Certificate[] chain;
314+
315+
SavingTrustManager(final X509TrustManager tm) {
316+
this.tm = tm;
317+
}
318+
319+
@Override
320+
public X509Certificate[] getAcceptedIssuers() {
321+
return new X509Certificate[0];
322+
}
323+
324+
@Override
325+
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
326+
throw new UnsupportedOperationException();
327+
}
328+
329+
@Override
330+
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
331+
this.chain = chain;
332+
this.tm.checkServerTrusted(chain, authType);
333+
}
334+
}
335+
}

0 commit comments

Comments
 (0)