@Override public DTLSFlight getStartHandshakeMessage() { ClientHello message = new ClientHello(maxProtocolVersion, new SecureRandom()); // store client random for later calculations clientRandom = message.getRandom(); // the mandatory to implement ciphersuites, the preferred one should be first in the list if (ScProperties.std .getStr("PREFERRED_CIPHER_SUITE") .equals(CipherSuite.TLS_PSK_WITH_AES_128_CCM_8.toString())) { message.addCipherSuite(CipherSuite.TLS_PSK_WITH_AES_128_CCM_8); message.addCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8); } else { message.addCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8); message.addCipherSuite(CipherSuite.TLS_PSK_WITH_AES_128_CCM_8); } message.addCompressionMethod(CompressionMethod.NULL); // set current state state = message.getMessageType().getCode(); // store for later calculations clientHello = message; DTLSFlight flight = new DTLSFlight(); flight.addMessage(wrapMessage(message)); return flight; }
/** * Cancels any pending re-transmission of an outbound flight that has been registered previously * using the {@link #setPendingFlight(DTLSFlight)} method. * * <p>This method is usually invoked once an flight has been acknowledged by the peer. */ public void cancelPendingFlight() { if (pendingFlight != null) { pendingFlight.getRetransmitTask().cancel(); pendingFlight.setRetransmitTask(null); pendingFlight = null; } }
/** * A {@link HelloVerifyRequest} is sent by the server upon the arrival of the client's {@link * ClientHello}. It is sent by the server to prevent flooding of a client. The client answers with * the same {@link ClientHello} as before with the additional cookie. * * @param message the server's {@link HelloVerifyRequest}. * @return {@link ClientHello} with server's {@link Cookie} set. */ private DTLSFlight receivedHelloVerifyRequest(HelloVerifyRequest message) { clientHello.setCookie(message.getCookie()); // update the length (cookie added) clientHello.setFragmentLength(clientHello.getMessageLength()); DTLSFlight flight = new DTLSFlight(); flight.addMessage(wrapMessage(clientHello)); return flight; }
/** * Called when the client received the server's finished message. If the data can be verified, * encrypted application data can be sent. * * @param message the {@link Finished} message. * @return the list * @throws HandshakeException */ private DTLSFlight receivedServerFinished(Finished message) throws HandshakeException { DTLSFlight flight = new DTLSFlight(); message.verifyData(getMasterSecret(), false, handshakeHash); state = HandshakeType.FINISHED.getCode(); session.setActive(true); // received server's Finished message, now able to send encrypted // message ApplicationMessage applicationMessage = new ApplicationMessage(this.message.getBytes()); flight.addMessage(wrapMessage(applicationMessage)); // application data is not retransmitted flight.setRetransmissionNeeded(false); return flight; }
private ClientHello getClientHello(DTLSFlight flight) throws GeneralSecurityException, HandshakeException { Record record = flight.getMessages().get(0); return (ClientHello) record.getFragment(); }
/** * The ServerHelloDone message is sent by the server to indicate the end of the ServerHello and * associated messages. The client prepares all necessary messages (depending on server's previous * flight) and returns the next flight. * * @return the client's next flight to be sent. * @throws HandshakeException */ private DTLSFlight receivedServerHelloDone(ServerHelloDone message) throws HandshakeException { DTLSFlight flight = new DTLSFlight(); if (serverHelloDone != null && (serverHelloDone.getMessageSeq() == message.getMessageSeq())) { // discard duplicate message return flight; } serverHelloDone = message; /* * All possible handshake messages sent in this flight. Used to compute * handshake hash. */ CertificateMessage clientCertificate = null; ClientKeyExchange clientKeyExchange = null; CertificateVerify certificateVerify = null; /* * First, if required by server, send Certificate. */ if (certificateRequest != null) { // TODO load the client's certificate according to the allowed // parameters in the CertificateRequest clientCertificate = new CertificateMessage(certificates, session.sendRawPublicKey()); flight.addMessage(wrapMessage(clientCertificate)); } /* * Second, send ClientKeyExchange as specified by the key exchange * algorithm. */ byte[] premasterSecret; switch (keyExchange) { case EC_DIFFIE_HELLMAN: clientKeyExchange = new ECDHClientKeyExchange(ecdhe.getPublicKey()); premasterSecret = ecdhe.getSecret(ephemeralServerPublicKey).getEncoded(); generateKeys(premasterSecret); break; case PSK: String identity = ScProperties.std.getProperty("PSK_IDENTITY"); clientKeyExchange = new PSKClientKeyExchange(identity); byte[] psk = sharedKeys.get(identity); if (psk == null) { AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE); throw new HandshakeException( "No preshared secret found for identity: " + identity, alert); } LOGGER.info("Using PSK identity: " + identity); premasterSecret = generatePremasterSecretFromPSK(psk); generateKeys(premasterSecret); break; case NULL: clientKeyExchange = new NULLClientKeyExchange(); // We assume, that the premaster secret is empty generateKeys(new byte[] {}); break; default: AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE); throw new HandshakeException("Unknown key exchange algorithm: " + keyExchange, alert); } flight.addMessage(wrapMessage(clientKeyExchange)); /* * Third, send CertificateVerify message if necessary. */ if (certificateRequest != null) { // prepare handshake messages handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, clientHello.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, serverHello.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, serverCertificate.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, serverKeyExchange.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, certificateRequest.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, serverHelloDone.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, clientCertificate.toByteArray()); handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, clientKeyExchange.toByteArray()); // TODO make sure, that signature is supported SignatureAndHashAlgorithm signatureAndHashAlgorithm = certificateRequest.getSupportedSignatureAlgorithms().get(0); certificateVerify = new CertificateVerify(signatureAndHashAlgorithm, privateKey, handshakeMessages); flight.addMessage(wrapMessage(certificateVerify)); } /* * Fourth, send ChangeCipherSpec */ ChangeCipherSpecMessage changeCipherSpecMessage = new ChangeCipherSpecMessage(); flight.addMessage(wrapMessage(changeCipherSpecMessage)); setCurrentWriteState(); session.incrementWriteEpoch(); /* * Fifth, send the finished message. */ try { // create hash of handshake messages // can't do this on the fly, since there is no explicit ordering of // messages MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(clientHello.toByteArray()); md.update(serverHello.toByteArray()); if (serverCertificate != null) { md.update(serverCertificate.toByteArray()); } if (serverKeyExchange != null) { md.update(serverKeyExchange.toByteArray()); } if (certificateRequest != null) { md.update(certificateRequest.toByteArray()); } md.update(serverHelloDone.toByteArray()); if (clientCertificate != null) { md.update(clientCertificate.toByteArray()); } md.update(clientKeyExchange.toByteArray()); if (certificateVerify != null) { md.update(certificateVerify.toByteArray()); } MessageDigest mdWithClientFinished = null; try { mdWithClientFinished = (MessageDigest) md.clone(); } catch (CloneNotSupportedException e) { LOGGER.severe("Clone not supported."); e.printStackTrace(); } handshakeHash = md.digest(); Finished finished = new Finished(getMasterSecret(), isClient, handshakeHash); flight.addMessage(wrapMessage(finished)); // compute handshake hash with client's finished message also // included, used for server's finished message mdWithClientFinished.update(finished.toByteArray()); handshakeHash = mdWithClientFinished.digest(); } catch (NoSuchAlgorithmException e) { LOGGER.severe("No such Message Digest Algorithm available."); e.printStackTrace(); } return flight; }