Skip to content

Instantly share code, notes, and snippets.

@mlaccetti
Created April 28, 2015 12:48
Show Gist options
  • Save mlaccetti/f824c0bc0b7b2d17084c to your computer and use it in GitHub Desktop.
Save mlaccetti/f824c0bc0b7b2d17084c to your computer and use it in GitHub Desktop.
Trusting Wildcards for Guggle
package laccetti.server.security;
import lombok.extern.slf4j.Slf4j;
import org.jsslutils.sslcontext.X509TrustManagerWrapper;
import javax.net.ssl.X509TrustManager;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* Wildcard Trust
*/
@Slf4j
public class DefaultTrustManager implements X509TrustManagerWrapper {
private final String[] hostNamePatterns;
public DefaultTrustManager(final String[] hostNamePatterns) {
this.hostNamePatterns = hostNamePatterns;
}
@Override
public X509TrustManager wrapTrustManager(final X509TrustManager trustManager) {
return new X509TrustManager() {
/**
* {@inheritDoc}
*/
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
log.trace("Validating client certificate.");
verifyHostName(chain);
}
/**
* {@inheritDoc}
*/
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
log.trace("Validating server certificate.");
verifyHostName(chain);
}
/**
* {@inheritDoc}
*/
public X509Certificate[] getAcceptedIssuers() {
log.debug("Getting accepted issuers.");
return trustManager.getAcceptedIssuers();
}
/**
* Checks whether a host name matches the provided pattern. It accepts
* the use of wildcards in the pattern, e.g. {@code *.example.com}.
*
* @param hostName The host name.
* @param pattern The host name pattern, which may contain wild cards.
* @return {@code true} if the host name matched the pattern, otherwise
* {@code false}.
*/
private boolean hostNameMatchesPattern(final String hostName, final String pattern) {
final String[] nameElements = hostName.split("\\.");
final String[] patternElements = pattern.split("\\.");
boolean hostMatch = nameElements.length == patternElements.length;
for (int i = 0; i < nameElements.length && hostMatch; i++) {
final String ne = nameElements[i];
final String pe = patternElements[i];
if (!pe.equals("*")) {
hostMatch = ne.equalsIgnoreCase(pe);
}
}
return hostMatch;
}
private void verifyHostName(final X509Certificate[] chain) {
try {
// TODO: NPE if root DN.
final List<String> hostnames = getNamesFromCertificate(chain[0]);
if (hostnames == null || hostnames.size() == 0) {
log.warn("No hostname(s) found in X509Certificate.");
throw new GeneralSecurityException("No hostname(s) found in X509Certificate.");
}
log.trace("Verifying hostnames: {}", hostnames);
boolean matched = false;
for (final String hostNamePattern : hostNamePatterns) {
for (final String hostname : hostnames) {
log.trace("Matching hostname {} against pattern {}", hostname, hostNamePattern);
if (hostNameMatchesPattern(hostname, hostNamePattern)) {
log.trace("Hostname matched.");
matched = true;
}
}
}
if (!matched) {
throw new CertificateException("The host name contained in the certificate chain subject DN could not be matched against the expected wildcards.'");
}
} catch (final Throwable t) {
log.warn("Error parsing subject dn: " + chain[0].getSubjectX500Principal(), t);
}
}
private List<String> getNamesFromCertificate(final X509Certificate cert) {
final List<String> cnList = new ArrayList<>(5);
final String subjectPrincipal = cert.getSubjectX500Principal().toString();
final StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
while (st.hasMoreTokens()) {
final String tok = st.nextToken();
final int x = tok.indexOf("CN=");
if (x >= 0) {
cnList.add(tok.substring(x + 3).trim());
}
}
if (!cnList.isEmpty()) {
return cnList;
}
return null;
}
};
}
}
package laccetti.server.security;
import lombok.extern.slf4j.Slf4j;
import org.jsslutils.sslcontext.PKIXSSLContextFactory;
import org.jsslutils.sslcontext.SSLContextFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import java.security.GeneralSecurityException;
/**
* Trust wildcards for Guggle - automatically wires the trust manager into the SSL context after Spring is done loading
*/
@Component
@Slf4j
public class SslContext {
@PostConstruct
public void init() throws GeneralSecurityException, SSLContextFactory.SSLContextFactoryException {
log.debug("Setting SSL context.");
final PKIXSSLContextFactory sslContextFactory = new PKIXSSLContextFactory();
sslContextFactory.setTrustManagerWrapper(new DefaultTrustManager(new String[]{"*.google.com", "*.googleapis.com", "*.storage.googleapis.com"}));
final SSLContext sslContext = sslContextFactory.buildSSLContext();
SSLContext.setDefault(sslContext);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment