Skip to content

Commit

Permalink
Fixes #6099 - Cipher preference may break SNI if certificates have di…
Browse files Browse the repository at this point in the history
…fferent key types.

Updated the logic in SslContextFactory.Server.sniSelect(...) to check if there is
any certificate that matches, and if so return a null alias in the hope to be called
again and pick the right alias for the SNI.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
(cherry picked from commit 6829691)
  • Loading branch information
sbordet committed May 10, 2021
1 parent e9f260f commit b5b3874
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 15 deletions.
Expand Up @@ -40,6 +40,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.Connection;
Expand Down Expand Up @@ -489,6 +490,21 @@ protected void customize(Socket socket, Class<? extends Connection> connection,
assertEquals(0, history.size());
}

@Test
public void testSNIWithDifferentKeyTypes() throws Exception
{
// This KeyStore contains 2 certificates, one with keyAlg=EC, one with keyAlg=RSA.
start(ssl -> ssl.setKeyStorePath("src/test/resources/keystore_sni_key_types.p12"));

// Make a request with SNI = rsa.domain.com, the RSA certificate should be chosen.
HttpTester.Response response1 = HttpTester.parseResponse(getResponse("rsa.domain.com", "rsa.domain.com"));
assertEquals(HttpStatus.OK_200, response1.getStatus());

// Make a request with SNI = ec.domain.com, the EC certificate should be chosen.
HttpTester.Response response2 = HttpTester.parseResponse(getResponse("ec.domain.com", "ec.domain.com"));
assertEquals(HttpStatus.OK_200, response2.getStatus());
}

private String getResponse(String host, String cn) throws Exception
{
String response = getResponse(host, host, cn);
Expand Down
Binary file not shown.
Expand Up @@ -250,7 +250,7 @@ public interface SniSelector
* <p>Selects a certificate based on SNI information.</p>
* <p>This method may be invoked multiple times during the TLS handshake, with different parameters.
* For example, the {@code keyType} could be different, and subsequently the collection of certificates
* (because they need to match the {@code keyType}.</p>
* (because they need to match the {@code keyType}).</p>
*
* @param keyType the key algorithm type name
* @param issuers the list of acceptable CA issuer subject names or null if it does not matter which issuers are used
Expand Down
Expand Up @@ -2155,12 +2155,17 @@ public void setWantClientAuth(boolean wantClientAuth)
}

/**
* Does the default {@link #sniSelect(String, Principal[], SSLSession, String, Collection)} implementation
* require an SNI match? Note that if a non SNI handshake is accepted, requests may still be rejected
* at the HTTP level for incorrect SNI (see SecureRequestCustomizer).
* <p>Returns whether an SNI match is required when choosing the alias that
* identifies the certificate to send to the client.</p>
* <p>The exact logic to choose an alias given the SNI is configurable via
* {@link #setSNISelector(SniX509ExtendedKeyManager.SniSelector)}.</p>
* <p>The default implementation is {@link #sniSelect(String, Principal[], SSLSession, String, Collection)}
* and if SNI is not required it will delegate the TLS implementation to
* choose an alias (typically the first alias in the KeyStore).</p>
* <p>Note that if a non SNI handshake is accepted, requests may still be rejected
* at the HTTP level for incorrect SNI (see SecureRequestCustomizer).</p>
*
* @return true if no SNI match is handled as no certificate match, false if no SNI match is handled by
* delegation to the non SNI matching methods.
* @return whether an SNI match is required when choosing the alias that identifies the certificate
*/
@ManagedAttribute("Whether the TLS handshake is rejected if there is no SNI host match")
public boolean isSniRequired()
Expand All @@ -2169,14 +2174,12 @@ public boolean isSniRequired()
}

/**
* Set if the default {@link #sniSelect(String, Principal[], SSLSession, String, Collection)} implementation
* require an SNI match? Note that if a non SNI handshake is accepted, requests may still be rejected
* at the HTTP level for incorrect SNI (see SecureRequestCustomizer).
* This setting may have no effect if {@link #sniSelect(String, Principal[], SSLSession, String, Collection)} is
* overridden or a non null function is passed to {@link #setSNISelector(SniX509ExtendedKeyManager.SniSelector)}.
* <p>Sets whether an SNI match is required when choosing the alias that
* identifies the certificate to send to the client.</p>
* <p>This setting may have no effect if {@link #sniSelect(String, Principal[], SSLSession, String, Collection)} is
* overridden or a custom function is passed to {@link #setSNISelector(SniX509ExtendedKeyManager.SniSelector)}.</p>
*
* @param sniRequired true if no SNI match is handled as no certificate match, false if no SNI match is handled by
* delegation to the non SNI matching methods.
* @param sniRequired whether an SNI match is required when choosing the alias that identifies the certificate
*/
public void setSniRequired(boolean sniRequired)
{
Expand Down Expand Up @@ -2244,9 +2247,15 @@ public String sniSelect(String keyType, Principal[] issuers, SSLSession session,
.filter(x509 -> x509.matches(sniHost))
.collect(Collectors.toList());

// No match, let the JDK decide unless unmatched SNIs are rejected.
if (matching.isEmpty())
return isSniRequired() ? null : SniX509ExtendedKeyManager.SniSelector.DELEGATE;
{
// There is no match for this SNI among the certificates valid for
// this keyType; check if there is any certificate that matches this
// SNI, as we will likely be called again with a different keyType.
boolean anyMatching = aliasCerts().values().stream()
.anyMatch(x509 -> x509.matches(sniHost));
return isSniRequired() || anyMatching ? null : SniX509ExtendedKeyManager.SniSelector.DELEGATE;
}

String alias = matching.get(0).getAlias();
if (matching.size() == 1)
Expand Down

0 comments on commit b5b3874

Please sign in to comment.