/*
* This file is part of FTB Launcher.
*
* Copyright © 2012-2016, FTB Launcher Contributors <https://github.com/Slowpoke101/FTBLaunch/>
* FTB Launcher is licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Original code from http://nodsw.com/blog/leeland/2006/12/06-no-more-unable-find-valid-certification-path-requested-target
* Java 7 fix from http://infposs.blogspot.fi/2013/06/installcert-and-java-7.html
*
* Adapted code for FTB launcher: removed keychain modification, added more error handling and using launcher's logging system
*/
package net.ftb.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import net.ftb.log.Logger;
public class SSLUtils
{
/**
*
* @param s host name to test
* @param p port to test
* @throws Exception
*/
public static void printServerCertChain (String s, int p)
{
boolean handshake_failed = false;
KeyStore ks;
SSLSocketFactory factory;
SSLSocket socket;
SavingTrustManager tm;
// fill keychain
char[] passphrase = "changeit".toCharArray();
char SEP = File.separatorChar;
File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
File file = new File(dir, "cacerts");
try
{
InputStream in = new FileInputStream(file);
ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, passphrase);
in.close();
}
catch (Exception e)
{
Logger.logWarn("Keychain loadign failed", e);
return;
}
// prepare SSL
try
{
SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
tm = new SavingTrustManager(defaultTrustManager);
context.init(null, new TrustManager[]
{tm}, null);
factory = context.getSocketFactory();
}
catch (Exception e)
{
Logger.logWarn("SSL preparation failed", e);
return;
}
// open socket
try
{
socket = (SSLSocket)factory.createSocket(s, p);
socket.setSoTimeout(10000);
}
catch (UnknownHostException e)
{
Logger.logWarn("Host lookup failed", e);
return;
}
catch (Exception e)
{
Logger.logWarn("Generic socket fail", e);
return;
}
// and finally initiate SSL handshake
try
{
socket.startHandshake();
socket.close();
Logger.logDebug("SSL handshake was succesfull. Printing certificate chain...");
}
catch (SSLException e)
{
handshake_failed = true;
}
catch (IOException e)
{
Logger.logWarn("IOException", e);
return;
}
X509Certificate[] chain = tm.chain;
if (chain == null)
{
Logger.logDebug("Could not obtain server certificate chain");
return;
}
if (handshake_failed)
{
Logger.logError("SSL handshake failed. Something might be altering SSL certificates");
Logger.logError("Certificates are not trusted by JVM certificate chain");
Logger.logError("Certificate chain will be printed in debug logging level");
}
Logger.logDebug("");
Logger.logDebug("Server sent " + chain.length + " certificate(s):");
Logger.logDebug("");
try
{
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
MessageDigest md5 = MessageDigest.getInstance("MD5");
for(int i = 0; i < chain.length; i++)
{
X509Certificate cert = chain[i];
Logger.logDebug(" " + (i + 1) + " Subject " + cert.getSubjectDN());
Logger.logDebug(" Issuer " + cert.getIssuerDN());
sha1.update(cert.getEncoded());
Logger.logDebug(" sha1 " + toHexString(sha1.digest()));
md5.update(cert.getEncoded());
Logger.logDebug(" md5 " + toHexString(md5.digest()));
Logger.logDebug("");
}
}
catch (Exception e)
{
Logger.logDebug("Certificate printing failed", e);
}
}
private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
private static String toHexString (byte[] bytes)
{
StringBuilder sb = new StringBuilder(bytes.length * 3);
for(int b : bytes)
{
b &= 0xff;
sb.append(HEXDIGITS[b >> 4]);
sb.append(HEXDIGITS[b & 15]);
sb.append(' ');
}
return sb.toString();
}
private static class SavingTrustManager implements X509TrustManager
{
private final X509TrustManager tm;
private X509Certificate[] chain;
SavingTrustManager (final X509TrustManager tm)
{
this.tm = tm;
}
@Override
public X509Certificate[] getAcceptedIssuers ()
{
return new X509Certificate[0];
// throw new UnsupportedOperationException();
}
@Override
public void checkClientTrusted (final X509Certificate[] chain, final String authType) throws CertificateException
{
throw new UnsupportedOperationException();
}
@Override
public void checkServerTrusted (final X509Certificate[] chain, final String authType) throws CertificateException
{
this.chain = chain;
this.tm.checkServerTrusted(chain, authType);
}
}
}