diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java index d17b8bb78..47fad88a1 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java @@ -323,7 +323,7 @@ public void verify(IOSession ioSession, SSLSession session) throws SSLException if (cvConfig != null) { try { - ocspCrl.verifyRevocationStatus(session.getPeerCertificateChain(), + ocspCrl.verifyRevocationStatus(session.getPeerCertificates(), cvConfig.getCacheSize(), cvConfig.getCacheDuration()); } catch (CertificateVerificationException e) { throw new SSLException("Certificate chain validation failed for host : " + diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java index e042e8c79..e8208cdab 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java @@ -326,7 +326,7 @@ public void verify(IOSession ioSession, SSLSession session) throws SSLException if (cvConfig != null) { //Do revocation verification of Certificates try { - ocspCrl.verifyRevocationStatus(session.getPeerCertificateChain(), + ocspCrl.verifyRevocationStatus(session.getPeerCertificates(), cvConfig.getCacheSize(), cvConfig.getCacheDuration()); } catch (CertificateVerificationException e) { throw new SSLException("Certificate chain validation failed for host : " + diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/RevocationVerificationManager.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/RevocationVerificationManager.java index 76043b280..907310cf8 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/RevocationVerificationManager.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/RevocationVerificationManager.java @@ -27,7 +27,7 @@ import org.apache.synapse.transport.utils.sslcert.ocsp.OCSPVerifier; import org.apache.synapse.transport.utils.sslcert.pathvalidation.CertificatePathValidator; -import java.io.ByteArrayInputStream; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; /** @@ -56,11 +56,11 @@ public RevocationVerificationManager(Integer cacheAllocatedSize, Integer cacheDu * This method first tries to verify the given certificate chain using OCSP since OCSP * verification is faster. If that fails it tries to do the verification using CRL. * - * @param peerCertificates javax.security.cert.X509Certificate[] array of peer + * @param peerCertificates java.security.cert.Certificate[] array of peer * certificate chain from peer/client. * @throws CertificateVerificationException */ - public void verifyRevocationStatus(javax.security.cert.X509Certificate[] peerCertificates) + public void verifyRevocationStatus(Certificate[] peerCertificates) throws CertificateVerificationException { X509Certificate[] convertedCertificates = convert(peerCertificates); @@ -93,31 +93,20 @@ public void verifyRevocationStatus(javax.security.cert.X509Certificate[] peerCer } /** - * @param certs array of javax.security.cert.X509Certificate[] s. - * @return the converted array of java.security.cert.X509Certificate[] s. - * @throws CertificateVerificationException + * @param certs array of java.security.cert.Certificate[] s. + * @return the same chain narrowed to java.security.cert.X509Certificate[] s. + * @throws CertificateVerificationException if the chain holds a certificate that is not X.509. */ - private X509Certificate[] convert(javax.security.cert.X509Certificate[] certs) + private X509Certificate[] convert(Certificate[] certs) throws CertificateVerificationException { X509Certificate[] certChain = new X509Certificate[certs.length]; - Throwable exceptionThrown; for (int i = 0; i < certs.length; i++) { - try { - byte[] encoded = certs[i].getEncoded(); - ByteArrayInputStream bis = new ByteArrayInputStream(encoded); - java.security.cert.CertificateFactory cf - = java.security.cert.CertificateFactory.getInstance("X.509"); - certChain[i]=((X509Certificate)cf.generateCertificate(bis)); - continue; - } catch (java.security.cert.CertificateEncodingException e) { - exceptionThrown = e; - } catch (javax.security.cert.CertificateEncodingException e) { - exceptionThrown = e; - } catch (java.security.cert.CertificateException e) { - exceptionThrown = e; + if (!(certs[i] instanceof X509Certificate)) { + throw new CertificateVerificationException("Certificate at index " + i + + " is of type " + certs[i].getType() + "; revocation verification " + + "requires X.509 certificates"); } - throw new CertificateVerificationException("Cant Convert certificates from " + - "javax to java", exceptionThrown); + certChain[i] = (X509Certificate) certs[i]; } return certChain; } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/ParentLastClassLoader.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/ParentLastClassLoader.java index ecdac713d..79ae2d9d3 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/ParentLastClassLoader.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/ParentLastClassLoader.java @@ -26,6 +26,7 @@ import java.util.Hashtable; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.regex.Pattern; /** * The ParentLastClassLoader is needed because the OCSP/CRL feature for synapse uses a dependency @@ -43,8 +44,18 @@ public class ParentLastClassLoader extends ClassLoader { //used to cache already defined classes private static final Hashtable classes = new Hashtable(); + /** + * Matches the bcprov/bcpkix/bcutil jars the OCSP/CRL feature draws classes from, at any + * pinned version, while excluding the legacy bcprov-jdk15 Apache Rampart brings in transitively. + */ + private static final Pattern BOUNCYCASTLE_JAR = + Pattern.compile("bc(prov|pkix|util)-jdk[0-9]+on-.*[.]jar"); + private File[] jarFiles; // Jar files + //jars stay open for the life of the loader; see readClassBytes + private final Hashtable openJars = new Hashtable(); + public ParentLastClassLoader(ClassLoader parent) { super(parent); String libDir = System.getProperty("synapse.bcprov.lib"); @@ -53,13 +64,15 @@ public ParentLastClassLoader(ClassLoader parent) { } File dir = new File(libDir); - jarFiles = dir.listFiles(new FileFilter() { + File[] matched = dir.listFiles(new FileFilter() { @Override public boolean accept(File f) { return f.getName().startsWith("synapse-nhttp-transport") || - f.getName().equals("bcprov-jdk15on-1.49.jar"); + BOUNCYCASTLE_JAR.matcher(f.getName()).matches(); } }); + // listFiles yields null when libDir is absent or unreadable. + jarFiles = matched != null ? matched : new File[0]; } @Override @@ -72,7 +85,6 @@ protected synchronized Class loadClass(String className, boolean resolve) throws ClassNotFoundException { try { - byte classByte[]; Class result; //checks in cached classes @@ -81,23 +93,10 @@ protected synchronized Class loadClass(String className, return result; } - for (File jarFile : jarFiles) { - try { - JarFile jar = new JarFile(jarFile); - JarEntry entry = jar.getJarEntry(className.replace(".", "/") + ".class"); - InputStream is = jar.getInputStream(entry); - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - int nextValue = is.read(); - while (-1 != nextValue) { - byteStream.write(nextValue); - nextValue = is.read(); - } - - classByte = byteStream.toByteArray(); - result = defineClass(className, classByte, 0, classByte.length, null); - classes.put(className, result); - break; - } catch (Exception ignored) {} + byte[] classByte = readClassBytes(className); + if (classByte != null) { + result = defineClass(className, classByte, 0, classByte.length, null); + classes.put(className, result); } if (result != null) { @@ -110,5 +109,42 @@ protected synchronized Class loadClass(String className, return super.loadClass(className, resolve); } } + + /** + * Reads a class from the first jar that carries it, or returns null if none does. Jars are + * opened once and kept open, and entries read buffered, since this runs per class, thousands of times. + */ + private byte[] readClassBytes(String className) { + String entryName = className.replace(".", "/") + ".class"; + for (File jarFile : jarFiles) { + try { + JarFile jar = openJars.get(jarFile.getPath()); + if (jar == null) { + jar = new JarFile(jarFile); + openJars.put(jarFile.getPath(), jar); + } + + JarEntry entry = jar.getJarEntry(entryName); + if (entry == null) { + continue; + } + + InputStream is = jar.getInputStream(entry); + try { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int read = is.read(buffer); + while (-1 != read) { + byteStream.write(buffer, 0, read); + read = is.read(buffer); + } + return byteStream.toByteArray(); + } finally { + is.close(); + } + } catch (Exception ignored) {} + } + return null; + } } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/SynapseAdaptorForOcspCrl.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/SynapseAdaptorForOcspCrl.java index 5a2c64842..186b25c86 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/SynapseAdaptorForOcspCrl.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/utils/sslcert/adaptor/SynapseAdaptorForOcspCrl.java @@ -24,7 +24,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import javax.security.cert.X509Certificate; +import java.security.cert.Certificate; /** * This is the adaptor used to plug OCSP/CRL feature to synapse while solving the Jar Hell @@ -37,7 +37,7 @@ public class SynapseAdaptorForOcspCrl { private static final ClassLoader loader = new ParentLastClassLoader( Thread.currentThread().getContextClassLoader()); - public void verifyRevocationStatus(X509Certificate[] peerCertificates, Integer cacheSize, + public void verifyRevocationStatus(Certificate[] peerCertificates, Integer cacheSize, Integer cacheDelay) throws CertificateVerificationException { if (peerCertificates == null || cacheSize == null || cacheDelay == null) { @@ -46,8 +46,10 @@ public void verifyRevocationStatus(X509Certificate[] peerCertificates, Integer c try { Class revocationManagerClass = loader.loadClass(Constants.REVOCATION_MANAGER); + // Looked up against the declared parameter type rather than the array's runtime + // class, which an SSLSession is free to narrow to X509Certificate[]. Method method = revocationManagerClass.getMethod(Constants.VERIFY_METHOD, - peerCertificates.getClass()); + Certificate[].class); Constructor constructor = revocationManagerClass.getConstructor(cacheSize.getClass(), cacheDelay.getClass()); Object revocationManager = constructor.newInstance(cacheSize, cacheDelay);