TLS SSL handshakes explained

TLS (SSL) handshakes may not sound very familiar, but they are one of the most critical parts of securely connecting to a website. In a nutshell, they establish the parameters of a connection between two parties and enable communication. Read on if you would like TLS (SSL) handshakes explained in more detail.


TLS stands for Transport Layer Security, while SSL is short for Secure Sockets Layer. People often use the terms interchangeably, but TLS is really just the updated version of SSL.

The TLS handshake is a critical part of the overall TLS protocol. It’s a subprotocol that is responsible for securely setting up a connection. Once this is established, data is sent in a safe manner over the record layer, which is the other main subprotocol of TLS.

The TLS handshake plays an important role in much of your online security. It’s a key part of setting up secure connections in everything from VOIP calls to your email login page.

What is TLS (SSL)?

Transport Layer Security (TLS) is a protocol that can provide security across networks like the Internet. If we didn’t have security protocols, we would run into a range of problems, especially when transporting sensitive and important data.

Without security protocols:

  • There would be no way to protect the intimate details you had shared in your private messages.
  • You wouldn’t know if you were really on your bank’s website or a fraudulent copy.
  • You couldn’t prevent your messages from being changed by attackers before they were delivered to your recipients.

TLS is one of the mechanisms that was developed to address these issues, and it’s able to:

  • Authenticate other parties.
  • Show whether data maintains its integrity.
  • Keep data confidential.

TLS operates between the network and application layers of the OSI model. The TLS (SSL) handshake is one layer of the TLS protocol, and its purpose is to authenticate the other party and establish secure parameters for the data exchange.

The other major layer is the TLS record, which uses the parameters set up in the handshake to safely send the data between the parties. It transmits this data in packets called records.

A brief background on TLS (SSL)

TLS began its life through a joint initiative from several US Government agencies and companies in the eighties. It evolved into various versions of Secure Sockets Layer (SSL) in the 1990s.

In 1999, the updated version of the protocol had its name switched to Transport Security Layer, as part of what was essentially a compromise between Microsoft and Netscape. The latest version is TLS 1.3, which the latest versions of browsers like Chrome and Firefox use by default.

The uses of TLS (SSL)

These days, your data often travels over TLS whenever you are online. TLS provides the security in HTTPS, which is used by most major websites to protect your data. It’s especially common on login pages and in online banking. You can tell when a website is secured by TLS, because the URL will begin with https rather than http, and there will be a little lock icon to the left of the address bar.

TLS can also be implemented over a number of other Transport Layer protocols to provide security. These include SMTP, FTP, XMPP and others. When TLS is in place alongside these protocols, it can protect emails, file transfers and instant messaging, respectively. TLS can also be used to safeguard data transmitted over VPNs.

What is a handshake?

When you go online, you probably don’t give a lot of thought to what goes on under the hood to make it all happen. You probably care far more about the memes or YouTube videos than the complex technical details of how they end up on your phone.

While it may be instantaneous, a whole lot has to happen for your device—the client—to connect to a website like Facebook, which acts as the server. Our information ecosystem is composed of a huge mishmash of different standards, protocols, procedures, ciphers and a whole lot more.

The client—your phone or computer—will support a bunch of these in an order of preference, while it won’t be compatible with others. Facebook’s server will do the same, but many of its protocols, standards, ciphers and all of the rest may be different to those on your phone or computer. Some clients and servers won’t have the latest versions of particular cryptographic algorithms, while others will refuse to connect in an insecure manner.

Essentially, when the client and server meet, they have to sort through this mess of different systems to find an acceptable and mutually intelligible way of communicating. This is what the handshake is for. It may be helpful to think of the handshake through an analogy:

Travelers from distant lands

Picture two people from distant lands communicating for the first time. They come from extremely different cultures, with their own languages, customs, manners and much more. However, both people are extremely well-traveled and have picked up many of the various systems from the places they have traveled through.

The two people want to do business together, so they need to be able to communicate clearly and effectively. But how can they do so when they have so many different options to choose from?

If they don’t agree beforehand, they may confuse the German ‘nein’ for the number ‘nine’. A friendly Western thumbs up could be misinterpreted as the offensive Iranian gesture.

What if they wanted to keep their communications confidential? Should they use a simple Caesar cipher, where every letter is shifted one place to the right: A equals B, B equals C, C equals D, and so on? This would make a word like ‘Hello’ become ‘Ifmmp’. Or, should they use a system like Pig Latin, where ‘Hello’ would really be ‘ello-hay’?

As you can see, they have a whole lot of options to choose from, but the important thing is that they both agree on which systems they will use, and how they will use them. A good way to decide is to just list out all of the options they know, in order of preference.

Traveler A could lay out a list like this:

  • Alphabet
    • Roman
    • Arabic
    • Cyrillic
  • Language
    • English
    • Spanish
    • Arabic
    • Russian
  • Gestures
    • Western
    • South Asian
  • Ciphers
    • Pig Latin
    • Caesar cipher
  • Unit of measurement
    • Metric
    • Imperial

Traveler B could then go through the list and choose their preferences from the available options. They might send back a message with their choices, for example:

  • Alphabet – Roman
  • Language – Spanish
  • Gestures – South Asian
  • Cipher – Pig Latin
  • Unit of measurement – Metric

Once traveler A receives Traveler B’s message, they will know exactly how to communicate in their future correspondence. They could clearly discuss or send letters about their business dealings, without being muddled up. The communications wouldn’t be encrypted with a cipher that the other party couldn’t decrypt, nor would they expectantly end up with 1,000lb of wheat when they really wanted 1,000kg.

Handshaking: Establishing the communication parameters

The scene that we have outlined above is analogous to what happens during a more technical handshake. The only major difference is the types of parameters that are used in computer communications. These will vary from protocol to protocol, but they can include things like:

  • Coding alphabet
  • Interrupt procedures
  • Ciphers
  • Keys
  • Compression
  • Parity
  • Message size

Some handshaking procedures may be relatively simple, while others may include many separate steps and parameters that must be decided on. The important thing is that by the end, the two parties end up with a mutually agreed-upon means of communicating, which they can then use in their future interactions.

TLS versions

As of December 2023, 66.2% of the world’s most popular websites (according to Alexa, and reported by SSL Pulse) support TLS 1.3. The same source states that 99.9% of these websites also support the previous version TLS 1.2, while 31.8% support TLS 1.1, and 29.5% support TLS 1.0.

The latest versions of most modern browsers have disabled their default support for TLS versions 1.0 and 1.1. This includes Google Chrome, Microsoft Edge, Apple’s Safari and Mozilla Firefox. A lot of other popular software has done the same.

TLS 1.3 will be the focus of our article, because it’s the latest and most secure version, and the world is slowly moving toward it. While the other versions accomplish much the same task, there are several key differences that make them less efficient and more vulnerable to attacks than TLS 1.3.

The TLS (SSL) handshake: The short version

The client connects to the server and sends a Client Hello Message. This message includes the parameters it supports, such as:

  • The versions of TLS it’s compatible with.
  • A nonce.
  • The cipher suites it can use for encryption and authentication.
  • A number of extensions that add functionality. This includes things like the key sharing parameters and a number of extras.

In anticipation that the server will accept many of the same parameters that the client prefers, the Client Hello also includes information that helps the server establish a secure connection as part of its response.

When this is the case, it saves on a round of messages. This makes TLS 1.3 faster than previous versions of the protocol. If the server cannot meet the client’s expectations, they will have to renegotiate.

When the server receives the Client Hello message, it sends the client a Server Hello message in response. This includes its choices from the Client’s list of preferences:

  • The version of TLS.
  • A nonce.
  • The cipher suite.
  • It’s key sharing parameters.

Each of the above aspects are sent as plaintext, but the Server Hello also includes additional messages that have been encrypted with keys derived from information aspects of the Server Hello message. These include:

  • The server’s digital certificate.
  • A Certificate Verify message that contains a digital signature from the server.
  • A Finished message, which contains a HMAC that the client can use to verify the authenticity of the message.

Once the server sends the Finished message, it can begin sending application data to the client. This application data is encrypted with a different set of keying material.

Unless the client is also authenticating itself to the server (in which case it must send its certificate and the Certificate Verify message), all the client has to do is send its Finished message, which is also encrypted. It can then start sending application data to the server, although this is encrypted with different keying material. At this point, the TLS handshake is complete.

The TLS (SSL) handshake in depth

Let’s take a look at what a typical TLS (SSL) handshake looks like. When you visit, your browser acts as the client, which connects to the server, ultimately bringing our web page onto your screen.

Under TLS, when the client wants to connect to the server, the first step is for the Client to send the Server a Client Hello message.

The Client Hello

A Client Hello message looks like this in the WireShark packet analyzer:

tls 1.3 wireshark

As you can see, it looks a little complicated. We’ll just discuss the most important parts.

TLS version

In the second line, it says TLSv1.3 Record Layer: Handshake Protocol: Client Hello. However, if we skip down a couple of lines, we see Version: TLS 1.0 (0x0301). You may also notice that just a few lines further down, it says Version: TLS 1.2 (0x0303). The bracketed numbers behind each version are just codes for their respective versions.

Looking at these conflicting numbers, you might be confused about which version of TLS the Client Hello message actually wants to use. The answer lies in the depths of the RFC, the technical documentation that sets out the TLS 1.3 standard. In section 4.2.1, it discusses the Supported Versions extension (TLS extensions can add additional functionality and capabilities, but they aren’t required).

The RFC essentially says that when the Supported Versions extension is present, the server should ignore the other versions mentioned in the Client Hello message, and select a version from the extension instead.

If we open up the Supported Versions extension in the fifth line from the bottom, we see:

tls 1.3 wireshark 1

Here, it shows us that only TLS versions 1.3 and 1.2 are supported. The version that ends up being used will depend on what the server supports.

Message length

Heading back toward the top of the first image, we see Length in both the fifth and eighth lines. The first one says 512, while the second says 508. This is just the size of their respective messages in bytes, and the discrepancy is because the TLS record header itself takes up 4 bytes.


The highlighted field in the first capture says Random, and the data next to it is a 32-byte nonce. This is an arbitrary number that is only ever used once. Its main purpose is to stop replay attacks.

In essence, the addition of this unique number prevents an attacker from being able to send the client the very same packets that it had received in a previous connection. The Random nonce is generated by a secure random number generator.

Session ID Length and value

After this, we have two separate fields, Session ID Length and Session ID. The Session ID is a 32-byte number, and in previous versions, it was used to resume connections when the authentication process had already been completed in an earlier connection. In TLS1.3, the Pre-Shared Key extension described below is used instead.

List of Cipher Suites

The Cipher Suites are used to encrypt and authenticate the connection. In combination with TLS’ other cryptographic mechanisms, these Cipher Suites help to give the data confidentiality, authenticity and integrity. When we expand the cipher suites section, we can see all of the accepted cipher suites, listed in the order of the client’s preference:

tls wireshark 2

While there are 18 different suites listed, the RFC states that TLS 1.3 only supports five different cipher suites:

  • TLS_AES_128_GCM_SHA256
  • TLS_AES_256_GCM_SHA384
  • TLS_CHACHA20_POLY1305_SHA256
  • TLS_AES_128_CCM_SHA256
  • TLS_AES_128_CCM_8_SHA256

If you look closely, you will see that the first three in both lists are the same, just in a different order. The last two from the RFC are not supported by the browser in our example, which is why they are not included in the Wireshark capture. The reason the capture shows 15 different cipher suites is to provide backward compatibility with older versions of TLS.

If only the five TLS 1.3 cipher suites were included, the browser would not be able to securely connect to browsers that used TLS versions 1.2 or earlier. Given that many servers still don’t use the latest version, this would cause significant compatibility issues.

Each cipher suite is made up of a symmetric-key cipher and a HKDF hash pair. TLS 1.3 only uses authenticated encryption with associated data (AEAD) ciphers as its symmetric-key ciphers. These algorithms provide integrity, authenticity and confidentiality to data at the same time.

HKDFs are key derivation functions (KDFs) that are based on hash-based message authentication codes (HMAC). TLS uses HKDFs to extract entropy and expand the generated outputs as part of the key derivation process.

As an example of how the above cipher suites work, TLS_AES_128_GCM_SHA256 uses an AEAD cipher mode of AES-128 GCM, with the SHA-256 hash function as the HKDF. This suite is stronger than many of the suites used in previous versions of TLS.

Key exchange method

Another aspect that is critical for establishing a secure connection is the key exchange. In TLS 1.3, this is achieved through extensions. Either the Key Share extension, the Pre-shared Key extension (this has not been used in the example we are examining through Wireshark), or both.

The client and server will both have key pairs that are composed of both a public and private key, which are mathematically linked.

When these public-private key pairs are used alongside the elliptic-curve Diffie Hellman key agreement protocol, it allows the client and the server to establish a shared secret that they can use to encrypt future communications. Due to the elaborate math that underlies this exchange, the two parties can securely establish the key, even if an attacker is eavesdropping on them.

tls 1.3 wireshark 8

In our Wireshark capture, next to the first Key Share Entry: Group, we see a code, x25519, followed by Key Exchange Length: 32. The code indicates Curve25519, an elliptic curve with 128 bits of security, which is used as part of the elliptic-curve Diffie-Hellman key agreement scheme. Key Exchange Length is exactly what it sounds like, the length of the key in the exchange.

Beneath this, we have Key Exchange: efifecba2658 etc. This is the key that the client wants to use with Curve22519, although the end of it has been cut off in the Wireshark capture.

Underneath this, you will see Key Share Entry: Group followed by the secp256r1 code. This refers to Standards for Efficient Cryptography, while the P stands for Parameters. secp256r1 is simply another elliptic curve with 256 bits of security.

Curve25519 and secp256r1 are the two options that the client has provided for the key exchange. Because x25519 comes first, Curve25519 is the client’s preferred choice.

When the Key Share extension is used, the client sends the server one or more of its public keys (but not their private counterparts) as part of the Client Hello message.

In this case, the key for Curve25519 and the key for the secp256r1 curve have been sent in anticipation that the server will support at least one of these options. If the server does, it can begin encrypting part of its response when it sends the Server Hello message (we will discuss this in detail further down in the article).

TLS 1.3’s Key Share extension is a significant departure from the mechanisms used in version 1.2 and those that came before it. In these earlier versions, there was an additional Client Key Exchange message sent after the Client Hello. This meant that a larger portion of the handshake was sent as plaintext, leaving it visible to attackers.

By assuming that the server will accept one of the client’s preferences and eliminating the need for the Client Key Exchange message, TLS 1.3 is also faster and more efficient than previous versions.

In cases where the server cannot meet the client’s expectations, the server sends a Retry Hello Request message to attempt alternative means of forming a secure connection. In most cases, TLS 1.3’s presumption ends up saving a round of messages, helping to speed up the exchange. Overall, this ends up being more efficient, even if servers sometimes need to send RetryHelloRequest messages.

The other major option for key sharing is the Pre-Shared Key extension. The client and server can only use this if a pre-shared key has already been established. This can be done in previous connections between the two parties, or through an out-of-band mechanism.

When the client and server have previously established a pre-shared key, the Pre-Shared Key extension allows the client to list the pre-shared key options that are available to the server. In our Wireshark capture, pre-shared keys have not been used, so this extension had no effect on the connection we are observing.

Signature algorithms

TLS 1.3 contains two more important extensions that determine which signature algorithms will be used to create the digital signatures that provide authentication and integrity to the data.

Digital signatures are much like your handwritten signature. You sign receipts, contracts and other documents with your name in a way that’s hard to forge. This gives you a simple and mostly reliable way to authenticate that it is truly you who is signing the information.

Your signature infers that you have verified that the details of the document were correct at the time of signing, showing that the data has integrity.

Digital signatures perform similar roles, allowing us to verify the authenticity and integrity of information. The main difference is that they use a mixture of public-key algorithms and hashing functions to do so.

The Signature Algorithms extension was introduced in TLS 1.2, and it indicates which types of signature algorithms the client supports. If the client wants the server to authenticate itself with a certificate, the client must send the Signature Algorithms extension as part of its Client Hello message.

The signature algorithms listed in the Client Hello message can influence the type of certificate that the server will send to the client as part of its Certificate message (this happens later on, as part of the Server Hello).

The server will also choose its preferred signature algorithm from the client’s list, which is used as part of the Certificate Verify message, which is another part of the Server Hello message. We will discuss the Certificate and Certificate Verify messages in greater detail when we go into the specifics of the Server Hello message later on.

The second extension is the Signature Algorithms Cert extension, which was added in TLS 1.3. It allows implementations to clearly show their capabilities in situations where they support one list of algorithms for certificates and a separate set for TLS itself.

If an implementation uses the same set of algorithms for certificates and TLS itself, then the Signature Algorithms Cert can be omitted. If the Signature Algorithm Cert extension isn’t present in a message, then the Signature Algorithm extension is applied to both the signatures in certificates as well as TLS itself.

The accepted signature algorithms of TLS 1.3 include the following, in order of preference:

  • rsa_pkcs1_sha256(0x0401)
  • rsa_pkcs1_sha384(0x0501)
  • rsa_pkcs1_sha512(0x0601)
  • ecdsa_secp256r1_sha256(0x0403)
  • ecdsa_secp384r1_sha384(0x0503)
  • ecdsa_secp521r1_sha512(0x0603)
  • rsa_pss_rsae_sha256(0x0804)
  • rsa_pss_rsae_sha384(0x0805)
  • rsa_pss_rsae_sha512(0x0806)
  • ed25519(0x0807)
  • ed448(0x0808)
  • rsa_pss_pss_sha256(0x0809)
  • rsa_pss_pss_sha384(0x080a)
  • rsa_pss_pss_sha512(0x080b)

The following algorithms are also included for legacy purposes:

  • rsa_pkcs1_sha1(0x0201)
  • ecdsa_sha1(0x0203)

As an example of what each of these codes actually means, let’s take the following as an example:

  • rsa_pkcs1_sha256(0x0401)

It includes the letters rsa, because it’s based on the RSA cryptosystem, operating under the properties defined in the PKCS #1 family of Public Key Cryptography Standards. This is why the pkcs1 comes next. Under this particular signature algorithm, the message is hashed with the SHA-256 hash function, which explains the sha256. Finally, the bracketed 0x0401 is simply a code for this particular signature algorithm.

Compression methods and other extensions

If you’ve been following closely, you may have noticed that we neglected to touch on many of the lines in our Wireshark capture of the Client Hello message.

This includes Compression Methods and a bunch of extensions. Compression Methods is simply the type of Compression that the client supports. Compression can make the connection vulnerable to side-channel attacks, so TLS 1.3 always sets Compression Methods to null, which prevents compression from being used.

While the other extensions add additional functionality and can be important, it isn’t necessary to go into them to understand a basic TLS handshake.

Server Hello

When we inspect the server’s response to the Client Hello message in Wireshark, we see the following:

tls 1.3 wireshark 3

In the second line, it tells us that it’s the Handshake Protocol’s Server Hello message.

TLS version

Again, we have the seeming conflict about the versions. This time it says TLS 1.2 throughout the capture, but when we open up the Supported Versions extension toward the middle of the diagram, we see that it says TLS 1.3 (0x0304):

tls 1.3 wireshark 4

If you remember our discussion on versions from the Client Hello message, you will recall that the Supported Versions extension supersedes other mentions of the version. This means that in this case, the server also supports TLS 1.3, and this is what it has chosen for the connection.

Message Length

When we compare the Length fields on the fifth and eighth lines, we again notice the four-byte discrepancy, just like in the Client Hello message. Once more, this is the length of the record header.


The Server Hello message contains its own 32-byte nonce. This Random number is used to help prevent replay attacks. This nonce is also derived from a secure random number generator.

Session ID Length and value

If you compare the Wireshark capture of the Client Hello message against the Server Hello message, you can see that the session ID is the same number:


TLS 1.3 no longer uses the session ID for re-establishing connections, because it can now use the Pre-Shared Key extension instead. The Session ID is kept in TLS 1.3 for legacy reasons, and it’s simply a copy of the session ID that was sent by the client.

Cipher suite

If you look at the Cipher Suite, you can see that it’s no longer expandable. This is because the Server Hello message doesn’t contain a list of its preferred suites like the Client Hello message does. Instead, it features the Cipher Suite it chose from the list the client offered it. In this case, it’s:

TLS_AES_128_GCM_SHA256 (0x1301)

This is also the client’s preferred Cipher Suite. Refer to the Cipher Suite section of the Client Hello if you need a reminder of which algorithms this mishmash of characters represents.

Key Share extension

The server will have its own key pairs, comprised of both a public and a private key, just like the client does. The server’s keys are just as important for establishing a shared secret that can be used to secure the connection.

As part of the Key Share extension, the server will examine the key exchange options sent in the Client Hello message, then makes its choice:

tls 1.3 wireshark 6

In this case, it’s Curve25519, indicated by the x25519. The server also sends a public key from its key pair. Part of this public key is displayed after Key Exchange:


Change Cipher Spec Protocol: Change Cipher Spec

Just below the Key Share extension, you will see something that wasn’t referenced in the Client Hello message, the Change Cipher Spec Protocol: Change Cipher Spec field. It’s included in the Server Hello for compatibility with older versions. While it mentions TLS 1.2, it does not apply in this connection.

Application Data Protocol

The other important parts of the Server Hello message take place in the Application Data Protocol fields at the bottom of the Wireshark capture. However, they have been encrypted by the server, which means that we can’t actually see what is going on with Wireshark:

The encrypted data is the jumble of numbers and letters that follow the Encrypted Application Data fields.

The encrypted data includes the other extensions, the Certificate message, the Certificate Verify message and the Finished message. While we can’t see what’s happening in Wireshark, we can look at the RFC to get an understanding of what happens underneath. Before we discuss this encrypted data, let’s cover how the keys that encrypt the data are generated.

Key generation

By this stage of the connection, the server already has all of the information it needs to calculate the keys for this step of the exchange. This includes:

  • The client’s public key, which it received in the Client Hello message. Let’s just make up a random number, to give you an idea of what this would look like:


  • The server’s private key, which it has not shared with anyone. The server sends its matching public key to the client as part of the Key Share extension, which we mentioned just above. Again, let’s make up the server’s private key:


  • The messages that the server has already received or created. These messages will be put through hash functions, and the resulting hashes will become inputs at various stages of the process.

In essence, a hash function can take inputs of any size—whether it’s something book-length or a single letter—and return a string of a fixed length, which can’t be reversed to figure out the original input.

The same input always returns the same output, and it is statistically impractical for two different inputs to return the same input. These properties make hashes incredibly useful in cryptography.

When the client generates its keys, it uses the server’s public key (which it receives as part of the (Key Share extension in the Server Hello message), it’s own private key (it sent the matching public key as part of the Client Hello message), and hashes of similar messages.

The client and the server’s keys are derived using the process explained in the TLS 1.3 key schedule section. These steps can seem pointlessly complex, and it’s easy to miss the significance of what is happening. So let’s take a step back and analyze the fundamental problem that the whole process solves.

Creating a secure connection over an insecure channel

Let’s say you’re an activist in the pre-internet days, with critical information that you want to get to a journalist across the country. The problem is that you are both being targeted by the authorities, and they are carefully monitoring your communications.

You could send the journalist the information in a letter, but chances are it would get opened by the police, and when they discovered the information it would endanger both you and the journalist. The same goes if you use the phone—there could be a wiretap.

One option would be to code the information in the letter, so that even if the authorities found it, they wouldn’t know what it meant. But then you would also need some way to send the code to the journalist so that they would be able to read it. If you sent the code as a separate letter, the authorities could intercept it as well, and use it to decode the message. This could still get you in trouble.

The problem boils down to this: If you do not already have a secure channel, how can you safely exchange the information needed to set up a secure channel? Anyone monitoring the channel could use this very same information to decrypt any future communications that you encrypted.

The same issue applies to online communication. How can you set up the keys for secure communication over an insecure channel? It seems like an almost impossible problem to solve.

Thankfully, cryptographers did the hard work for us, and through the likes of public-key cryptography, the Diffie-Hellman key agreement scheme and the other mechanisms we will describe, the TLS handshake is able to establish a secure connection over an insecure channel.

TLS 1.3 key schedule

The key schedule uses the HKDF Extract and Expand functions with the hash function that was selected as part of the Cipher Suite. The keys are established along the lines of the following diagram:

TLS 1.3 11

There are three rounds of the HKDF Extract and Expand functions, each forming keys for different stages of the connection. The HKDF Extract function has two inputs:

  • The salt
  • The keying material

The output of each HKDF Extract function is a Secret, which becomes one of the inputs for the HKDF Expand function. The other input in the HKDF Expand function is a hash of various messages that have been sent between the client and server.

If you look at the diagram, the salt at the beginning of the process is the 0 at the top, while the first piece of keying material is the Pre-Shared Key (we will explain this in a moment) at the top left.

In the next round, the salt is the Derive Secret that was produced as the result of the first round of HKDF Extract and Expand functions. In this second round, the keying material is the Shared Secret, which we will also discuss in a moment.

In the third round, the salt is this second Derive Secret that was produced by the previous round of HKDF Extract and Expand functions. The keying material is the 0.

First round: Early Secret

If the client and server have already established a pre-shared key in an earlier connection, this key acts as the initial keying material. When there is no pre-shared key, its input is replaced with a string of zeros that is the same length as the hash. The 0 that acts as the first salt is also a hash-length string of zeros.

In our case, since there is no pre-shared key in place, both inputs into the HKDF Extract function are strings of zeros. This output is our Early Secret, which is then fed into the HKDF Expand function alongside hashes of different messages to yield each of the early secrets. The messages that are hashed, and their resulting secrets are:

  • A string of zeros – Binder Key
  • The Client Hello message – Client Early Traffic Secret
  • The Client Hello message – Early Exporter Master Secret
  • A string of zeros – The Derive Secret

Because there is no pre-shared key in our example and there will be no early encryption, all of these secrets are irrelevant. However, they are still produced because the process is the same every time, regardless of whether or not there is a pre-shared key.

Second round: Handshake Secret

In this round, our inputs into the HKDF Extract function are the Derive Secret (the salt) produced by the previous round, as well as a Shared Secret (the keying material) that is calculated by running the client’s public key and the server’s private key through the elliptic-curve Diffie-Hellman key agreement scheme. As we mentioned in the Key Share extension section, the curve in this example is Curve25519.

So let’s take the client’s public key, which the server received in the Client Hello:


And the Server’s private key, which it keeps to itself (although it sent the matching public key to the client):


After being run through the scheme, let’s say it gives us a value of:


The above value is the Shared Secret. It’s a critical part of how the whole operation works. Due to the strange math behind public-key cryptography and the Diffie-Hellman key agreement scheme, this is a value that both the client and the server can compute, without ever having to send it to each other.

This shared secret is crucial for both parties being able to set up a secure connection, and it allows them to do so even if an attacker may be watching the connection. Later on, when the client generates its keys, it is able to calculate this very same Shared Secret from the server’s public key and its own private key.

So, our inputs are the Derive Secret, plus this Shared Secret. They are put through the HKDF Extract function, which results in the Handshake Secret. The Handshake Secret goes through the HKDF Expand function alongside hashes of several different messages to produce the secrets for this round. The messages that are hashed, as well as their respective outputs are:

  • The Client Hello and the Server Hello – Client Handshake Traffic Secret
  • The Client Hello and the Server Hello – Server Handshake Traffic Secret
  • A string of zeros – Derive Secret

Third round: Master Secret

In this round, our salt is the Derive Secret from the previous round, and our keying material is a string of zeros. These values are put through the HKDF Extract function, which results in the Master Secret.

This Master Secret is then run through the HKDF Expand function alongside the hashes of the following messages to give us their respective secrets:

  • All of the messages from the Client Hello up until the server’s Finished message (see the Finished section for more details on this message) – Client Application Traffic Secret
  • All of the messages from the Client Hello up until the server’s Finished message – Server Application Traffic Secret
  • All of the messages from the Client Hello up until the server’s Finished message – Exporter Master Secret
  • All of the messages from the Client Hello up until the server’s Finished message – Resumption Master Secret.

Each of these keys have roles in the handshake process, but for the sake of simplicity, we won’t cover them all.

RFC 8446

Now that you have seen where the Secrets come from, we can take a look at the data that is encrypted. Although we can’t see inside the encryption, RFC 8446 gives us a good overview of the TLS handshake in section 2. Protocol Overview. We have modified it slightly to add a little more clarity:

 Client                                           Server

Key  ^ ClientHello
Exch | + key_share*
ange | + signature_algorithms*
     | + psk_key_exchange_modes*
     v + pre_shared_key*       -------->
                                            ServerHello  ^ Key
                                           + key_share*  | Exch
                                      + pre_shared_key*  v ange
                                  {EncryptedExtensions}  ^  Server
                                  {CertificateRequest*}  v  Params
                                         {Certificate*}  ^
                                   {CertificateVerify*}  | Auth
                                             {Finished}  v
                               <--------  [Application Data*]
     ^ {Certificate*}
Auth | {CertificateVerify*}
     v {Finished}              -------->
       [Application Data]      <------->  [Application Data]

              +  Indicates noteworthy extensions sent in the
                 previously noted message.

              *  Indicates optional or situation-dependent
                 messages/extensions that are not always sent.

              {} Indicates messages protected using keys
                 derived from a [sender]_handshake_traffic_secret.

              [] Indicates messages protected using keys
                 derived from [sender]_application_traffic_secret_

On the left we have the client, and on the right we have the server. The arrows indicate where the data is sent to.

The top left cluster shows the important details of the Client Hello message, which was the first one we delved into. This includes the Key Share, Signature Algorithms, Pre-Shared Key Exchange Modes and the Pre-Shared Key.

Our Wireshark capture used the Key Share extension for the key exchange, so the final two extensions are irrelevant to our overall example.

Jumping down, on the right we have our Server Hello message. We can see it includes the Key Share extension, where the server chooses its preferred method from the client’s list and sends the relevant public key.

Next is the Pre-Shared Key extension, but once more, this isn’t relevant to our particular example.

We have already discussed each of the messages that have been listed so far, but if you look toward the bottom of the diagram, you will see a legend that lays out what the various symbols mean. It says that the curly brackets {} ‘Indicates messages protected using keys derived from a [sender]_handshake_traffic_secret.’ We have also bolded the relevant fields in the diagram for emphasis.

This means that the following aspects of the Server Hello message are all sent to the client in an encrypted manner:






The [sender] in the legend refers to either the client or server. In this case, each of the above fields has been encrypted by keying material derived from the Server Handshake Traffic Secret. This Secret was produced in the second round of the key schedule, based on the prior inputs as well as the hash of both the Client Hello and Server Hello messages. The keys are computed by inserting the Server Handshake Traffic Secret into HKDF Expand functions alongside other inputs.

If you scroll back to the Wireshark capture of the Server Hello, you will notice that none of the sections enclosed with curly brackets {} can be seen in it. Instead, they are part of the application data that has been encrypted. In previous versions of TLS, this information was sent as plaintext.

Encrypted Extensions

The Encrypted Extensions in the Server Hello message are responses to the extensions in the Client Hello message. The Client Hello message included all of these extensions as plaintext, which allowed us to use Wireshark to examine the lists of options that the client offered the server.

Because the responses are encrypted in the Server Hello message, we cannot see which choices the server selected. Let’s just assume that the server accepted each of the client’s first preferences.

Certificate Request

The next aspect is Certificate Request, which is sometimes used by the server to authenticate the client as well. It’s not as common to authenticate clients, because a fraudulent visitor is generally less dangerous to a website than a fraudulent website is to a visitor.

When the Server sends a Certificate Request as part of its Server Hello message, it outlines its desired parameters that the client must meet to authenticate itself and prove that it is really who it says it is.

What are certificates?

The Certificate portion of the Server Hello is the first step for the server to authenticate itself to the client in TLS. But if we jump in too quickly, it might get a little confusing.

First, let’s back up and give a brief explainer on what certificates actually are, and the role that they play. If the whole point of TLS is to secure our data and give us confidentiality, integrity and authenticity, then there’s one important thing we need to consider. When we make a connection to another party, how do you know that they are really who they say they are?

For example, when you connect to Comparitech, how can you know that you are really visiting us, and not some scam website? It wouldn’t matter how secure your connection was if the person on the other side was really a fraud or a cybercriminal.

There are a few different ways we can resolve this issue, but TLS uses public-key certificates. Essentially, Comparitech goes to a trusted body called a Certificate Authority. Common Certificate Authorities include DigiCert, GlobalSign and Symantec. Comparitech brings one of these organizations a range of documents that prove it’s Comparitech and that it owns

The Certificate Authority looks these over, and because Comparitech is really the entity that it claims to be, the Certificate Authority issues it a certificate. This certificate includes things like the name of the website, a public key and a digital signature from the Certificate Authority. The digital signature indicates that the Certificate Authority has verified that Comparitech is who it says it is, and vouches for Comparitech.

As long as you trust the Certificate Authority and the certificate hasn’t expired, you can feel safe when connecting to Comparitech, because the Certificate Authority has verified it for you. You can check up on Comparitech’s certificate by clicking the lock icon to the left of the address bar, then following the menu options.

Now that we’ve explained how certificates work in general, it’s time to get back to looking at what’s going on in the TLS handshake. When your web browser—the client—begins connecting to a server, it wants to know that the site is legitimate. It wants to protect you from accidentally going to a fraudulent website. Legitimate websites also want your browser to connect to them instead of directing you to an impostor. So how do certificates work in the context of TLS 1.3?


Unless pre-shared keys are being used (via the Pre-Shared Key extension), the server must send a Certificate message as part of its Server Hello. The certificate includes the server’s hostname, a public key, and a digital signature from a Certificate Authority.

As we just mentioned, the Certificate Authority’s signature shows that it has verified the identity of the server. As long as your client trusts the Certificate Authority, it can be confident that the public key really does belong to the host, and the server is actually who it says it is.

The public key on this certificate has a matching private key, which the server keeps secret from everyone else. This will become important in the following section.

The certificate can also include additional extensions, but going into them would unnecessarily complicate our explanation.

Certificate Verify

The job of the Certificate Verify message is to prove that the server actually owns the certificate it sends to the client. To do this, it first takes the hash of both the Client Hello and Server Hello messages, then signs it with the private key that matches the public key on the certificate. This digital signature is computed with whichever signature algorithm the server selected from the client’s list in the Signature Algorithms extension.

When the client receives the Certificate Verify message, it must verify the signature. It does this by throwing both the digital signature (sent in the server’s Certificate Verify message) and the public key (from the server’s certificate) into an algorithm.

If the server’s certificate and digital signature are legitimate, then the output of the algorithm will be the very same as the hash that the server created by putting both the Client Hello and Server Hello messages through a hash function. Because the client has access to both of these messages, it can verify the client by putting both of these messages through the same hash function.

The client then compares the output of the hash function against the output of the earlier algorithm. If the two values match, then the digital signature and the certificate are legitimate, and the client verifies the server. If the values don’t match, the client terminates the handshake.

If you are curious about how this works in more detail, the RSA section from our article on digital signatures gives a more thorough rundown on how the hash of a message can be signed, and how this signature can be verified.


The server’s Finished message allows the client to verify that the handshake was completed successfully and that it hasn’t been altered. The first step of calculating the verification data involves computing a Finished Key by putting the Server Handshake Traffic Key (from the TLS key generation section) through the HKDF Expand function. The Finished Key and a hash of all of the prior messages then go through a hash function to form a hash-based message authentication code (HMAC).

The client must verify this Finished message once it receives it. If the contents of the message are incorrect, it must terminate the connection. At this stage of the connection, the server can begin sending application data to the client.

Application Data

If you refer back to the diagram at the start of the RFC8446 section, you will see that the following sections are all enclosed with curly brackets {}:






While Application Data has standard brackets:

[Application Data*]

The diagram states that messages enclosed by standard brackets are “protected using keys derived from [sender]_application_traffic_secret_N.” This means that unlike the earlier messages, the Application Data is not encrypted with keying material derived from the Server Handshake Traffic Secret.

Instead, the Application Data is encrypted with keying materials derived from the Server Application Traffic Secret. These keying materials are computed by running the Server Application Traffic Secret through the HKDF Expand function.

The keying material derived from the Server Application Traffic Secret can’t be used to encrypt the earlier messages because it cannot be computed until after the Finished message has been sent. This is because the Finished message is a component of the hash that the Server Application Traffic Secret uses.

Once the Finished message has been sent, all of the data must be encrypted with these new keys that have been derived from the Server Application Traffic Secret.

By this stage, the server’s role in the TLS handshake is complete, unless it is also authenticating the client. If the server is authenticating the client, then its responses to the client’s Certificate and Certificate Verify messages must also be encrypted with the keying material derived from the Server Application Traffic Secret.

The client’s final steps

Now that the server has established its keys, sent the client everything it needs for authentication, and even started sending the application data, let’s take a look at what the client has to do to complete its side of the handshake:

 Client                                           Server

Key  ^ ClientHello
Exch | + key_share*
ange | + signature_algorithms*
     | + psk_key_exchange_modes*
     v + pre_shared_key*       -------->
                                            ServerHello  ^ Key
                                           + key_share*  | Exch
                                      + pre_shared_key*  v ange
                                  {EncryptedExtensions}  ^  Server
                                  {CertificateRequest*}  v  Params
                                         {Certificate*}  ^
                                   {CertificateVerify*}  | Auth
                                             {Finished}  v
                               <--------  [Application Data*]
     ^ {Certificate*}
Auth | {CertificateVerify*}
     v {Finished}              -------->
       [Application Data]      <------->  [Application Data]

If we look toward the bottom left, we see that the client has three authentication-related messages; Certificate, Certificate Verify, and Finished.

You will also note that these messages are surrounded by curly brackets {}, indicating that they have been encrypted by keys derived from the Client Handshake Traffic Secret. These keys were derived in almost the exact same way as the server derived its equivalent keys.

We will save you the repetition of going into the key generation process once more. The key schedule works as shown in the TLS 1.3 key schedule section. The most important thing to remember is that all of the exhausting steps of the key schedule allowed the client and server to establish a Shared Secret that then enables them to set up a secure connection over an insecure channel.

Even though the client doesn’t have access to the server’s private keys and vice-versa, these cryptographic quirks still allow them to read each other’s encrypted messages, without ever having to send the key across an insecure channel.


The client only has to send a Certificate message if the server sent a Certificate Request message asking for client authentication.

If so, it must send the server a certificate that meets the server’s parameters. Just like the server’s certificate, it must include its hostname, public key and a digital signature from a Certificate Authority that proves that the client is who it says it is.

The client’s certificate also has a matching private key, which it never reveals to anyone else, but it uses it as part of its Certificate Verify message.

Certificate Verify

Again, the client only sends the Certificate Verify message if the server requested client authentication. When client authentication is required, the client proves that it owns the certificate in the same way that the server does in the earlier Certificate Verify section. The server verifies the message using the same steps as well.


The Finished message is the client’s final message in the basic handshake process. It’s similar to the server’s Finished message, except that the Finished Key used as part of the HMAC is calculated from the Client Handshake Traffic key rather than the Server Handshake Traffic Key.

When the server receives the client’s Finished message, it must verify the contents. If they are incorrect, it must terminate the connection.

Application data

Once the client has sent its Finished message, it can begin transmitting application data to the server. In the same way that the server uses different keys for its application data, the client switches away from the keys it used in its earlier encrypted messages. The client’s application data is encrypted with keying materials derived from the Client Application Traffic Secret, via the same means that the server’s application data keying materials were computed.

At this stage, the basic TLS handshake has concluded and the client and server can freely send application data over a secure TLS 1.3 connection. Although it took us a long time to describe all of the intricate details, this whole process can take place in the few moments it takes between clicking on a URL and arriving at the website.