Mercurial > hg > release > icedtea7-2.6
changeset 2000:dbe859b50636
Patch to detect certificate CN mismatches and offer user the choice to ignore it.
2009-08-25 Deepak Bhole <dbhole@redhat.com>
* netx/net/sourceforge/jnlp/resources/Messages.properties: Add new
message key for CN name mismatches.
* netx/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.
* netx/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.
author | Andrew John Hughes <ahughes@redhat.com> |
---|---|
date | Tue, 06 Oct 2009 18:06:43 +0100 |
parents | d38d3977ca3e |
children | 47802906c81f |
files | ChangeLog netx/net/sourceforge/jnlp/resources/Messages.properties netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java |
diffstat | 4 files changed, 176 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Tue Oct 06 18:01:08 2009 +0100 +++ b/ChangeLog Tue Oct 06 18:06:43 2009 +0100 @@ -1,3 +1,26 @@ +2009-08-25 Deepak Bhole <dbhole@redhat.com> + + * netx/net/sourceforge/jnlp/resources/Messages.properties: Add new + message key for CN name mismatches. + * netx/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. + * netx/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> * netx/net/sourceforge/jnlp/JNLPFile.java: Add a new key variable that is
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties Tue Oct 06 18:01:08 2009 +0100 +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties Tue Oct 06 18:06:43 2009 +0100 @@ -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/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java Tue Oct 06 18:01:08 2009 +0100 +++ b/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java Tue Oct 06 18:06:43 2009 +0100 @@ -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/cenetx/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/netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java Tue Oct 06 18:01:08 2009 +0100 +++ b/netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java Tue Oct 06 18:06:43 2009 +0100 @@ -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)); } /**