Mercurial > hg > release > icedtea6-1.6
changeset 1668:66cdb266311e
Import from trunk.
Detect CN mismatches in a site's https certificate and allow the user to
bypass it if they choose to do so.
author | Deepak Bhole <dbhole@redhat.com> |
---|---|
date | Tue, 25 Aug 2009 10:55:01 -0400 |
parents | da9eb62e065f |
children | 20cd55dd027f |
files | ChangeLog rt/net/sourceforge/jnlp/resources/Messages.properties rt/net/sourceforge/jnlp/security/HttpsCertVerifier.java rt/net/sourceforge/jnlp/security/VariableX509TrustManager.java |
diffstat | 4 files changed, 177 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Tue Aug 25 10:53:05 2009 -0400 +++ b/ChangeLog Tue Aug 25 10:55:01 2009 -0400 @@ -1,3 +1,26 @@ +2009-08-25 Deepak Bhole <dbhole@redhat.com> + + * rt/net/sourceforge/jnlp/resources/Messages.properties: Add new + message key for CN name mismatches. + * rt/net/sourceforge/jnlp/security/HttpsCertVerifier.java + (HttpsCertVerifier): Accept new parameters that indicate certificate trust + status, CN mismatch status, and the hostname. + (getAlreadyTrustPublisher): Use provided isTrusted boolean to get around + checkServerTrusted() synchronization. + (getDetails): Include details about CN mismatch. + (getNamesForCert): New private method. Returns all acceptable names for + a given X509Certificate. + (R): Overloaded the method to return messages that have 2 variables. + * rt/net/sourceforge/jnlp/security/VariableX509TrustManager.java: Extend + X509ExtendedTrustManager rather than X509TrustManager. + (checkClientTrusted): Overloaded method with one that takes a hostname. + (checkServerTrusted): Same. The new overloaded method also checks for CN + mismatch if the certificate is not explicitly trusted. + (isExplicitlyTrusted): Returns if the given certificate chain is part of + the local user trusted DB. + (askUser): Change parameters to accept information about trust, host match + status, and hostname. + 2009-08-25 Deepak Bhole <dbhole@redhat.com> * rt/net/sourceforge/jnlp/JNLPFile.java: Add a new key variable that is @@ -536,7 +559,7 @@ * plugin/icedteanp/IcedTeaNPPlugin.h: New file. Header for IcedTeaNPPlugin.cc. * plugin/icedteanp/IcedTeaPluginRequestProcessor.cc: New file. Processes - plugin data requests from Java side. + plugin data requests from Java side. * plugin/icedteanp/IcedTeaPluginRequestProcessor.h: new file. Header for IcedTeaPluginRequestProcessor.cc. * plugin/icedteanp/IcedTeaPluginUtils.cc: New file. Utility functions for
--- a/rt/net/sourceforge/jnlp/resources/Messages.properties Tue Aug 25 10:53:05 2009 -0400 +++ b/rt/net/sourceforge/jnlp/resources/Messages.properties Tue Aug 25 10:55:01 2009 -0400 @@ -168,6 +168,7 @@ SNotYetValidCert=Resources contain entries whose signer certificate is not yet valid. SUntrustedCertificate=The digital signature was generated with an untrusted certificate. STrustedCertificate=The digital signature was generated with a trusted certificate. +SCNMisMatch=The expected hostname for this certificate is: "{0}"<BR>The address being connected to is: "{1}" SRunWithoutRestrictions=This application will be run without the security restrictions normally provided by java.
--- a/rt/net/sourceforge/jnlp/security/HttpsCertVerifier.java Tue Aug 25 10:53:05 2009 -0400 +++ b/rt/net/sourceforge/jnlp/security/HttpsCertVerifier.java Tue Aug 25 10:55:01 2009 -0400 @@ -37,38 +37,49 @@ package net.sourceforge.jnlp.security; +import java.io.IOException; import java.security.cert.CertPath; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateNotYetValidException; -import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import net.sourceforge.jnlp.runtime.JNLPRuntime; import net.sourceforge.jnlp.tools.KeyTool; +import sun.security.util.DerValue; +import sun.security.util.HostnameChecker; +import sun.security.x509.X500Name; public class HttpsCertVerifier implements CertVerifier { private VariableX509TrustManager tm; private X509Certificate[] chain; private String authType; + private String hostName; + private boolean isTrusted; + private boolean hostMatched; private ArrayList<String> details = new ArrayList<String>(); - public HttpsCertVerifier(VariableX509TrustManager tm, X509Certificate[] chain, String authType) { + public HttpsCertVerifier(VariableX509TrustManager tm, + X509Certificate[] chain, String authType, + boolean isTrusted, boolean hostMatched, + String hostName) { this.tm = tm; this.chain = chain; this.authType = authType; + this.hostName = hostName; + this.isTrusted = isTrusted; + this.hostMatched = hostMatched; } public boolean getAlreadyTrustPublisher() { - try { - tm.checkServerTrusted(chain, authType, true); - return true; - } catch (CertificateException ce) { - return false; - } + return isTrusted; } public ArrayList<CertPath> getCerts() { @@ -91,10 +102,12 @@ } public ArrayList<String> getDetails() { + boolean hasExpiredCert=false; boolean hasExpiringCert=false; boolean notYetValidCert=false; boolean isUntrusted=false; + boolean CNMisMatch = !hostMatched; if (! getAlreadyTrustPublisher()) isUntrusted = true; @@ -121,7 +134,9 @@ } } - if (isUntrusted || hasExpiredCert || hasExpiringCert || notYetValidCert) { + String altNames = getNamesForCert(chain[0]); + + if (isUntrusted || hasExpiredCert || hasExpiringCert || notYetValidCert || CNMisMatch) { if (isUntrusted) addToDetails(R("SUntrustedCertificate")); if (hasExpiredCert) @@ -130,10 +145,54 @@ addToDetails(R("SHasExpiringCert")); if (notYetValidCert) addToDetails(R("SNotYetValidCert")); + if (CNMisMatch) + addToDetails(R("SCNMisMatch", altNames, this.hostName)); } + + return details; } + private String getNamesForCert(X509Certificate c) { + + String names = ""; + + + // We use the specification from + // http://java.sun.com/j2se/1.5.0/docs/api/java/security/cert/X509Certificate.html#getSubjectAlternativeNames() + // to determine the type of address + int ALTNAME_DNS = 2; + int ALTNAME_IP = 7; + + try { + Collection<List<?>> subjAltNames = c.getSubjectAlternativeNames(); + X500Name subjectName = HostnameChecker.getSubjectX500Name(c); + DerValue derValue = subjectName.findMostSpecificAttribute + (X500Name.commonName_oid); + names += derValue.getAsString(); + + if (subjAltNames != null) { + for (List<?> next : subjAltNames) { + if ( ((Integer)next.get(0)).intValue() == ALTNAME_IP || + ((Integer)next.get(0)).intValue() == ALTNAME_DNS + ) { + names += ", " + (String)next.get(1); + } + } + } + + if (subjAltNames != null) + names = names.substring(2); // remove proceeding ", " + + } catch (CertificateParsingException cpe) { + cpe.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + return names; + } + private void addToDetails(String detail) { if (!details.contains(detail)) details.add(detail); @@ -142,6 +201,10 @@ private static String R(String key) { return JNLPRuntime.getMessage(key); } + + private static String R(String key, String arg1, String arg2) { + return JNLPRuntime.getMessage(key, new Object[] { arg1, arg2 }); + } public Certificate getPublisher() { if (chain.length > 0)
--- a/rt/net/sourceforge/jnlp/security/VariableX509TrustManager.java Tue Aug 25 10:53:05 2009 -0400 +++ b/rt/net/sourceforge/jnlp/security/VariableX509TrustManager.java Tue Aug 25 10:55:01 2009 -0400 @@ -47,15 +47,18 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import sun.security.util.HostnameChecker; import sun.security.validator.ValidatorException; +import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; + /** * This class implements an X509 Trust Manager. The certificates it trusts are * "variable", in the sense that it can dynamically, and temporarily support * different certificates that are not in the keystore. */ -public class VariableX509TrustManager implements X509TrustManager { +public class VariableX509TrustManager extends X509ExtendedTrustManager { KeyStore userKeyStore = null; KeyStore caKeyStore = null; @@ -112,11 +115,11 @@ } /** - * Check if client is trusted (not support for custom here, only system/user) + * Check if client is trusted (no support for custom here, only system/user) */ - public void checkClientTrusted(X509Certificate[] chain, String authType) + public void checkClientTrusted(X509Certificate[] chain, String authType, + String hostName, String algorithm) throws CertificateException { - // First try catrustmanager, then try usertrustmanager try { caTrustManager.checkClientTrusted(chain, authType); @@ -131,9 +134,20 @@ } } + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + checkClientTrusted(chain, authType, null, null); + } + + public void checkServerTrusted(X509Certificate[] chain, String authType, + String hostName, String algorithm) + throws CertificateException { + checkServerTrusted(chain, authType, hostName, false); + } + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkServerTrusted(chain, authType, false); + checkServerTrusted(chain, authType, null, null); } /** @@ -143,17 +157,44 @@ * @param authType The auth type algorithm * @param checkOnly Whether to "check only" i.e. no user prompt, or to prompt for permission */ - public synchronized void checkServerTrusted(X509Certificate[] chain, String authType, boolean checkOnly) throws CertificateException { + public synchronized void checkServerTrusted(X509Certificate[] chain, + String authType, String hostName, + boolean checkOnly) throws CertificateException { + CertificateException ce = null; + boolean trusted = true; + boolean CNMatched = true; + try { checkAllManagers(chain, authType); - } catch (CertificateException ce) { - + } catch (CertificateException e) { + trusted = false; + ce = e; + } + + // If the certificate is not explicitly trusted, we + // need to prompt the user + if (!isExplicitlyTrusted(chain, authType)) { + + try { + HostnameChecker checker = HostnameChecker + .getInstance(HostnameChecker.TYPE_TLS); + + checker.match(hostName, chain[0]); // only need to match @ 0 for + // CN + + } catch (CertificateException e) { + CNMatched = false; + ce = e; + } + } + + if (!trusted || !CNMatched) { if (checkOnly) { throw ce; } else { - boolean b = askUser(chain,authType); - + boolean b = askUser(chain, authType, trusted, CNMatched, hostName); + if (b) { temporarilyTrust(chain[0]); } @@ -162,7 +203,7 @@ } } } - + /** * Check system, user and custom trust manager */ @@ -179,6 +220,26 @@ } } } + + /** + * Return if the user explicitly trusted this i.e. in userTrustManager or temporarilyTrusted + */ + private boolean isExplicitlyTrusted(X509Certificate[] chain, String authType) { + boolean explicitlyTrusted = false; + + try { + userTrustManager.checkServerTrusted(chain, authType); + explicitlyTrusted = true; + } catch (ValidatorException uex) { + if (temporarilyTrusted.contains(chain[0])) + explicitlyTrusted = true; + } catch (CertificateException ce) { + // do nothing, this means that the cert is not explicitly trusted + } + + return explicitlyTrusted; + + } public X509Certificate[] getAcceptedIssuers() { // delegate to default @@ -201,8 +262,14 @@ * @param authType The authentication algorithm * @return user's response */ - private boolean askUser(X509Certificate[] chain, String authType) { - return SecurityWarningDialog.showCertWarningDialog(SecurityWarningDialog.AccessType.UNVERIFIED, null, new HttpsCertVerifier(this, chain, authType)); + private boolean askUser(X509Certificate[] chain, String authType, + boolean isTrusted, boolean hostMatched, + String hostName) { + return SecurityWarningDialog.showCertWarningDialog( + SecurityWarningDialog.AccessType.UNVERIFIED, null, + new HttpsCertVerifier(this, chain, authType, + isTrusted, hostMatched, + hostName)); } /**