Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions files/en-us/web/api/rtcdtlstransport/getremotecertificates/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
title: "RTCDtlsTransport: getRemoteCertificates() method"
short-title: getRemoteCertificates()
slug: Web/API/RTCDtlsTransport/getRemoteCertificates
page-type: web-api-instance-method
browser-compat: api.RTCDtlsTransport.getRemoteCertificates
---

{{APIRef("WebRTC")}}

The **`getRemoteCertificates()`** method of the {{domxref("RTCDtlsTransport")}} interface returns the certificates chain of the remote peer of the DTLS connection.

## Syntax

```js-nolint
getRemoteCertificates()
```

### Parameters

None.

### Return value

An array of {{jsxref("ArrayBuffer")}} objects that represents the remote peer's certificate chain.
Each object contains a DER-encoded X.509 certificate.

For [`new`](/en-US/docs/Web/API/RTCDtlsTransport/state#new) connections, this is an empty array.
It is populated with the certificates from the remote peer when the state of the transport changes to [`connected`](/en-US/docs/Web/API/RTCDtlsTransport/state#connected).

### Exceptions

None

## Description

WebRTC media and data channels are secured using DTLS (Datagram Transport Layer Security).
During signaling, each endpoint advertises the fingerprint of the DTLS certificate it will present.
During the DTLS handshake, the user agent verifies that the certificate presented by the remote peer matches the fingerprint in the negotiated SDP.

DTLS guarantees the connected peer is the one you've been negotiating with, because only that peer has the private key matching the certificate whose fingerprint was exchanged during signaling.
Comment thread
hamishwillee marked this conversation as resolved.
However, since WebRTC uses self-signed certificates rather than certificates issued by a certificate authority, the certificate does not identify the person, service, or device at the other end.
Establishing the identity of the remote peer usually requires an out-of-band mechanism such as comparing certificate fingerprints verbally over a phone call, or using a separate authenticated channel.

The `getRemoteCertificates()` method allows you to get the remote certificates used by DTLS and use them for _application-layer_ authentication of the remote peer.

For fingerprint continuity, each peer must use the same certificate across sessions, rather than generating a new one each time they connect."
After connecting to the remote peer, you'd exchange information out-of-band to verify that it is the intended party, and use `getRemoteCertificates()` to get its certificates.
When you subsequently connect to that remote peer, you'd only allow communication if it has exactly the same certificate fingerprints.
There is still a window for a person-in-the-middle attack, but it only exists for the very first connection between peers.

Note that `getRemoteCertificates()` returns raw DER-encoded X.509 certificates as `ArrayBuffer` objects.
DER (Distinguished Encoding Rules) is the binary serialization format used for X.509 certificates in TLS and DTLS.
Unlike {{domxref("RTCCertificate")}}, these buffers do not expose fingerprints and expiration date directly.
To work with them you must process the raw bytes: you can hash the buffer with {{domxref("SubtleCrypto.digest()")}} to compute a fingerprint (as shown in the example below), or pass it to an X.509 parsing library to inspect individual fields.

## Example

### Basic usage

This example shows how to fingerprint the remote peer's certificate after the DTLS handshake and compare it against a stored value.

```js
async function getRemoteFingerprint(dtlsTransport) {
const certs = dtlsTransport.getRemoteCertificates();
if (certs.length === 0) {
return null;
}

// Hash the raw DER-encoded certificate with SHA-256
const digest = await crypto.subtle.digest("SHA-256", certs[0]);

// Format as colon-separated hex, matching the SDP a=fingerprint convention
return Array.from(new Uint8Array(digest))
.map((b) => b.toString(16).padStart(2, "0"))
.join(":");
}

// Call once DTLS handshake is complete
pc.addEventListener("connectionstatechange", async () => {
if (pc.connectionState === "connected") {
const sender = pc.getSenders()[0];
if (!sender?.transport) return;

const fingerprint = await getRemoteFingerprint(sender.transport);
if (!fingerprint) return;
const stored = localStorage.getItem("remote-peer-fingerprint");

if (!stored) {
localStorage.setItem("remote-peer-fingerprint", fingerprint);
} else if (stored !== fingerprint) {
console.error("Certificate mismatch — closing connection");
pc.close();
}
}
});
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("RTCPeerConnection.generateCertificate()")}}
- [WebRTC API](/en-US/docs/Web/API/WebRTC_API)
Comment thread
hamishwillee marked this conversation as resolved.
- [Introduction to the Real-time Transport Protocol (RTP)](/en-US/docs/Web/API/WebRTC_API/Intro_to_RTP)
5 changes: 2 additions & 3 deletions files/en-us/web/api/rtcdtlstransport/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ _Also inherits properties from {{DOMxRef("EventTarget")}}._
_Also inherits methods from {{DOMxRef("EventTarget")}}._

- {{DOMxRef("RTCDtlsTransport.getRemoteCertificates", "getRemoteCertificates()")}}
- : Returns an array of {{jsxref("ArrayBuffer")}} containing the certificates of the remote peer of the connection.
- : Returns an array of {{jsxref("ArrayBuffer")}} objects representing the remote peer's certificate chain.

## Events

Expand All @@ -48,6 +48,7 @@ _Also inherits methods from {{DOMxRef("EventTarget")}}._

`RTCDtlsTransport` objects are created when an app calls either {{domxref("RTCPeerConnection.setLocalDescription", "setLocalDescription()")}} or {{domxref("RTCPeerConnection.setRemoteDescription", "setRemoteDescription()")}}. The number of DTLS transports created and how they're used depends on the bundling mode used when creating the {{domxref("RTCPeerConnection")}}.

[Bundling](https://datatracker.ietf.org/doc/rfc8843/) lets you use one `RTCDtlsTransport` to carry the data for multiple higher-level transports, such as multiple {{domxref("RTCRtpTransceiver")}}s.
Whether bundling is used depends on what the other endpoint is able to negotiate. All browsers support bundling, so when both endpoints are browsers, you can rest assured that bundling will be used.

Some non-browser legacy endpoints, however, may not support bundle. To be able to negotiate with such endpoints (or to exclude them entirely), the `bundlePolicy` property may be provided when creating the connection. The `bundlePolicy` lets you control how to negotiate with these legacy endpoints. The default policy is `"balanced"`, which provides a balance between performance and compatibility.
Expand All @@ -62,8 +63,6 @@ const rtcConfig = {
const pc = new RTCPeerConnection(rtcConfig);
```

[Bundling](https://datatracker.ietf.org/doc/rfc8843/) lets you use one `RTCDtlsTransport` to carry the data for multiple higher-level transports, such as multiple {{domxref("RTCRtpTransceiver")}}s.

#### When not using BUNDLE

When the connection is created without using BUNDLE, each RTP or RTCP component of each {{domxref("RTCRtpTransceiver")}} has its own `RTCDtlsTransport`; that is, every {{domxref("RTCRtpSender")}} and {{domxref("RTCRtpReceiver")}}, has its own transport, and all {{domxref("RTCDataChannel")}} objects share a transport dedicated to SCTP.
Expand Down