package org.openstack.atlas.util.ca.util; import java.net.ProtocolException; import javax.net.ssl.SSLPeerUnverifiedException; import org.openstack.atlas.util.ca.util.fileio.RsaFileUtils; import java.io.InputStream; import org.openstack.atlas.util.ca.exceptions.X509ReaderException; import java.net.HttpURLConnection; import java.net.URI; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateParsingException; import java.util.logging.Level; import javax.net.ssl.SSLContext; import org.openstack.atlas.util.ca.util.sslborker.TrustAllHostsVerifier; import java.io.IOException; import java.security.cert.Certificate; import javax.net.ssl.HttpsURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Collection; import java.util.logging.Logger; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bouncycastle.jce.provider.X509CertificateObject; import org.openstack.atlas.util.ca.PemUtils; import org.openstack.atlas.util.ca.StringUtils; import org.openstack.atlas.util.ca.exceptions.NotAnX509CertificateException; import org.openstack.atlas.util.ca.exceptions.PemException; import org.openstack.atlas.util.ca.primitives.Debug; import org.openstack.atlas.util.ca.primitives.PemBlock; import org.openstack.atlas.util.ca.primitives.RsaConst; import org.openstack.atlas.util.ca.util.sslborker.OverTrustingSSLContext; import static org.openstack.atlas.util.ca.StringUtils.asciiString; public class X509ReaderWriter { private static final Logger LOG = Logger.getLogger(X509ReaderWriter.class.getName()); private static int nopCount = 0; private static final int PAGESIZE = 4096; private static final String BEG_PRV; private static final String END_PRV; private static final String BEG_CSR; private static final String END_CSR; private static final String BEG_CRT; private static final String END_CRT; static { RsaConst.init(); BEG_PRV = asciiString(PemUtils.BEG_PRV); BEG_CSR = asciiString(PemUtils.BEG_CSR); BEG_CRT = asciiString(PemUtils.BEG_CRT); END_PRV = asciiString(PemUtils.END_PRV); END_CSR = asciiString(PemUtils.END_CSR); END_CRT = asciiString(PemUtils.END_CRT); } public static ResponseWithExcpetions<String> writeSet(Collection<X509CertificateObject> x509objs) { StringBuilder sb = new StringBuilder(PAGESIZE * 4); List<Exception> exceptions = new ArrayList<Exception>(); for (X509CertificateObject x509obj : x509objs) { try { String pem = PemUtils.toPemString(x509obj); sb.append(pem); } catch (PemException ex) { String fmt = "Object with hashcode %d " + "could not be serialized to X509Certificate Pem"; String msg = String.format(fmt, x509obj.hashCode()); exceptions.add(buildNotX509ObjectException(msg, null, null, ex)); } } return new ResponseWithExcpetions<String>(exceptions, sb.toString()); } public static ResponseWithExcpetions<Set<X509CertificateObject>> readSet(String pemString) { ResponseWithExcpetions<Set<X509CertificateObject>> resp; List<Exception> exceptions = new ArrayList<Exception>(); byte[] pemBytes = StringUtils.asciiBytes(pemString); Set<X509CertificateObject> x509objs = new HashSet<X509CertificateObject>(); List<PemBlock> blocks = PemUtils.parseMultiPem(pemBytes); for (PemBlock block : blocks) { if (!StringUtils.strEquals(block.getStartLine(), BEG_CRT)) { continue; } if (block.getDecodedObject() == null) { exceptions.add(buildNotX509ObjectException(null, block.getLineNum(), asciiString(block.getPemData()), null)); continue; } if (!(block.getDecodedObject() instanceof X509CertificateObject)) { exceptions.add(buildNotX509ObjectException(null, block.getLineNum(), asciiString(block.getPemData()), null)); continue; } X509CertificateObject x509obj = (X509CertificateObject) block.getDecodedObject(); x509objs.add(x509obj); } return new ResponseWithExcpetions<Set<X509CertificateObject>>(exceptions, x509objs); } private static NotAnX509CertificateException buildNotX509ObjectException(String msg, Integer lineNum, String pemString, Throwable th) { NotAnX509CertificateException ex; StringBuilder sb = new StringBuilder(); if (msg != null) { sb.append(msg); } else { if (lineNum == null) { sb.append("Object was not an X509Certificate"); } else { sb.append(String.format("Object at line %d was not an X509Certificate", lineNum.intValue())); } if (pemString != null) { sb.append(String.format("\n%s", pemString)); } } if (th != null) { ex = new NotAnX509CertificateException(sb.toString(), th); } else { ex = new NotAnX509CertificateException(sb.toString()); } return ex; } public static Collection<X509Certificate> nonPemUtilRead(String pem) throws CertificateException, NoSuchProviderException, UnsupportedEncodingException { Collection x509s; CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); ByteArrayInputStream bais = new ByteArrayInputStream(pem.getBytes("US-ASCII")); x509s = cf.generateCertificates(bais); return x509s; } public static List<X509CertificateObject> getX509CertificateObjectsFromSSLServer(String uriStr) throws X509ReaderException { URI uri; String fmt; String msg; try { uri = new URI(uriStr); } catch (URISyntaxException ex) { fmt = "Caught exception trying to convert %s to uri"; msg = String.format(fmt, uriStr); throw new X509ReaderException(msg, ex); } return getX509CertificateObjectsFromSSLServer(uri); } public static List<X509CertificateObject> getX509CertificateObjectsFromSSLServer(URI uri) throws X509ReaderException { int i; String fmt; String msg; List<X509CertificateObject> x509certObjs = new ArrayList<X509CertificateObject>(); URL url; try { url = uri.toURL(); } catch (MalformedURLException ex) { fmt = "Caught Exception trying to convert %s to url:%s"; msg = String.format(fmt, uri.toString(), StringUtils.getEST(ex)); throw new X509ReaderException(msg, ex); } HttpsURLConnection con; try { con = (HttpsURLConnection) url.openConnection(); } catch (IOException ex) { fmt = "Got IOException opening url to %s:%s"; msg = String.format(fmt, url.toString(), StringUtils.getEST(ex)); logEx(msg, ex); throw new X509ReaderException(msg, ex); } SSLContext sc; Certificate[] crts; try { sc = OverTrustingSSLContext.newOverTrustingSSLContext("SSL"); con.setSSLSocketFactory(sc.getSocketFactory()); con.setHostnameVerifier(new TrustAllHostsVerifier()); } catch (NoSuchAlgorithmException ex) { logAndThrowSSLSetupExeption(uri, ex); } catch (KeyManagementException ex) { logAndThrowSSLSetupExeption(uri, ex); } try { con.connect(); } catch (IOException ex) { fmt = "Exception caught when trying to connect to ssl Server: %s"; msg = String.format(fmt, uri.toString(), StringUtils.getEST(ex)); logEx(msg, ex); throw new X509ReaderException(msg, ex); } try { crts = con.getServerCertificates(); } catch (SSLPeerUnverifiedException ex) { fmt = "Exception caught when trying to retrieve Peer certs from ssl Server: %s"; msg = String.format(fmt, uri.toString(), StringUtils.getEST(ex)); logEx(msg, ex); throw new X509ReaderException(msg, ex); } for (Certificate crt : crts) { String className = crt.getClass().getName(); if (crt instanceof X509Certificate) { X509Certificate x509 = (X509Certificate) crt; String exMsg; try { X509Inspector xi = X509Inspector.newX509Inspector(x509); X509CertificateObject x509obj = xi.getX509CertificateObject(); x509certObjs.add(x509obj); } catch (CertificateEncodingException ex) { logEx(ex); continue; } catch (CertificateParsingException ex) { logEx(ex); continue; } catch (NotAnX509CertificateException ex) { logEx(ex); continue; } } } con.disconnect(); return x509certObjs; } private static X509ReaderException logAndThrowSSLSetupExeption(URI uri, Exception ex) { String fmt = "Exception caught setting up over trusting SSL socket factory for %s:%s"; String msg = String.format(uri.toString(), StringUtils.getEST(ex)); logEx(msg, ex); X509ReaderException newEx = new X509ReaderException(msg, ex); return newEx; } public static List<X509CertificateObject> getCaX509CertificateObjectFromUriString(String uriStr) throws X509ReaderException { String fmt; String msg; URI uri; try { uri = new URI(uriStr); } catch (URISyntaxException ex) { fmt = "Caught exception trying to convert %s to uri"; msg = String.format(fmt, uriStr); throw new X509ReaderException(msg, ex); } return getCaX509CertificateObjectFromUri(uri); } public static List<X509CertificateObject> getCaX509CertificateObjectFromUri(URI uri) throws X509ReaderException { String fmt; String msg; URL url; try { url = uri.toURL(); } catch (MalformedURLException ex) { fmt = "Caught Exception trying to convert %s to url:%s"; msg = String.format(fmt, uri.toString(), StringUtils.getEST(ex)); throw new X509ReaderException(msg, ex); } String uriScheme = uri.getScheme(); HttpURLConnection con; if (uriScheme.equals("http")) { try { con = (HttpURLConnection) url.openConnection(); } catch (IOException ex) { fmt = "Got IOException opening url to %s:%s"; msg = String.format(fmt, url.toString(), StringUtils.getEST(ex)); logEx(msg, ex); throw new X509ReaderException(msg, ex); } } else if (uriScheme.equals("https")) { try { con = (HttpsURLConnection) url.openConnection(); } catch (IOException ex) { fmt = "Got IOException opening url to %s:%s"; msg = String.format(fmt, url.toString(), StringUtils.getEST(ex)); logEx(msg, ex); throw new X509ReaderException(msg, ex); } } else { fmt = "Unknown schema %s from url %s"; msg = String.format(fmt, url.toString()); LOG.severe(msg); throw new X509ReaderException(msg); } con.setRequestProperty("accept", "application/x-x509-ca-cert"); try { con.setRequestMethod("GET"); } catch (ProtocolException ex) { fmt = "Error GET method protocal not recognized when setting up connection to %s: %s "; msg = String.format(fmt, url.toString(), StringUtils.getEST(ex)); logEx(msg, ex); throw new X509ReaderException(msg); } con.setDoOutput(false); con.setDoInput(true); InputStream is; byte[] pemBytes; String pemStr; try { is = (InputStream) con.getContent(); pemBytes = RsaFileUtils.readInputStream(is); pemStr = new String(pemBytes); } catch (IOException ex) { fmt = "Got exception while reading http connection from %s:%s"; msg = String.format(fmt, url.toString(), StringUtils.getEST(ex)); throw new X509ReaderException(msg); } List<X509CertificateObject> x509objs; x509objs = decodeAsPem(pemBytes); if (x509objs.size() > 0) { return x509objs; } try { x509objs = decodeAsDer(pemBytes); if(x509objs.size() < 1){ throw new X509ReaderException("Error decoded 0 objects from DER encoded response"); } return x509objs; } catch (X509ReaderException ex) { throw new X509ReaderException("Could not decode X509 certificate", ex); } } private static List<X509CertificateObject> decodeAsDer(byte[] pemBytes) throws X509ReaderException { InputStream is; is = new ByteArrayInputStream(pemBytes); CertificateFactory cf; List<X509CertificateObject> x509objs = new ArrayList<X509CertificateObject>(); try { cf = CertificateFactory.getInstance("X.509", "BC"); } catch (CertificateException ex) { throw new X509ReaderException("Could not initialize CertificateFactory CertificateException", ex); } catch (NoSuchProviderException ex) { throw new X509ReaderException("Could not initialize CertificateFactory Could not find Bouncycastle provider", ex); } Object obj; while (true) { try { obj = cf.generateCertificate(is); } catch (CertificateException ex) { throw new X509ReaderException("Certificate Error trying decode X509CertificateObject as DER"); } if (obj == null) { break; } if (!(obj instanceof X509CertificateObject)) { continue; } x509objs.add((X509CertificateObject)obj); } return x509objs; } private static List<X509CertificateObject> decodeAsPem(byte[] pemBytes) { String msg; List<X509CertificateObject> x509ObjList = new ArrayList<X509CertificateObject>(); List<PemBlock> blocks = PemUtils.parseMultiPem(pemBytes); for (PemBlock block : blocks) { if (block.getDecodedObject() == null) { continue; } if (!(block.getDecodedObject() instanceof X509CertificateObject)) { continue; } x509ObjList.add((X509CertificateObject) block.getDecodedObject()); } return x509ObjList; } private static void logEx(Throwable th) { String exMsg = StringUtils.getEST(th); String msg = "Exception Causght: " + StringUtils.getEST(th); LOG.severe(msg); } private static void logEx(String msg, Throwable th) { String exMsg = String.format("%s:%s", msg, StringUtils.getEST(th)); LOG.severe(msg); } public static int nop() { X509CertificateObject blah; return nopCount++; } }