aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java')
-rw-r--r--libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java649
1 files changed, 649 insertions, 0 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java
new file mode 100644
index 000000000..1d05711e4
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSServerProtocol.java
@@ -0,0 +1,649 @@
+package org.spongycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.util.PublicKeyFactory;
+import org.spongycastle.util.Arrays;
+
+public class DTLSServerProtocol
+ extends DTLSProtocol
+{
+ protected boolean verifyRequests = true;
+
+ public DTLSServerProtocol(SecureRandom secureRandom)
+ {
+ super(secureRandom);
+ }
+
+ public boolean getVerifyRequests()
+ {
+ return verifyRequests;
+ }
+
+ public void setVerifyRequests(boolean verifyRequests)
+ {
+ this.verifyRequests = verifyRequests;
+ }
+
+ public DTLSTransport accept(TlsServer server, DatagramTransport transport)
+ throws IOException
+ {
+ if (server == null)
+ {
+ throw new IllegalArgumentException("'server' cannot be null");
+ }
+ if (transport == null)
+ {
+ throw new IllegalArgumentException("'transport' cannot be null");
+ }
+
+ SecurityParameters securityParameters = new SecurityParameters();
+ securityParameters.entity = ConnectionEnd.server;
+ securityParameters.serverRandom = TlsProtocol.createRandomBlock(secureRandom);
+
+ ServerHandshakeState state = new ServerHandshakeState();
+ state.server = server;
+ state.serverContext = new TlsServerContextImpl(secureRandom, securityParameters);
+ server.init(state.serverContext);
+
+ DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.serverContext, server, ContentType.handshake);
+
+ // TODO Need to handle sending of HelloVerifyRequest without entering a full connection
+
+ try
+ {
+ return serverHandshake(state, recordLayer);
+ }
+ catch (TlsFatalAlert fatalAlert)
+ {
+ recordLayer.fail(fatalAlert.getAlertDescription());
+ throw fatalAlert;
+ }
+ catch (IOException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ recordLayer.fail(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer)
+ throws IOException
+ {
+ SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
+ DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer);
+
+ DTLSReliableHandshake.Message clientMessage = handshake.receiveMessage();
+
+ {
+ // NOTE: After receiving a record from the client, we discover the record layer version
+ ProtocolVersion client_version = recordLayer.getDiscoveredPeerVersion();
+ // TODO Read RFCs for guidance on the expected record layer version number
+ state.serverContext.setClientVersion(client_version);
+ }
+
+ if (clientMessage.getType() == HandshakeType.client_hello)
+ {
+ processClientHello(state, clientMessage.getBody());
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ {
+ byte[] serverHelloBody = generateServerHello(state);
+
+ if (state.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + state.maxFragmentLength);
+ recordLayer.setPlaintextLimit(plainTextLimit);
+ }
+
+ securityParameters.cipherSuite = state.selectedCipherSuite;
+ securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
+ securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.serverContext,
+ state.selectedCipherSuite);
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length
+ * has a verify_data_length equal to 12. This includes all existing cipher suites.
+ */
+ securityParameters.verifyDataLength = 12;
+
+ handshake.sendMessage(HandshakeType.server_hello, serverHelloBody);
+ }
+
+ handshake.notifyHelloComplete();
+
+ Vector serverSupplementalData = state.server.getServerSupplementalData();
+ if (serverSupplementalData != null)
+ {
+ byte[] supplementalDataBody = generateSupplementalData(serverSupplementalData);
+ handshake.sendMessage(HandshakeType.supplemental_data, supplementalDataBody);
+ }
+
+ state.keyExchange = state.server.getKeyExchange();
+ state.keyExchange.init(state.serverContext);
+
+ state.serverCredentials = state.server.getCredentials();
+
+ Certificate serverCertificate = null;
+
+ if (state.serverCredentials == null)
+ {
+ state.keyExchange.skipServerCredentials();
+ }
+ else
+ {
+ state.keyExchange.processServerCredentials(state.serverCredentials);
+
+ serverCertificate = state.serverCredentials.getCertificate();
+ byte[] certificateBody = generateCertificate(serverCertificate);
+ handshake.sendMessage(HandshakeType.certificate, certificateBody);
+ }
+
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (serverCertificate == null || serverCertificate.isEmpty())
+ {
+ state.allowCertificateStatus = false;
+ }
+
+ if (state.allowCertificateStatus)
+ {
+ CertificateStatus certificateStatus = state.server.getCertificateStatus();
+ if (certificateStatus != null)
+ {
+ byte[] certificateStatusBody = generateCertificateStatus(state, certificateStatus);
+ handshake.sendMessage(HandshakeType.certificate_status, certificateStatusBody);
+ }
+ }
+
+ byte[] serverKeyExchange = state.keyExchange.generateServerKeyExchange();
+ if (serverKeyExchange != null)
+ {
+ handshake.sendMessage(HandshakeType.server_key_exchange, serverKeyExchange);
+ }
+
+ if (state.serverCredentials != null)
+ {
+ state.certificateRequest = state.server.getCertificateRequest();
+ if (state.certificateRequest != null)
+ {
+ state.keyExchange.validateCertificateRequest(state.certificateRequest);
+
+ byte[] certificateRequestBody = generateCertificateRequest(state, state.certificateRequest);
+ handshake.sendMessage(HandshakeType.certificate_request, certificateRequestBody);
+
+ TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(),
+ state.certificateRequest.getSupportedSignatureAlgorithms());
+ }
+ }
+
+ handshake.sendMessage(HandshakeType.server_hello_done, TlsUtils.EMPTY_BYTES);
+
+ handshake.getHandshakeHash().sealHashAlgorithms();
+
+ clientMessage = handshake.receiveMessage();
+
+ if (clientMessage.getType() == HandshakeType.supplemental_data)
+ {
+ processClientSupplementalData(state, clientMessage.getBody());
+ clientMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ state.server.processClientSupplementalData(null);
+ }
+
+ if (state.certificateRequest == null)
+ {
+ state.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+ if (clientMessage.getType() == HandshakeType.certificate)
+ {
+ processClientCertificate(state, clientMessage.getBody());
+ clientMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ if (TlsUtils.isTLSv12(state.serverContext))
+ {
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ notifyClientCertificate(state, Certificate.EMPTY_CHAIN);
+ }
+ }
+
+ if (clientMessage.getType() == HandshakeType.client_key_exchange)
+ {
+ processClientKeyExchange(state, clientMessage.getBody());
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange);
+ recordLayer.initPendingEpoch(state.server.getCipher());
+
+ TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
+
+ /*
+ * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing
+ * capability (i.e., all certificates except those containing fixed Diffie-Hellman
+ * parameters).
+ */
+ if (expectCertificateVerifyMessage(state))
+ {
+ byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify);
+ processCertificateVerify(state, certificateVerifyBody, prepareFinishHash);
+ }
+
+ // NOTE: Calculated exclusive of the actual Finished message from the client
+ byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.client_finished,
+ TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null));
+ processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedClientVerifyData);
+
+ if (state.expectSessionTicket)
+ {
+ NewSessionTicket newSessionTicket = state.server.getNewSessionTicket();
+ byte[] newSessionTicketBody = generateNewSessionTicket(state, newSessionTicket);
+ handshake.sendMessage(HandshakeType.session_ticket, newSessionTicketBody);
+ }
+
+ // NOTE: Calculated exclusive of the Finished message itself
+ byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.server_finished,
+ TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null));
+ handshake.sendMessage(HandshakeType.finished, serverVerifyData);
+
+ handshake.finish();
+
+ state.server.notifyHandshakeComplete();
+
+ return new DTLSTransport(recordLayer);
+ }
+
+ protected byte[] generateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest)
+ throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ certificateRequest.encode(buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateCertificateStatus(ServerHandshakeState state, CertificateStatus certificateStatus)
+ throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ certificateStatus.encode(buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket)
+ throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ newSessionTicket.encode(buf);
+ return buf.toByteArray();
+ }
+
+ protected byte[] generateServerHello(ServerHandshakeState state)
+ throws IOException
+ {
+ SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ ProtocolVersion server_version = state.server.getServerVersion();
+ if (!server_version.isEqualOrEarlierVersionOf(state.serverContext.getClientVersion()))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ // TODO Read RFCs for guidance on the expected record layer version number
+ // recordStream.setReadVersion(server_version);
+ // recordStream.setWriteVersion(server_version);
+ // recordStream.setRestrictReadVersion(true);
+ state.serverContext.setServerVersion(server_version);
+
+ TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf);
+
+ buf.write(securityParameters.getServerRandom());
+
+ /*
+ * The server may return an empty session_id to indicate that the session will not be cached
+ * and therefore cannot be resumed.
+ */
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+
+ state.selectedCipherSuite = state.server.getSelectedCipherSuite();
+ if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
+ || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error);
+
+ state.selectedCompressionMethod = state.server.getSelectedCompressionMethod();
+ if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ TlsUtils.writeUint16(state.selectedCipherSuite, buf);
+ TlsUtils.writeUint8(state.selectedCompressionMethod, buf);
+
+ state.serverExtensions = state.server.getServerExtensions();
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ if (state.secure_renegotiation)
+ {
+ byte[] renegExtData = TlsUtils.getExtensionData(state.serverExtensions, TlsProtocol.EXT_RenegotiationInfo);
+ boolean noRenegExt = (null == renegExtData);
+
+ if (noRenegExt)
+ {
+ /*
+ * Note that sending a "renegotiation_info" extension in response to a ClientHello
+ * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
+ * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed
+ * because the client is signaling its willingness to receive the extension via the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+
+ /*
+ * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
+ * "renegotiation_info" extension in the ServerHello message.
+ */
+ state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions);
+ state.serverExtensions.put(TlsProtocol.EXT_RenegotiationInfo,
+ TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
+ }
+ }
+
+ if (state.serverExtensions != null)
+ {
+ state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, state.serverExtensions,
+ AlertDescription.internal_error);
+
+ securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(state.serverExtensions);
+
+ state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions,
+ TlsExtensionsUtils.EXT_status_request, AlertDescription.internal_error);
+
+ state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions,
+ TlsProtocol.EXT_SessionTicket, AlertDescription.internal_error);
+
+ TlsProtocol.writeExtensions(buf, state.serverExtensions);
+ }
+
+ return buf.toByteArray();
+ }
+
+ protected void notifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate)
+ throws IOException
+ {
+ if (state.certificateRequest == null)
+ {
+ throw new IllegalStateException();
+ }
+
+ if (state.clientCertificate != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ state.clientCertificate = clientCertificate;
+
+ if (clientCertificate.isEmpty())
+ {
+ state.keyExchange.skipClientCredentials();
+ }
+ else
+ {
+
+ /*
+ * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request
+ * message was non-empty, one of the certificates in the certificate chain SHOULD be
+ * issued by one of the listed CAs.
+ */
+
+ state.clientCertificateType = TlsUtils.getClientCertificateType(clientCertificate,
+ state.serverCredentials.getCertificate());
+
+ state.keyExchange.processClientCertificate(clientCertificate);
+ }
+
+ /*
+ * RFC 5246 7.4.6. If the client does not send any certificates, the server MAY at its
+ * discretion either continue the handshake without client authentication, or respond with a
+ * fatal handshake_failure alert. Also, if some aspect of the certificate chain was
+ * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its
+ * discretion either continue the handshake (considering the client unauthenticated) or send
+ * a fatal alert.
+ */
+ state.server.notifyClientCertificate(clientCertificate);
+ }
+
+ protected void processClientCertificate(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ Certificate clientCertificate = Certificate.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ notifyClientCertificate(state, clientCertificate);
+ }
+
+ protected void processCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ DigitallySigned clientCertificateVerify = DigitallySigned.parse(state.serverContext, buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ // Verify the CertificateVerify message contains a correct signature.
+ try
+ {
+ // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
+ byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null);
+
+ org.spongycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
+
+ TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
+ tlsSigner.init(state.serverContext);
+ tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+ clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
+ }
+ catch (Exception e)
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ protected void processClientHello(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ // TODO Read RFCs for guidance on the expected record layer version number
+ ProtocolVersion client_version = TlsUtils.readVersion(buf);
+ if (!client_version.isDTLS())
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * Read the client random
+ */
+ byte[] client_random = TlsUtils.readFully(32, buf);
+
+ byte[] sessionID = TlsUtils.readOpaque8(buf);
+ if (sessionID.length > 32)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347
+ byte[] cookie = TlsUtils.readOpaque8(buf);
+
+ int cipher_suites_length = TlsUtils.readUint16(buf);
+ if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ /*
+ * NOTE: "If the session_id field is not empty (implying a session resumption request) this
+ * vector must include at least the cipher_suite from that session."
+ */
+ state.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf);
+
+ int compression_methods_length = TlsUtils.readUint8(buf);
+ if (compression_methods_length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ state.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf);
+
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions.
+ */
+ state.clientExtensions = TlsProtocol.readExtensions(buf);
+
+ state.serverContext.setClientVersion(client_version);
+
+ state.server.notifyClientVersion(client_version);
+
+ state.serverContext.getSecurityParameters().clientRandom = client_random;
+
+ state.server.notifyOfferedCipherSuites(state.offeredCipherSuites);
+ state.server.notifyOfferedCompressionMethods(state.offeredCompressionMethods);
+
+ /*
+ * RFC 5746 3.6. Server Behavior: Initial Handshake
+ */
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+
+ /*
+ * When a ClientHello is received, the server MUST check if it includes the
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
+ * to TRUE.
+ */
+ if (Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ {
+ state.secure_renegotiation = true;
+ }
+
+ /*
+ * The server MUST check if the "renegotiation_info" extension is included in the
+ * ClientHello.
+ */
+ byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo);
+ if (renegExtData != null)
+ {
+ /*
+ * If the extension is present, set secure_renegotiation flag to TRUE. The
+ * server MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake.
+ */
+ state.secure_renegotiation = true;
+
+ if (!Arrays.constantTimeAreEqual(renegExtData, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+ }
+
+ state.server.notifySecureRenegotiation(state.secure_renegotiation);
+
+ if (state.clientExtensions != null)
+ {
+ state.server.processClientExtensions(state.clientExtensions);
+ }
+ }
+
+ protected void processClientKeyExchange(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ state.keyExchange.processClientKeyExchange(buf);
+
+ TlsProtocol.assertEmpty(buf);
+ }
+
+ protected void processClientSupplementalData(ServerHandshakeState state, byte[] body)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+ Vector clientSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
+ state.server.processClientSupplementalData(clientSupplementalData);
+ }
+
+ protected boolean expectCertificateVerifyMessage(ServerHandshakeState state)
+ {
+ return state.clientCertificateType >= 0 && TlsUtils.hasSigningCapability(state.clientCertificateType);
+ }
+
+ protected static class ServerHandshakeState
+ {
+ TlsServer server = null;
+ TlsServerContextImpl serverContext = null;
+ int[] offeredCipherSuites;
+ short[] offeredCompressionMethods;
+ Hashtable clientExtensions;
+ int selectedCipherSuite = -1;
+ short selectedCompressionMethod = -1;
+ boolean secure_renegotiation = false;
+ short maxFragmentLength = -1;
+ boolean allowCertificateStatus = false;
+ boolean expectSessionTicket = false;
+ Hashtable serverExtensions = null;
+ TlsKeyExchange keyExchange = null;
+ TlsCredentials serverCredentials = null;
+ CertificateRequest certificateRequest = null;
+ short clientCertificateType = -1;
+ Certificate clientCertificate = null;
+ }
+}