Created
April 28, 2015 12:48
-
-
Save mlaccetti/f824c0bc0b7b2d17084c to your computer and use it in GitHub Desktop.
Trusting Wildcards for Guggle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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