/** * Copyright 2010, Konstantinos Koutsopoulos (k.koutsopoulos@yahoo.gr), Nikos Mouratidis (nmouratid@teemail.gr) * * 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. */ package org.panlab.tgw.restclient; import org.panlab.tgw.App; import org.panlab.tgw.util.XMLElement; import org.panlab.tgw.util.XMLUtil; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.URL; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.Enumeration; import java.util.Hashtable; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import javax.security.cert.X509Certificate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.netbeans.xml.schema.repo.PtmDocument.Ptm; /** * * @author kkoutso */ public class PtmInfoParser { private static Log log = LogFactory.getLog(PtmInfoParser.class); private static String ptmToCheck=null; private static byte[] getClientHello() { long time = System.currentTimeMillis(); time /= 1000; //System.out.println(time + " "); int index = 0; String[] suite = ((SSLSocketFactory) SSLSocketFactory.getDefault()).getDefaultCipherSuites(); for (int j = 0; j < suite.length; j++) { if (suites.containsKey(suite[j])) { index++; //System.out.println(suite[j]); } } int tempIndex = 0; byte[] suiteBytes = new byte[index * 3]; for (int j = 0; j < suite.length; j++) { //log.info(j+": "); if (suites.containsKey(suite[j])) { //log.info(suite[j]); suiteBytes[3 * (index - tempIndex) - 3] = suites.get(suite[j])[0]; suiteBytes[3 * (index - tempIndex) - 2] = suites.get(suite[j])[1]; suiteBytes[3 * (index - tempIndex) - 1] = suites.get(suite[j])[2]; tempIndex++; } } byte[] clientHello = { (byte) 0x80, (byte) (41 + suiteBytes.length), //Length 0x01, //Message Type: Client Hello 0x03, 0x01, //Version TLSv1.0 (byte) (suiteBytes.length / 256), (byte) (suiteBytes.length % 256), //Cipher Spec Length 0x00, 0x00, //Session ID Length 0x00, 0x20, //Challenge Length }; byte[] clientRandom = { (byte) (time / (256 * 256 * 256)), (byte) ((time % (256 * 256 * 256)) / (256 * 256)), (byte) (((time % (256 * 256 * 256)) % (256 * 256)) / 256), (byte) (time % 256), 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x0f }; ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(clientHello, 0, clientHello.length); baos.write(suiteBytes, 0, suiteBytes.length); baos.write(clientRandom, 0, clientRandom.length); return baos.toByteArray(); } private static Hashtable<String, byte[]> suites = new Hashtable<String, byte[]>(); static { suites.put("TLS_RSA_WITH_NULL_MD5", new byte[] { 0x00, 0x00, 0x01 }); suites.put("TLS_RSA_WITH_NULL_SHA", new byte[] { 0x00, 0x00, 0x02 }); suites.put("TLS_RSA_WITH_NULL_SHA256", new byte[] { 0x00, 0x00, 0x3B }); suites.put("TLS_RSA_WITH_RC4_128_MD5", new byte[] { 0x00, 0x00, 0x04 }); suites.put("TLS_RSA_WITH_RC4_128_SHA", new byte[] { 0x00, 0x00, 0x05 }); suites.put("TLS_RSA_WITH_3DES_EDE_CBC_SHA", new byte[] { 0x00, 0x00, 0x0A }); suites.put("TLS_RSA_WITH_AES_128_CBC_SHA", new byte[] { 0x00, 0x00, 0x2F }); suites.put("TLS_RSA_WITH_AES_256_CBC_SHA", new byte[] { 0x00, 0x00, 0x35 }); suites.put("TLS_RSA_WITH_AES_128_CBC_SHA256", new byte[] { 0x00, 0x00, 0x3C }); suites.put("TLS_RSA_WITH_AES_256_CBC_SHA256", new byte[] { 0x00, 0x00, 0x3D }); suites.put("TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", new byte[] { 0x00, 0x00, 0x0D }); suites.put("TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", new byte[] { 0x00, 0x00, 0x10 }); suites.put("TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", new byte[] { 0x00, 0x00, 0x13 }); suites.put("TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", new byte[] { 0x00, 0x00, 0x16 }); suites.put("TLS_DH_DSS_WITH_AES_128_CBC_SHA", new byte[] { 0x00, 0x00, 0x30 }); suites.put("TLS_DH_RSA_WITH_AES_128_CBC_SHA", new byte[] { 0x00, 0x00, 0x31 }); suites.put("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", new byte[] { 0x00, 0x00, 0x32 }); suites.put("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", new byte[] { 0x00, 0x00, 0x33 }); suites.put("TLS_DH_DSS_WITH_AES_256_CBC_SHA", new byte[] { 0x00, 0x00, 0x36 }); suites.put("TLS_DH_RSA_WITH_AES_256_CBC_SHA", new byte[] { 0x00, 0x00, 0x37 }); suites.put("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", new byte[] { 0x00, 0x00, 0x38 }); suites.put("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", new byte[] { 0x00, 0x00, 0x39 }); suites.put("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", new byte[] { 0x00, 0x00, 0x3E }); suites.put("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", new byte[] { 0x00, 0x00, 0x3F }); suites.put("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", new byte[] { 0x00, 0x00, 0x40 }); suites.put("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", new byte[] { 0x00, 0x00, 0x67 }); suites.put("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", new byte[] { 0x00, 0x00, 0x68 }); suites.put("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", new byte[] { 0x00, 0x00, 0x69 }); suites.put("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", new byte[] { 0x00, 0x00, 0x6A }); suites.put("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", new byte[] { 0x00, 0x00, 0x6B }); suites.put("TLS_DH_anon_WITH_RC4_128_MD5", new byte[] { 0x00, 0x00, 0x18 }); suites.put("TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", new byte[] { 0x00, 0x00, 0x1B }); suites.put("TLS_DH_anon_WITH_AES_128_CBC_SHA", new byte[] { 0x00, 0x00, 0x34 }); suites.put("TLS_DH_anon_WITH_AES_256_CBC_SHA", new byte[] { 0x00, 0x00, 0x3A }); suites.put("TLS_DH_anon_WITH_AES_128_CBC_SHA256", new byte[] { 0x00, 0x00, 0x6C }); suites.put("TLS_DH_anon_WITH_AES_256_CBC_SHA256", new byte[] { 0x00, 0x00, 0x6D }); } private static void getPTMCertificate(String alias, URL url) { if(App.ptm_indexes.containsKey(alias)) return; System.out.println("Trying "+alias); try { Socket socket = new Socket(url.getHost(), url.getPort()); OutputStream os = socket.getOutputStream(); os.write(getClientHello()); os.flush(); os.flush(); InputStream is = socket.getInputStream(); byte[] buffer = new byte[2000]; int length = 0; int tries = 0; while (true) { length += is.read(buffer, length, buffer.length - length); if (length > 0 && tries == 0) { tries++; if (buffer[0] == 0x16) { log.info("TLSv1 Handshake"); log.info("Version: " + getText(buffer, 1, 2)); log.info("Length: " + getInt(buffer, 3, 2)); byte[] tmp = new byte[getInt(buffer, 3, 2) + 5]; System.arraycopy(buffer, 0, tmp, 0, length); buffer = tmp; } } //log.info("Length: "+length); if (buffer.length == length) { break; } } //log.info("Length: "+length); int index = 0; if ((index = locateServerCertificate(buffer)) > 0) { log.info("Certificates Section Located at: " + index); length = getInt(buffer, index + 1, 3); log.info("Certificates length: " + length); length = getInt(buffer, index + 7, 3); log.info("First Certificate length: " + length); //log.info(getText(buffer, index+10, length)); byte[] cert = new byte[length]; System.arraycopy(buffer, index + 10, cert, 0, length); X509Certificate x509 = X509Certificate.getInstance(cert); log.info(x509.getSubjectDN().toString().replace(", ", ",")); processCertificate(alias, x509, url); } } catch (Exception error) { log.error(error.getMessage()); } } public static void refreshPTMs() { try { Ptm ptm[] = RepoAdapter.getPtms(); for (int i = 0; i < ptm.length; i++) { { if(ptmToCheck==null || ptmToCheck.equalsIgnoreCase(ptm[i].getCommonName())) { log.info(ptm[i].getCommonName() + " " + ptm[i].getUrl()); URL url = new URL(ptm[i].getUrl()); getPTMCertificate(ptm[i].getCommonName(), url); } } } } catch (Exception error) { error.printStackTrace(); } } public static void main(String args[]) { try { if(args!=null && args.length > 0) ptmToCheck = args[0]; refreshPTMs(); } catch (Exception error) { log.error(error.getMessage()); } } private static String getText(byte[] array, int start, int length) { int i; String str = "0x", tmp; for (i = start; i < start + length; i++) { tmp = Integer.toHexString(array[i]); str += tmp.length() == 2 ? tmp : (tmp.length() == 1 ? "0" + tmp : tmp.substring(6)); } return str; } private static int getInt(byte[] buffer, int start, int length) { int result = 0; int tmp; for (int j = start; j < start + length; j++) { if (buffer[j] < 0) { tmp = 256 + buffer[j]; } else { tmp = buffer[j]; } result += (int) (tmp * Math.pow(256, start + length - 1 - j)); } return result; } private static int locateServerCertificate(byte[] buffer) { int index = 5; while (buffer[index] != 0x0b) { index += 4 + getInt(buffer, index + 1, 3); if (index > buffer.length) { return -1; } log.info("" + index); } return index; } private static void processCertificate(String alias, X509Certificate x509, URL url) { try { String store = System.getProperty("javax.net.ssl.trustStore"); String password = System.getProperty("javax.net.ssl.trustStorePassword"); KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(new FileInputStream(store), password.toCharArray()); Enumeration<String> en = keystore.aliases(); while (en.hasMoreElements()) { log.info(en.nextElement()); } if (!keystore.containsAlias(alias)) { ByteArrayInputStream bais = new ByteArrayInputStream(x509.getEncoded()); Certificate cert = CertificateFactory.getInstance("x509").generateCertificate(bais); keystore.setCertificateEntry(alias, cert); storeNewPTM(alias, url, x509.getSubjectDN().toString().replace(", ",",")); en = keystore.aliases(); while (en.hasMoreElements()) { log.info(en.nextElement()); } keystore.store(new FileOutputStream(store), password.toCharArray()); TrustManagerFactory.getInstance("PKIX").init(keystore); } } catch (Exception error) { log.error(error.getMessage()); } } private static void storeNewPTM(String alias, URL url, String dn) { try { File file = new File("conf.xml"); String content; if(file.exists()) { FileInputStream fis = new FileInputStream(file); int length = fis.available(); byte bArray[] = new byte[length]; fis.read(bArray); fis.close(); content = new String(bArray); } else content = "<ptms></ptms>"; Object objs[] = XMLUtil.getElements(content); FileOutputStream fos = new FileOutputStream("conf.xml"); fos.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".getBytes()); fos.write("<ptms>\n".getBytes()); if (objs != null) { for (int i = 0; i < objs.length; i++) { XMLElement element = (XMLElement) (objs[i]); fos.write((element.toString()+"\n").getBytes()); } } XMLElement elem = new XMLElement(alias, "cert", "\""+dn+"\"", url.toString()); fos.write((elem.toString()+"\n").getBytes()); fos.write("</ptms>\n".getBytes()); fos.close(); } catch (Exception error) { log.error(error.getMessage()); } } }