org.bouncycastle.jsse.provider.ProvTlsServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bctls-jdk18on Show documentation
Show all versions of bctls-jdk18on Show documentation
The Bouncy Castle Java APIs for TLS and DTLS, including a provider for the JSSE.
The newest version!
package org.bouncycastle.jsse.provider;
import java.io.IOException;
import java.math.BigInteger;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.jsse.BCSNIMatcher;
import org.bouncycastle.jsse.BCSNIServerName;
import org.bouncycastle.jsse.BCX509Key;
import org.bouncycastle.jsse.provider.SignatureSchemeInfo.PerConnection;
import org.bouncycastle.tls.AlertDescription;
import org.bouncycastle.tls.AlertLevel;
import org.bouncycastle.tls.Certificate;
import org.bouncycastle.tls.CertificateRequest;
import org.bouncycastle.tls.CertificateStatus;
import org.bouncycastle.tls.ClientCertificateType;
import org.bouncycastle.tls.DefaultTlsServer;
import org.bouncycastle.tls.KeyExchangeAlgorithm;
import org.bouncycastle.tls.NamedGroup;
import org.bouncycastle.tls.ProtocolName;
import org.bouncycastle.tls.ProtocolVersion;
import org.bouncycastle.tls.SecurityParameters;
import org.bouncycastle.tls.ServerName;
import org.bouncycastle.tls.SessionParameters;
import org.bouncycastle.tls.SignatureAndHashAlgorithm;
import org.bouncycastle.tls.TlsContext;
import org.bouncycastle.tls.TlsCredentials;
import org.bouncycastle.tls.TlsDHUtils;
import org.bouncycastle.tls.TlsExtensionsUtils;
import org.bouncycastle.tls.TlsFatalAlert;
import org.bouncycastle.tls.TlsSession;
import org.bouncycastle.tls.TlsUtils;
import org.bouncycastle.tls.TrustedAuthority;
import org.bouncycastle.tls.crypto.DHGroup;
import org.bouncycastle.tls.crypto.TlsDHConfig;
import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
class ProvTlsServer
extends DefaultTlsServer
implements ProvTlsPeer
{
private static final Logger LOG = Logger.getLogger(ProvTlsServer.class.getName());
private static final String PROPERTY_DEFAULT_DHE_PARAMETERS = "jdk.tls.server.defaultDHEParameters";
// TODO[jsse] Integrate this into NamedGroupInfo
private static final int provEphemeralDHKeySize = PropertyUtils.getIntegerSystemProperty("jdk.tls.ephemeralDHKeySize", 2048, 1024, 8192);
private static final DHGroup[] provServerDefaultDHEParameters = getDefaultDHEParameters();
private static final boolean provServerEnableCA = PropertyUtils
.getBooleanSystemProperty("jdk.tls.server.enableCAExtension", true);
private static final boolean provServerEnableSessionResumption = PropertyUtils
.getBooleanSystemProperty("org.bouncycastle.jsse.server.enableSessionResumption", true);
// TODO[jsse] Support status_request and status_request_v2 extensions
// private static final boolean provServerEnableStatusRequest = PropertyUtils.getBooleanSystemProperty(
// "jdk.tls.server.enableStatusRequestExtension", false);
private static final boolean provServerEnableStatusRequest = false;
private static final boolean provServerEnableTrustedCAKeys = PropertyUtils
.getBooleanSystemProperty("org.bouncycastle.jsse.server.enableTrustedCAKeysExtension", false);
private static final boolean provServerOmitSigAlgsCert = PropertyUtils
.getBooleanSystemProperty("org.bouncycastle.jsse.server.omitSigAlgsCertExtension", true);
private static DHGroup[] getDefaultDHEParameters()
{
String propertyValue = PropertyUtils.getStringSecurityProperty(PROPERTY_DEFAULT_DHE_PARAMETERS);
if (null == propertyValue)
{
return null;
}
String input = JsseUtils.stripDoubleQuotes(JsseUtils.removeAllWhitespace(propertyValue));
int limit = input.length();
if (limit < 1)
{
return null;
}
ArrayList dhGroups = new ArrayList();
int outerComma = -1;
do
{
int openBrace = outerComma + 1;
if (openBrace >= limit || '{' != input.charAt(openBrace))
{
break;
}
int modulus = openBrace + 1;
int innerComma = input.indexOf(',', modulus);
if (innerComma <= modulus)
{
break;
}
int generator = innerComma + 1;
int closeBrace = input.indexOf('}', generator);
if (closeBrace <= generator)
{
break;
}
try
{
BigInteger p = parseDHParameter(input, modulus, innerComma);
BigInteger g = parseDHParameter(input, generator, closeBrace);
DHGroup dhGroup = TlsDHUtils.getStandardGroupForDHParameters(p, g);
if (null != dhGroup)
{
dhGroups.add(dhGroup);
}
else if (!p.isProbablePrime(120))
{
LOG.log(Level.WARNING, "Non-prime modulus ignored in security property ["
+ PROPERTY_DEFAULT_DHE_PARAMETERS + "]: " + p.toString(16));
}
else
{
dhGroups.add(new DHGroup(p, null, g, 0));
}
}
catch (Exception e)
{
break;
}
outerComma = closeBrace + 1;
if (outerComma >= limit)
{
DHGroup[] result = dhGroups.toArray(new DHGroup[dhGroups.size()]);
java.util.Arrays.sort(result, new Comparator()
{
public int compare(DHGroup a, DHGroup b)
{
return a.getP().bitLength() - b.getP().bitLength();
}
});
return result;
}
}
while (',' == input.charAt(outerComma));
LOG.log(Level.WARNING, "Invalid syntax for security property [" + PROPERTY_DEFAULT_DHE_PARAMETERS + "]");
return null;
}
private static BigInteger parseDHParameter(String s, int beginIndex, int endIndex)
{
return new BigInteger(s.substring(beginIndex, endIndex), 16);
}
protected final String serverID;
protected final ProvTlsManager manager;
protected final ProvSSLParameters sslParameters;
protected final JsseSecurityParameters jsseSecurityParameters = new JsseSecurityParameters();
protected ProvSSLSession sslSession = null;
protected BCSNIServerName matchedSNIServerName = null;
protected Set keyManagerMissCache = null;
protected TlsCredentials credentials = null;
protected boolean handshakeComplete = false;
ProvTlsServer(ProvTlsManager manager, ProvSSLParameters sslParameters)
{
super(manager.getContextData().getCrypto());
this.serverID = JsseUtils.getPeerID("server", manager);
this.manager = manager;
this.sslParameters = sslParameters.copyForConnection();
}
public String getID()
{
return serverID;
}
public ProvSSLSession getSession()
{
return sslSession;
}
public TlsContext getTlsContext()
{
return context;
}
@Override
protected boolean allowCertificateStatus()
{
return provServerEnableStatusRequest;
}
@Override
protected boolean allowMultiCertStatus()
{
return provServerEnableStatusRequest;
}
@Override
protected boolean allowTrustedCAIndication()
{
return null != jsseSecurityParameters.trustedIssuers;
}
@Override
protected String getDetailMessageNoCipherSuite()
{
// CAUTION: Required for Common Criteria
StringBuilder sb = new StringBuilder(serverID);
int[] offered = offeredCipherSuites;
if (TlsUtils.isNullOrEmpty(offered))
{
sb.append(" found no selectable cipher suite because none were offered.");
}
else
{
sb.append(" found no selectable cipher suite among the ");
sb.append(offered.length);
sb.append(" offered: ");
ProvSSLContextSpi context = manager.getContextData().getContext();
sb.append('[');
JsseUtils.appendCipherSuiteDetail(sb, context, offered[0]);
for (int i = 1; i < offered.length; ++i)
{
sb.append(", ");
JsseUtils.appendCipherSuiteDetail(sb, context, offered[i]);
}
sb.append(']');
}
return sb.toString();
}
@Override
protected int getMaximumNegotiableCurveBits()
{
NamedGroupInfo.DefaultedResult maxBitsResult = NamedGroupInfo.getMaximumBitsServerECDH(
jsseSecurityParameters.namedGroups);
int maxBits = maxBitsResult.getResult();
return maxBits;
}
@Override
protected int getMaximumNegotiableFiniteFieldBits()
{
NamedGroupInfo.DefaultedResult maxBitsResult = NamedGroupInfo.getMaximumBitsServerFFDHE(
jsseSecurityParameters.namedGroups);
int maxBits = maxBitsResult.getResult();
if (maxBitsResult.isDefaulted() &&
!TlsUtils.isNullOrEmpty(provServerDefaultDHEParameters) &&
!manager.getContextData().getContext().isFips())
{
DHGroup largest = provServerDefaultDHEParameters[provServerDefaultDHEParameters.length - 1];
maxBits = Math.max(maxBits, largest.getP().bitLength());
}
return maxBits >= provEphemeralDHKeySize ? maxBits : 0;
}
@Override
protected Vector getProtocolNames()
{
return JsseUtils.getProtocolNames(sslParameters.getApplicationProtocols());
}
@Override
protected int[] getSupportedCipherSuites()
{
return manager.getContextData().getContext().getActiveCipherSuites(getCrypto(), sslParameters,
getProtocolVersions());
}
@Override
protected ProtocolVersion[] getSupportedVersions()
{
return manager.getContextData().getContext().getActiveProtocolVersions(sslParameters);
}
@Override
protected boolean preferLocalCipherSuites()
{
return sslParameters.getUseCipherSuitesOrder();
}
@Override
protected boolean selectCipherSuite(int cipherSuite) throws IOException
{
TlsCredentials cipherSuiteCredentials = null;
int keyExchangeAlgorithm = TlsUtils.getKeyExchangeAlgorithm(cipherSuite);
if (!KeyExchangeAlgorithm.isAnonymous(keyExchangeAlgorithm))
{
cipherSuiteCredentials = selectCredentials(jsseSecurityParameters.trustedIssuers, keyExchangeAlgorithm);
if (null == cipherSuiteCredentials)
{
String cipherSuiteName = ProvSSLContextSpi.getCipherSuiteName(cipherSuite);
if (LOG.isLoggable(Level.FINER))
{
LOG.finer(serverID + " found no credentials for cipher suite: " + cipherSuiteName);
}
return false;
}
}
boolean result = super.selectCipherSuite(cipherSuite);
if (result)
{
this.credentials = cipherSuiteCredentials;
}
return result;
}
@Override
public TlsDHConfig getDHConfig() throws IOException
{
int minimumFiniteFieldBits = TlsDHUtils.getMinimumFiniteFieldBits(selectedCipherSuite);
minimumFiniteFieldBits = Math.max(minimumFiniteFieldBits, provEphemeralDHKeySize);
NamedGroupInfo.DefaultedResult namedGroupResult = NamedGroupInfo.selectServerFFDHE(
jsseSecurityParameters.namedGroups, minimumFiniteFieldBits);
int namedGroup = namedGroupResult.getResult();
if (namedGroupResult.isDefaulted() &&
!TlsUtils.isNullOrEmpty(provServerDefaultDHEParameters) &&
!manager.getContextData().getContext().isFips())
{
for (DHGroup dhGroup : provServerDefaultDHEParameters)
{
int bits = dhGroup.getP().bitLength();
if (bits >= minimumFiniteFieldBits)
{
if (namedGroup < 0 || bits <= NamedGroup.getFiniteFieldBits(namedGroup))
{
return new TlsDHConfig(dhGroup);
}
break;
}
}
}
return TlsDHUtils.createNamedDHConfig(context, namedGroup);
}
@Override
protected int selectDH(int minimumFiniteFieldBits)
{
throw new UnsupportedOperationException();
}
@Override
protected int selectDHDefault(int minimumFiniteFieldBits)
{
throw new UnsupportedOperationException();
}
@Override
protected int selectECDH(int minimumCurveBits)
{
return NamedGroupInfo.selectServerECDH(jsseSecurityParameters.namedGroups, minimumCurveBits).getResult();
}
@Override
protected int selectECDHDefault(int minimumCurveBits)
{
throw new UnsupportedOperationException();
}
@Override
protected ProtocolName selectProtocolName() throws IOException
{
if (null == sslParameters.getEngineAPSelector() && null == sslParameters.getSocketAPSelector())
{
return super.selectProtocolName();
}
@SuppressWarnings("unchecked")
Vector applicationProtocols = clientProtocolNames;
List protocols = JsseUtils.getProtocolNames(applicationProtocols);
String protocol = manager.selectApplicationProtocol(Collections.unmodifiableList(protocols));
if (null == protocol)
{
throw new TlsFatalAlert(AlertDescription.no_application_protocol);
}
else if (protocol.length() < 1)
{
return null;
}
else if (!protocols.contains(protocol))
{
throw new TlsFatalAlert(AlertDescription.no_application_protocol);
}
return ProtocolName.asUtf8Encoding(protocol);
}
@Override
protected boolean shouldSelectProtocolNameEarly()
{
return null == sslParameters.getEngineAPSelector() && null == sslParameters.getSocketAPSelector();
}
@Override
public boolean allowLegacyResumption()
{
return JsseUtils.allowLegacyResumption();
}
@Override
public int getMaxCertificateChainLength()
{
return JsseUtils.getMaxCertificateChainLength();
}
@Override
public int getMaxHandshakeMessageSize()
{
return JsseUtils.getMaxHandshakeMessageSize();
}
public synchronized boolean isHandshakeComplete()
{
return handshakeComplete;
}
@Override
public TlsCredentials getCredentials()
throws IOException
{
if (credentials == null)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
return credentials;
}
@Override
public CertificateRequest getCertificateRequest() throws IOException
{
if (!isClientAuthEnabled())
{
return null;
}
final ContextData contextData = manager.getContextData();
final ProtocolVersion negotiatedVersion = context.getServerVersion();
Vector serverSigAlgs =
jsseSecurityParameters.signatureSchemes.getLocalSignatureAndHashAlgorithms();
Vector certificateAuthorities = null;
if (provServerEnableCA)
{
certificateAuthorities = JsseUtils.getCertificateAuthorities(contextData.getX509TrustManager());
}
if (TlsUtils.isTLSv13(negotiatedVersion))
{
/*
* TODO[tls13] RFC 8446 4.4.2.1. A server MAY request that a client present an OCSP response
* with its certificate by sending an empty "status_request" extension in its
* CertificateRequest message.
*/
/*
* RFC 8446 4.3.2. This field SHALL be zero length unless used for the post-handshake
* authentication exchanges [..].
*/
byte[] certificateRequestContext = TlsUtils.EMPTY_BYTES;
Vector serverSigAlgsCert =
jsseSecurityParameters.signatureSchemes.getLocalSignatureAndHashAlgorithmsCert();
if (serverSigAlgsCert == null && !provServerOmitSigAlgsCert)
{
serverSigAlgsCert = jsseSecurityParameters.signatureSchemes.getLocalSignatureAndHashAlgorithms();
}
return new CertificateRequest(certificateRequestContext, serverSigAlgs, serverSigAlgsCert,
certificateAuthorities);
}
// TODO[jsse] These should really be based on TlsCrypto support
short[] certificateTypes = new short[]{ ClientCertificateType.ecdsa_sign,
ClientCertificateType.rsa_sign, ClientCertificateType.dss_sign };
return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
}
@Override
public CertificateStatus getCertificateStatus() throws IOException
{
// TODO[jsse] Support status_request and status_request_v2 extensions
// SecurityParameters securityParameters = context.getSecurityParametersHandshake();
// int statusRequestVersion = securityParameters.getStatusRequestVersion();
//
// if (statusRequestVersion == 2)
// {
// int count = statusRequestV2.size();
// for (int i = 0; i < count; ++i)
// {
// CertificateStatusRequestItemV2 item = (CertificateStatusRequestItemV2)statusRequestV2.get(i);
// short statusType = item.getStatusType();
// if (CertificateStatusType.ocsp_multi == statusType)
// {
// int chainLength = credentials.getCertificate().getLength();
// Vector ocspResponseList = new Vector(chainLength);
// for (int j = 0; j < chainLength; ++j)
// {
// // TODO Actual OCSP response
// ocspResponseList.add(null);
// }
//
// return new CertificateStatus(CertificateStatusType.ocsp_multi, ocspResponseList);
// }
// else if (CertificateStatusType.ocsp == statusType)
// {
// // TODO Actual OCSP response
// OCSPResponse ocspResponse;
//
// return new CertificateStatus(CertificateStatusType.ocsp, ocspResponse);
// }
// }
// }
// else if (statusRequestVersion == 1)
// {
// if (CertificateStatusType.ocsp == certificateStatusRequest.getStatusType())
// {
// OCSPStatusRequest ocspStatusRequest = certificateStatusRequest.getOCSPStatusRequest();
//
// @SuppressWarnings("unchecked")
// Vector responderIDList = ocspStatusRequest.getResponderIDList();
// Extensions requestExtensions = ocspStatusRequest.getRequestExtensions();
//
// X509Certificate eeCert = JsseUtils.getEndEntity(getCrypto(), credentials.getCertificate());
//
// // ...
// }
// }
return null;
}
@Override
public JcaTlsCrypto getCrypto()
{
return manager.getContextData().getCrypto();
}
@Override
public int[] getSupportedGroups() throws IOException
{
// Setup the local supported groups
{
ContextData contextData = manager.getContextData();
ProtocolVersion negotiatedVersion = context.getServerVersion();
jsseSecurityParameters.namedGroups = contextData.getNamedGroupsServer(sslParameters, negotiatedVersion);
}
return NamedGroupInfo.getSupportedGroupsLocalServer(jsseSecurityParameters.namedGroups);
}
@Override
public int getSelectedCipherSuite() throws IOException
{
final ContextData contextData = manager.getContextData();
final SecurityParameters securityParameters = context.getSecurityParametersHandshake();
// Set up the peer supported groups
{
int[] clientSupportedGroups = securityParameters.getClientSupportedGroups();
jsseSecurityParameters.namedGroups.notifyPeerData(clientSupportedGroups);
}
// Setup the local supported signature schemes
{
ProtocolVersion negotiatedVersion = context.getServerVersion();
// TODO[jsse] May want this selection to depend on the peer's supported_groups
jsseSecurityParameters.signatureSchemes = contextData.getSignatureSchemesServer(sslParameters,
negotiatedVersion, jsseSecurityParameters.namedGroups);
}
// Setup the peer supported signature schemes
{
@SuppressWarnings("unchecked")
Vector clientSigAlgs = (Vector)
securityParameters.getClientSigAlgs();
@SuppressWarnings("unchecked")
Vector clientSigAlgsCert = (Vector)
securityParameters.getClientSigAlgsCert();
/*
* TODO[tls13] Legacy schemes (cert-only for TLS 1.3) complicate these conversions. Consider which
* (if any) of these should be constrained by locally enabled schemes (especially once
* jdk.tls.signatureSchemes support added).
*/
List peerSigSchemes = contextData.getSignatureSchemes(clientSigAlgs);
List peerSigSchemesCert = null;
if (clientSigAlgsCert != clientSigAlgs)
{
peerSigSchemesCert = contextData.getSignatureSchemes(clientSigAlgsCert);
}
jsseSecurityParameters.signatureSchemes.notifyPeerData(peerSigSchemes, peerSigSchemesCert);
if (LOG.isLoggable(Level.FINEST))
{
{
String title = serverID + " peer signature_algorithms";
LOG.finest(JsseUtils.getSignatureAlgorithmsReport(title, peerSigSchemes));
}
if (peerSigSchemesCert != null)
{
String title = serverID + " peer signature_algorithms_cert";
LOG.finest(JsseUtils.getSignatureAlgorithmsReport(title, peerSigSchemesCert));
}
}
}
if (DummyX509KeyManager.INSTANCE == contextData.getX509KeyManager())
{
// We don't support anonymous cipher suites, so there has to be a (real) key manager
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
keyManagerMissCache = new HashSet();
int selectedCipherSuite = super.getSelectedCipherSuite();
keyManagerMissCache = null;
String selectedCipherSuiteName = contextData.getContext().validateNegotiatedCipherSuite(sslParameters,
selectedCipherSuite);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " selected cipher suite: " + selectedCipherSuiteName);
}
return selectedCipherSuite;
}
@Override
public Hashtable getServerExtensions() throws IOException
{
super.getServerExtensions();
/*
* RFC 6066 When resuming a session, the server MUST NOT include a server_name extension
* in the server hello.
*/
if (null != matchedSNIServerName)
{
// TODO[tls13] Resumption/PSK. SNI is always negotiated per-connection in TLS 1.3
TlsExtensionsUtils.addServerNameExtensionServer(serverExtensions);
}
@SuppressWarnings("unchecked")
Hashtable result = serverExtensions;
return result;
}
@Override
public TlsSession getSessionToResume(byte[] sessionID)
{
ProvSSLSessionContext sslSessionContext = manager.getContextData().getServerSessionContext();
if (provServerEnableSessionResumption)
{
ProvSSLSession availableSSLSession = sslSessionContext.getSessionImpl(sessionID);
if (null != availableSSLSession)
{
TlsSession sessionToResume = availableSSLSession.getTlsSession();
if (isResumable(availableSSLSession, sessionToResume))
{
this.sslSession = availableSSLSession;
return sessionToResume;
}
}
}
JsseUtils.checkSessionCreationEnabled(manager);
return null;
}
@Override
public byte[] getNewSessionID()
{
// TODO[tls13] Resumption/PSK
if (!provServerEnableSessionResumption || TlsUtils.isTLSv13(context))
{
return null;
}
return context.getNonceGenerator().generateNonce(32);
}
@Override
public void notifySession(TlsSession session)
{
byte[] sessionID = session.getSessionID();
boolean isResumed = (null != sslSession && sslSession.getTlsSession() == session);
if (isResumed)
{
if (LOG.isLoggable(Level.FINE))
{
// -DM Hex.toHexString
LOG.fine(serverID + " resumed session: " + Hex.toHexString(sessionID));
}
}
else
{
this.sslSession = null;
if (LOG.isLoggable(Level.FINE))
{
if (TlsUtils.isNullOrEmpty(sessionID))
{
LOG.fine(serverID + " did not specify a session ID");
}
else
{
// -DM Hex.toHexString
LOG.fine(serverID + " specified new session: " + Hex.toHexString(sessionID));
}
}
JsseUtils.checkSessionCreationEnabled(manager);
}
manager.notifyHandshakeSession(manager.getContextData().getServerSessionContext(),
context.getSecurityParametersHandshake(), jsseSecurityParameters, sslSession);
}
@Override
public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
{
Level level = alertLevel == AlertLevel.warning ? Level.FINE
: alertDescription == AlertDescription.internal_error ? Level.WARNING
: Level.INFO;
if (LOG.isLoggable(level))
{
String msg = JsseUtils.getAlertRaisedLogMessage(serverID, alertLevel, alertDescription);
if (message != null)
{
msg = msg + ": " + message;
}
LOG.log(level, msg, cause);
}
}
@Override
public void notifyAlertReceived(short alertLevel, short alertDescription)
{
super.notifyAlertReceived(alertLevel, alertDescription);
Level level = alertLevel == AlertLevel.warning ? Level.FINE
: Level.INFO;
if (LOG.isLoggable(level))
{
String msg = JsseUtils.getAlertReceivedLogMessage(serverID, alertLevel, alertDescription);
LOG.log(level, msg);
}
}
@Override
public ProtocolVersion getServerVersion() throws IOException
{
ProtocolVersion serverVersion = super.getServerVersion();
String serverVersionName = manager.getContextData().getContext().validateNegotiatedProtocol(sslParameters,
serverVersion);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " selected protocol version: " + serverVersionName);
}
return serverVersion;
}
@Override
public void notifyClientCertificate(Certificate clientCertificate) throws IOException
{
// NOTE: This method isn't called unless we returned non-null from getCertificateRequest() earlier
if (!isClientAuthEnabled())
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (null == clientCertificate || clientCertificate.isEmpty())
{
if (sslParameters.getNeedClientAuth())
{
short alertDescription = TlsUtils.isTLSv13(context)
? AlertDescription.certificate_required
: AlertDescription.handshake_failure;
throw new TlsFatalAlert(alertDescription);
}
}
else
{
X509Certificate[] chain = JsseUtils.getX509CertificateChain(getCrypto(), clientCertificate);
/*
* We never try to continue the handshake with an untrusted client certificate (although it could
* be an option if we only "want" client-auth, rather than "need".
*
* NOTE: The 'authType' parameter is a dummy value not used by trust managers.
*/
manager.checkClientTrusted(chain, "TLS-client-auth");
}
}
@Override
public void notifyConnectionClosed()
{
super.notifyConnectionClosed();
if (LOG.isLoggable(Level.INFO))
{
LOG.info(serverID + " disconnected from " + JsseUtils.getPeerReport(manager));
}
}
@Override
public void notifyHandshakeBeginning() throws IOException
{
super.notifyHandshakeBeginning();
if (LOG.isLoggable(Level.INFO))
{
LOG.info(serverID + " accepting connection from " + JsseUtils.getPeerReport(manager));
}
}
@Override
public synchronized void notifyHandshakeComplete() throws IOException
{
super.notifyHandshakeComplete();
this.handshakeComplete = true;
if (LOG.isLoggable(Level.INFO))
{
LOG.info(serverID + " established connection with " + JsseUtils.getPeerReport(manager));
}
TlsSession connectionTlsSession = context.getSession();
if (null == sslSession || sslSession.getTlsSession() != connectionTlsSession)
{
ProvSSLSessionContext sslSessionContext = manager.getContextData().getServerSessionContext();
String peerHost = manager.getPeerHost();
int peerPort = manager.getPeerPort();
JsseSessionParameters jsseSessionParameters = new JsseSessionParameters(
sslParameters.getEndpointIdentificationAlgorithm(), matchedSNIServerName);
// TODO[tls13] Resumption/PSK
boolean addToCache = provServerEnableSessionResumption && !TlsUtils.isTLSv13(context);
this.sslSession = sslSessionContext.reportSession(peerHost, peerPort, connectionTlsSession,
jsseSessionParameters, addToCache);
}
manager.notifyHandshakeComplete(new ProvSSLConnection(this));
}
@Override
public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException
{
if (!secureRenegotiation)
{
boolean allowLegacyHelloMessages = PropertyUtils.getBooleanSystemProperty(
"sun.security.ssl.allowLegacyHelloMessages", true);
if (!allowLegacyHelloMessages)
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
}
}
@Override
public void processClientExtensions(@SuppressWarnings("rawtypes") Hashtable clientExtensions) throws IOException
{
super.processClientExtensions(clientExtensions);
/*
* TODO[jsse] RFC 6066 A server that implements this extension MUST NOT accept the
* request to resume the session if the server_name extension contains a different name.
*/
@SuppressWarnings("unchecked")
Vector serverNameList = context.getSecurityParametersHandshake().getClientServerNames();
if (null != serverNameList)
{
Collection sniMatchers = sslParameters.getSNIMatchers();
if (null == sniMatchers || sniMatchers.isEmpty())
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " ignored SNI (no matchers specified)");
}
}
else
{
this.matchedSNIServerName = JsseUtils.findMatchingSNIServerName(serverNameList, sniMatchers);
if (null == matchedSNIServerName)
{
throw new TlsFatalAlert(AlertDescription.unrecognized_name);
}
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " accepted SNI: " + matchedSNIServerName);
}
}
}
if (TlsUtils.isTLSv13(context))
{
@SuppressWarnings("unchecked")
Vector certificateAuthorities = TlsExtensionsUtils
.getCertificateAuthoritiesExtension(clientExtensions);
jsseSecurityParameters.trustedIssuers = JsseUtils.toX500Principals(certificateAuthorities);
}
else
{
if (provServerEnableTrustedCAKeys)
{
@SuppressWarnings("unchecked")
Vector trustedCAKeys = this.trustedCAKeys;
jsseSecurityParameters.trustedIssuers = JsseUtils.getTrustedIssuers(trustedCAKeys);
}
}
}
@Override
public boolean requiresCloseNotify()
{
return JsseUtils.requireCloseNotify();
}
@Override
public boolean requiresExtendedMasterSecret()
{
return !JsseUtils.allowLegacyMasterSecret();
}
@Override
public boolean shouldUseExtendedMasterSecret()
{
return JsseUtils.useExtendedMasterSecret();
}
protected boolean isClientAuthEnabled()
{
return sslParameters.getNeedClientAuth() || sslParameters.getWantClientAuth();
}
protected boolean isResumable(ProvSSLSession provSSLSession, TlsSession tlsSession)
{
if (null == tlsSession || !tlsSession.isResumable())
{
return false;
}
{
SecurityParameters securityParameters = context.getSecurityParametersHandshake();
ProtocolVersion negotiatedVersion = securityParameters.getNegotiatedVersion();
if (TlsUtils.isTLSv13(negotiatedVersion))
{
return false;
}
// TODO[resumption] Avoid the copy somehow?
SessionParameters sessionParameters = tlsSession.exportSessionParameters();
if (null == sessionParameters ||
!negotiatedVersion.equals(sessionParameters.getNegotiatedVersion()) ||
!Arrays.contains(getCipherSuites(), sessionParameters.getCipherSuite()) ||
!Arrays.contains(offeredCipherSuites, sessionParameters.getCipherSuite()))
{
return false;
}
if (sslParameters.getNeedClientAuth() && sessionParameters.getPeerCertificate() == null)
{
return false;
}
{
String connectionEndpointID = sslParameters.getEndpointIdentificationAlgorithm();
if (null != connectionEndpointID)
{
JsseSessionParameters jsseSessionParameters = provSSLSession.getJsseSessionParameters();
String sessionEndpointID = jsseSessionParameters.getEndpointIDAlgorithm();
if (!connectionEndpointID.equalsIgnoreCase(sessionEndpointID))
{
if (LOG.isLoggable(Level.FINER))
{
LOG.finer(serverID + ": Session not resumable - endpoint ID algorithm mismatch; connection: "
+ connectionEndpointID + ", session: " + sessionEndpointID);
}
return false;
}
}
}
}
{
/*
* TODO[tls13] RFC 8446 4.2.11. In TLS versions prior to TLS 1.3, the Server Name
* Identification (SNI) value was intended to be associated with the session (Section 3
* of [RFC6066]), with the server being required to enforce that the SNI value
* associated with the session matches the one specified in the resumption handshake.
* However, in reality the implementations were not consistent on which of two supplied
* SNI values they would use, leading to the consistency requirement being de facto
* enforced by the clients. In TLS 1.3, the SNI value is always explicitly specified in
* the resumption handshake, and there is no need for the server to associate an SNI
* value with the ticket. Clients, however, SHOULD store the SNI with the PSK to fulfill
* the requirements of Section 4.6.1.
*/
JsseSessionParameters jsseSessionParameters = provSSLSession.getJsseSessionParameters();
BCSNIServerName connectionSNI = matchedSNIServerName;
BCSNIServerName sessionSNI = jsseSessionParameters.getMatchedSNIServerName();
if (!JsseUtils.equals(connectionSNI, sessionSNI))
{
if (LOG.isLoggable(Level.FINEST))
{
LOG.finest(serverID + ": Session not resumable - SNI mismatch; connection: " + connectionSNI
+ ", session: " + sessionSNI);
}
return false;
}
}
return true;
}
protected TlsCredentials selectCredentials(Principal[] issuers, int keyExchangeAlgorithm) throws IOException
{
switch (keyExchangeAlgorithm)
{
case KeyExchangeAlgorithm.DHE_DSS:
case KeyExchangeAlgorithm.DHE_RSA:
case KeyExchangeAlgorithm.ECDHE_ECDSA:
case KeyExchangeAlgorithm.ECDHE_RSA:
case KeyExchangeAlgorithm.RSA:
{
if (KeyExchangeAlgorithm.RSA == keyExchangeAlgorithm
|| !TlsUtils.isSignatureAlgorithmsExtensionAllowed(context.getServerVersion()))
{
return selectServerCredentialsLegacy(issuers, keyExchangeAlgorithm);
}
return selectServerCredentials12(issuers, keyExchangeAlgorithm);
}
case KeyExchangeAlgorithm.NULL:
{
byte[] certificateRequestContext = TlsUtils.EMPTY_BYTES;
return selectServerCredentials13(issuers, certificateRequestContext);
}
default:
return null;
}
}
protected TlsCredentials selectServerCredentials12(Principal[] issuers, int keyExchangeAlgorithm) throws IOException
{
final short legacySignatureAlgorithm = TlsUtils.getLegacySignatureAlgorithmServer(keyExchangeAlgorithm);
PerConnection signatureSchemes = jsseSecurityParameters.signatureSchemes;
LinkedHashMap keyTypeMap = new LinkedHashMap();
for (SignatureSchemeInfo signatureSchemeInfo : signatureSchemes.getPeerSigSchemes())
{
if (!TlsUtils.isValidSignatureSchemeForServerKeyExchange(signatureSchemeInfo.getSignatureScheme(),
keyExchangeAlgorithm))
{
continue;
}
final short signatureAlgorithm = signatureSchemeInfo.getSignatureAlgorithm();
String keyType = (legacySignatureAlgorithm == signatureAlgorithm)
? JsseUtils.getKeyTypeLegacyServer(keyExchangeAlgorithm)
: signatureSchemeInfo.getKeyType();
if (keyManagerMissCache.contains(keyType))
{
continue;
}
if (keyTypeMap.containsKey(keyType))
{
continue;
}
if (!signatureSchemeInfo.isSupportedPre13() ||
!signatureSchemes.hasLocalSignatureScheme(signatureSchemeInfo))
{
continue;
}
keyTypeMap.put(keyType, signatureSchemeInfo);
}
if (keyTypeMap.isEmpty())
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " (1.2) has no key types to try for KeyExchangeAlgorithm " + keyExchangeAlgorithm);
}
return null;
}
String[] keyTypes = keyTypeMap.keySet().toArray(TlsUtils.EMPTY_STRINGS);
BCX509Key x509Key = manager.chooseServerKey(keyTypes, issuers);
if (null == x509Key)
{
handleKeyManagerMisses(keyTypeMap, null);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " (1.2) did not select any credentials for KeyExchangeAlgorithm " + keyExchangeAlgorithm);
}
return null;
}
String selectedKeyType = x509Key.getKeyType();
handleKeyManagerMisses(keyTypeMap, selectedKeyType);
SignatureSchemeInfo selectedSignatureSchemeInfo = keyTypeMap.get(selectedKeyType);
if (null == selectedSignatureSchemeInfo)
{
throw new TlsFatalAlert(AlertDescription.internal_error, "Key manager returned invalid key type");
}
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " (1.2) selected credentials for signature scheme '" + selectedSignatureSchemeInfo
+ "' (keyType '" + selectedKeyType + "'), with private key algorithm '"
+ JsseUtils.getPrivateKeyAlgorithm(x509Key.getPrivateKey()) + "'");
}
return JsseUtils.createCredentialedSigner(context, getCrypto(), x509Key,
selectedSignatureSchemeInfo.getSignatureAndHashAlgorithm());
}
protected TlsCredentials selectServerCredentials13(Principal[] issuers, byte[] certificateRequestContext)
throws IOException
{
PerConnection signatureSchemes = jsseSecurityParameters.signatureSchemes;
LinkedHashMap keyTypeMap = new LinkedHashMap();
for (SignatureSchemeInfo signatureSchemeInfo : signatureSchemes.getPeerSigSchemes())
{
String keyType = signatureSchemeInfo.getKeyType13();
if (keyManagerMissCache.contains(keyType))
{
continue;
}
if (keyTypeMap.containsKey(keyType))
{
continue;
}
if (!signatureSchemeInfo.isSupportedPost13() ||
!signatureSchemes.hasLocalSignatureScheme(signatureSchemeInfo))
{
continue;
}
keyTypeMap.put(keyType, signatureSchemeInfo);
}
if (keyTypeMap.isEmpty())
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " (1.3) found no usable signature schemes");
}
return null;
}
String[] keyTypes = keyTypeMap.keySet().toArray(TlsUtils.EMPTY_STRINGS);
BCX509Key x509Key = manager.chooseServerKey(keyTypes, issuers);
if (null == x509Key)
{
handleKeyManagerMisses(keyTypeMap, null);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " (1.3) did not select any credentials");
}
return null;
}
String selectedKeyType = x509Key.getKeyType();
handleKeyManagerMisses(keyTypeMap, selectedKeyType);
SignatureSchemeInfo selectedSignatureSchemeInfo = keyTypeMap.get(selectedKeyType);
if (null == selectedSignatureSchemeInfo)
{
throw new TlsFatalAlert(AlertDescription.internal_error, "Key manager returned invalid key type");
}
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(serverID + " (1.3) selected credentials for signature scheme '" + selectedSignatureSchemeInfo
+ "' (keyType '" + selectedKeyType + "'), with private key algorithm '"
+ JsseUtils.getPrivateKeyAlgorithm(x509Key.getPrivateKey()) + "'");
}
return JsseUtils.createCredentialedSigner13(context, getCrypto(), x509Key,
selectedSignatureSchemeInfo.getSignatureAndHashAlgorithm(), certificateRequestContext);
}
protected TlsCredentials selectServerCredentialsLegacy(Principal[] issuers, int keyExchangeAlgorithm)
throws IOException
{
String keyType = JsseUtils.getKeyTypeLegacyServer(keyExchangeAlgorithm);
if (keyManagerMissCache.contains(keyType))
{
return null;
}
BCX509Key x509Key = manager.chooseServerKey(new String[]{ keyType }, issuers);
if (null == x509Key)
{
keyManagerMissCache.add(keyType);
return null;
}
if (KeyExchangeAlgorithm.RSA == keyExchangeAlgorithm)
{
return JsseUtils.createCredentialedDecryptor(getCrypto(), x509Key);
}
return JsseUtils.createCredentialedSigner(context, getCrypto(), x509Key, null);
}
private void handleKeyManagerMisses(LinkedHashMap keyTypeMap, String selectedKeyType)
{
for (Map.Entry entry : keyTypeMap.entrySet())
{
String keyType = entry.getKey();
if (keyType.equals(selectedKeyType))
{
break;
}
keyManagerMissCache.add(keyType);
if (LOG.isLoggable(Level.FINER))
{
SignatureSchemeInfo signatureSchemeInfo = entry.getValue();
LOG.finer(serverID + " found no credentials for signature scheme '" + signatureSchemeInfo
+ "' (keyType '" + keyType + "')");
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy