Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 : " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 : " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,8 +44,18 @@ public class ParentLastClassLoader extends ClassLoader {
//used to cache already defined classes
private static final Hashtable<String,Class> classes = new Hashtable<String,Class>();

/**
* 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<String,JarFile> openJars = new Hashtable<String,JarFile>();

public ParentLastClassLoader(ClassLoader parent) {
super(parent);
String libDir = System.getProperty("synapse.bcprov.lib");
Expand All @@ -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
Expand All @@ -72,7 +85,6 @@ protected synchronized Class<?> loadClass(String className,
boolean resolve) throws ClassNotFoundException {

try {
byte classByte[];
Class result;

//checks in cached classes
Expand All @@ -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) {
Expand All @@ -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;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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);
Expand Down
Loading