/** * The ServerKeyExchange message is sent by the server only when the server {@link * CertificateMessage} (if sent) does not contain enough data to allow the client to exchange a * premaster secret. Used when the key exchange is ECDH. The client tries to verify the server's * signature and on success prepares the ECDH key agreement. * * @param message the server's {@link ServerKeyExchange} message. * @throws HandshakeException if the message can't be verified. */ private void receivedServerKeyExchange(ECDHServerKeyExchange message) throws HandshakeException { if (serverKeyExchange != null && (serverKeyExchange.getMessageSeq() == message.getMessageSeq())) { // discard duplicate message return; } serverKeyExchange = message; message.verifySignature(serverPublicKey, clientRandom, serverRandom); // get the curve parameter spec by the named curve id ECParameterSpec params = ECDHServerKeyExchange.NAMED_CURVE_PARAMETERS.get(message.getCurveId()); if (params == null) { AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE); throw new HandshakeException("Server used unsupported elliptic curve for ECDH", alert); } ephemeralServerPublicKey = message.getPublicKey(params); ecdhe = new ECDHECryptography(ephemeralServerPublicKey.getParams()); }
/** * 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; }