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[] 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();
  }
  private void processHandshakeMessage(short type, byte[] buf) throws IOException {
    ByteArrayInputStream is = new ByteArrayInputStream(buf);

    switch (type) {
      case HandshakeType.certificate:
        {
          switch (connection_state) {
            case CS_SERVER_HELLO_RECEIVED:
              {
                // Parse the Certificate message and send to cipher suite

                Certificate serverCertificate = Certificate.parse(is);

                assertEmpty(is);

                this.keyExchange.processServerCertificate(serverCertificate);

                this.authentication = tlsClient.getAuthentication();
                this.authentication.notifyServerCertificate(serverCertificate);

                break;
              }
            default:
              this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
          }

          connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
          break;
        }
      case HandshakeType.finished:
        switch (connection_state) {
          case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED:
            /*
             * Read the checksum from the finished message, it has always 12
             * bytes for TLS 1.0 and 36 for SSLv3.
             */
            boolean isTls =
                tlsClientContext.getServerVersion().getFullVersion()
                    >= ProtocolVersion.TLSv10.getFullVersion();

            int checksumLength = isTls ? 12 : 36;
            byte[] serverVerifyData = new byte[checksumLength];
            TlsUtils.readFully(serverVerifyData, is);

            assertEmpty(is);

            /*
             * Calculate our own checksum.
             */
            byte[] expectedServerVerifyData =
                TlsUtils.calculateVerifyData(
                    tlsClientContext, "server finished", rs.getCurrentHash(TlsUtils.SSL_SERVER));

            /*
             * Compare both checksums.
             */
            if (!Arrays.constantTimeAreEqual(expectedServerVerifyData, serverVerifyData)) {
              /*
               * Wrong checksum in the finished message.
               */
              this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
            }

            connection_state = CS_DONE;

            /*
             * We are now ready to receive application data.
             */
            this.appDataReady = true;
            break;
          default:
            this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
        }
        break;
      case HandshakeType.server_hello:
        switch (connection_state) {
          case CS_CLIENT_HELLO_SEND:
            /*
             * Read the server hello message
             */
            ProtocolVersion server_version = TlsUtils.readVersion(is);
            ProtocolVersion client_version = this.tlsClientContext.getClientVersion();
            if (server_version.getFullVersion() > client_version.getFullVersion()) {
              this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
            }

            this.tlsClientContext.setServerVersion(server_version);
            this.tlsClient.notifyServerVersion(server_version);

            /*
             * Read the server random
             */
            securityParameters.serverRandom = new byte[32];
            TlsUtils.readFully(securityParameters.serverRandom, is);

            byte[] sessionID = TlsUtils.readOpaque8(is);
            if (sessionID.length > 32) {
              this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
            }

            this.tlsClient.notifySessionID(sessionID);

            /*
             * Find out which CipherSuite the server has chosen and check that
             * it was one of the offered ones.
             */
            int selectedCipherSuite = TlsUtils.readUint16(is);
            if (!arrayContains(offeredCipherSuites, selectedCipherSuite)
                || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
              this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
            }

            this.tlsClient.notifySelectedCipherSuite(selectedCipherSuite);

            /*
             * Find out which CompressionMethod the server has chosen and check that
             * it was one of the offered ones.
             */
            short selectedCompressionMethod = TlsUtils.readUint8(is);
            if (!arrayContains(offeredCompressionMethods, selectedCompressionMethod)) {
              this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
            }

            this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod);

            /*
             * RFC3546 2.2 The extended server hello message format MAY be
             * sent in place of the server hello message when the client has
             * requested extended functionality via the extended client hello
             * message specified in Section 2.1. ... Note that the extended
             * server hello message is only sent in response to an extended
             * client hello message. This prevents the possibility that the
             * extended server hello message could "break" existing TLS 1.0
             * clients.
             */

            /*
             * 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.
             */

            // Integer -> byte[]
            Hashtable serverExtensions = new Hashtable();

            if (is.available() > 0) {
              // Process extensions from extended server hello
              byte[] extBytes = TlsUtils.readOpaque16(is);

              ByteArrayInputStream ext = new ByteArrayInputStream(extBytes);
              while (ext.available() > 0) {
                Integer extType = Integers.valueOf(TlsUtils.readUint16(ext));
                byte[] extValue = TlsUtils.readOpaque16(ext);

                /*
                 * RFC 5746 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. TLS implementations MUST continue to comply with
                 * Section 7.4.1.4 for all other extensions.
                 */

                if (!extType.equals(EXT_RenegotiationInfo)
                    && clientExtensions.get(extType) == null) {
                  /*
                   * RFC 3546 2.3 Note that for all extension types
                   * (including those defined in future), the extension
                   * type MUST NOT appear in the extended server hello
                   * unless the same extension type appeared in the
                   * corresponding client hello. Thus clients MUST abort
                   * the handshake if they receive an extension type in
                   * the extended server hello that they did not request
                   * in the associated (extended) client hello.
                   */
                  this.failWithError(AlertLevel.fatal, AlertDescription.unsupported_extension);
                }

                if (serverExtensions.containsKey(extType)) {
                  /*
                   * RFC 3546 2.3 Also note that when multiple
                   * extensions of different types are present in the
                   * extended client hello or the extended server hello,
                   * the extensions may appear in any order. There MUST
                   * NOT be more than one extension of the same type.
                   */
                  this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
                }

                serverExtensions.put(extType, extValue);
              }
            }

            assertEmpty(is);

            /*
             * RFC 5746 3.4. When a ServerHello is received, the client MUST
             * check if it includes the "renegotiation_info" extension:
             */
            {
              boolean secure_negotiation = serverExtensions.containsKey(EXT_RenegotiationInfo);

              /*
               * If the extension is present, set the secure_renegotiation
               * flag to TRUE. The client MUST then verify that the length
               * of the "renegotiated_connection" field is zero, and if it
               * is not, MUST abort the handshake (by sending a fatal
               * handshake_failure alert).
               */
              if (secure_negotiation) {
                byte[] renegExtValue = (byte[]) serverExtensions.get(EXT_RenegotiationInfo);

                if (!Arrays.constantTimeAreEqual(
                    renegExtValue, createRenegotiationInfo(emptybuf))) {
                  this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
                }
              }

              tlsClient.notifySecureRenegotiation(secure_negotiation);
            }

            if (clientExtensions != null) {
              tlsClient.processServerExtensions(serverExtensions);
            }

            this.keyExchange = tlsClient.getKeyExchange();

            connection_state = CS_SERVER_HELLO_RECEIVED;
            break;
          default:
            this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
        }
        break;
      case HandshakeType.server_hello_done:
        switch (connection_state) {
          case CS_SERVER_HELLO_RECEIVED:

            // There was no server certificate message; check it's OK
            this.keyExchange.skipServerCertificate();
            this.authentication = null;

            // NB: Fall through to next case label

          case CS_SERVER_CERTIFICATE_RECEIVED:

            // There was no server key exchange message; check it's OK
            this.keyExchange.skipServerKeyExchange();

            // NB: Fall through to next case label

          case CS_SERVER_KEY_EXCHANGE_RECEIVED:
          case CS_CERTIFICATE_REQUEST_RECEIVED:
            assertEmpty(is);

            connection_state = CS_SERVER_HELLO_DONE_RECEIVED;

            TlsCredentials clientCreds = null;
            if (certificateRequest == null) {
              this.keyExchange.skipClientCredentials();
            } else {
              clientCreds = this.authentication.getClientCredentials(certificateRequest);

              if (clientCreds == null) {
                this.keyExchange.skipClientCredentials();

                boolean isTls =
                    tlsClientContext.getServerVersion().getFullVersion()
                        >= ProtocolVersion.TLSv10.getFullVersion();

                if (isTls) {
                  sendClientCertificate(Certificate.EMPTY_CHAIN);
                } else {
                  sendAlert(AlertLevel.warning, AlertDescription.no_certificate);
                }
              } else {
                this.keyExchange.processClientCredentials(clientCreds);

                sendClientCertificate(clientCreds.getCertificate());
              }
            }

            /*
             * Send the client key exchange message, depending on the key
             * exchange we are using in our CipherSuite.
             */
            sendClientKeyExchange();

            connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;

            /*
             * Calculate the master_secret
             */
            byte[] pms = this.keyExchange.generatePremasterSecret();

            securityParameters.masterSecret =
                TlsUtils.calculateMasterSecret(this.tlsClientContext, pms);

            // TODO Is there a way to ensure the data is really overwritten?
            /*
             * RFC 2246 8.1. The pre_master_secret should be deleted from
             * memory once the master_secret has been computed.
             */
            Arrays.fill(pms, (byte) 0);

            if (clientCreds != null && clientCreds instanceof TlsSignerCredentials) {
              TlsSignerCredentials signerCreds = (TlsSignerCredentials) clientCreds;
              byte[] md5andsha1 = rs.getCurrentHash(null);
              byte[] clientCertificateSignature =
                  signerCreds.generateCertificateSignature(md5andsha1);
              sendCertificateVerify(clientCertificateSignature);

              connection_state = CS_CERTIFICATE_VERIFY_SEND;
            }

            /*
             * Now, we send change cipher state
             */
            byte[] cmessage = new byte[1];
            cmessage[0] = 1;
            rs.writeMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.length);

            connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;

            /*
             * Initialize our cipher suite
             */
            rs.clientCipherSpecDecided(tlsClient.getCompression(), tlsClient.getCipher());

            /*
             * Send our finished message.
             */
            byte[] clientVerifyData =
                TlsUtils.calculateVerifyData(
                    tlsClientContext, "client finished", rs.getCurrentHash(TlsUtils.SSL_CLIENT));

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            TlsUtils.writeUint8(HandshakeType.finished, bos);
            TlsUtils.writeOpaque24(clientVerifyData, bos);
            byte[] message = bos.toByteArray();

            rs.writeMessage(ContentType.handshake, message, 0, message.length);

            this.connection_state = CS_CLIENT_FINISHED_SEND;
            break;
          default:
            this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
        }
        break;
      case HandshakeType.server_key_exchange:
        {
          switch (connection_state) {
            case CS_SERVER_HELLO_RECEIVED:

              // There was no server certificate message; check it's OK
              this.keyExchange.skipServerCertificate();
              this.authentication = null;

              // NB: Fall through to next case label

            case CS_SERVER_CERTIFICATE_RECEIVED:
              this.keyExchange.processServerKeyExchange(is);

              assertEmpty(is);
              break;

            default:
              this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
          }

          this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
          break;
        }
      case HandshakeType.certificate_request:
        {
          switch (connection_state) {
            case CS_SERVER_CERTIFICATE_RECEIVED:

              // There was no server key exchange message; check it's OK
              this.keyExchange.skipServerKeyExchange();

              // NB: Fall through to next case label

            case CS_SERVER_KEY_EXCHANGE_RECEIVED:
              {
                if (this.authentication == null) {
                  /*
                   * RFC 2246 7.4.4. It is a fatal handshake_failure alert
                   * for an anonymous server to request client identification.
                   */
                  this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
                }

                int numTypes = TlsUtils.readUint8(is);
                short[] certificateTypes = new short[numTypes];
                for (int i = 0; i < numTypes; ++i) {
                  certificateTypes[i] = TlsUtils.readUint8(is);
                }

                byte[] authorities = TlsUtils.readOpaque16(is);

                assertEmpty(is);

                Vector authorityDNs = new Vector();

                ByteArrayInputStream bis = new ByteArrayInputStream(authorities);
                while (bis.available() > 0) {
                  byte[] dnBytes = TlsUtils.readOpaque16(bis);
                  authorityDNs.addElement(
                      X500Name.getInstance(ASN1Primitive.fromByteArray(dnBytes)));
                }

                this.certificateRequest = new CertificateRequest(certificateTypes, authorityDNs);
                this.keyExchange.validateCertificateRequest(this.certificateRequest);

                break;
              }
            default:
              this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
          }

          this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
          break;
        }
      case HandshakeType.hello_request:
        /*
         * RFC 2246 7.4.1.1 Hello request This message will be ignored by the
         * client if the client is currently negotiating a session. This message
         * may be ignored by the client if it does not wish to renegotiate a
         * session, or the client may, if it wishes, respond with a
         * no_renegotiation alert.
         */
        if (connection_state == CS_DONE) {
          // Renegotiation not supported yet
          sendAlert(AlertLevel.warning, AlertDescription.no_renegotiation);
        }
        break;
      case HandshakeType.client_key_exchange:
      case HandshakeType.certificate_verify:
      case HandshakeType.client_hello:
      default:
        // We do not support this!
        this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
        break;
    }
  }