End-to-End Encryption (E2EE) - Android
Overview
End-to-end encryption (E2EE) ensures that your media content remains private and secure by encrypting it on the sender's device and decrypting it only on the receiver's device. This prevents intermediaries, including VideoSDK servers, from accessing or modifying the content.
E2EE is particularly important for applications in security-critical domains such as telehealth, finance, and legal services.
E2EE Modes
VideoSDK offers two modes of End-to-End Encryption (E2EE) for securing media streams during sessions:
- Shared Key (Meeting-Level): A single encryption key is used across the entire session, encrypting and decrypting media tracks for all participants.
- Per-Participant Keys: Individual participants have unique encryption keys, providing enhanced security by ensuring each participant's media streams are encrypted separately.
How E2EE Works
- E2EE is applied at the session level, where in Shared Key mode, a single key encrypts and decrypts all media tracks for every participant, while in Per-Participant Keys mode, each participant is assigned a unique key to encrypt their individual media streams separately.
- Participants without the key cannot access the media.
- The entire encryption process is handled client-side. VideoSDK does not access or store any encryption keys.
Key Management & Distribution
You are fully responsible for generating, managing, and securely distributing encryption keys to all participants. VideoSDK never stores, accesses, or transfers your encryption keys.
- Common Key Distribution Approaches:
- Generate the key on your server when creating a meeting.
- Send the key securely along with the meeting access token.
- Use HTTPS-secured API calls to fetch the key on the client.
⚠️ Ensure that all participants have access to the correct encryption key before joining the room.
SDK Version Compatibility
The table below lists the minimum SDK versions that support E2EE.
Android | iOS | React Native | React | Flutter | JavaScript |
---|---|---|---|---|---|
0.5.0 | 2.2.2 | 0.2.1 | 0.3.5 | 2.1.0 | 0.2.6 |
Enabling End-to-End Encryption (E2EE) in VideoSDK Android
VideoSDK provides two ways to enable End-to-End Encryption (E2EE):
- Shared Key: A single encryption key shared by all participants.
- Per-Participant Key: Unique encryption keys assigned to individual participants.
1. Enabling E2EE using a Shared Key
When using a shared key, all participants utilize the same encryption key, set via the setSharedKey()
method.
- Kotlin
- Java
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize the encryption key using Shared Key approach
keySetup("SHARED_KEY") // Replace with your actual shared key
// Initialize the meeting instance
VideoSDK.initMeeting(...)
}
private fun keySetup(sharedKey: String) {
val baseKeyProvider = BaseKeyProvider()
// Set a single shared encryption key for all participants
baseKeyProvider.setSharedKey(sharedKey)
// Set the key provider for E2EE
VideoSDK.setKeyProvider(baseKeyProvider)
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Initialize the encryption key using Shared Key approach
keySetup("SHARED_KEY"); // Replace with your actual shared key
// Initialize the meeting instance
VideoSDK.initMeeting(...);
}
private void keySetup(String sharedKey){
BaseKeyProvider baseKeyProvider = new BaseKeyProvider();
// Set a single shared encryption key for all participants
baseKeyProvider.setSharedKey(sharedKey);
// Set the key provider for E2EE
VideoSDK.setKeyProvider(baseKeyProvider);
}
}
Make sure to set the encryption key using setSharedKey()
before assigning the key provider with setKeyProvider()
, otherwise it will throw an Exception.
2. Enabling E2EE using Per-Participant Keys
Per-Participant Keys enhance security by assigning a distinct encryption key to each participant, ensuring that access is uniquely controlled on a per-user basis.
Step 1. Initialize the BaseKeyProvider and Set Up Encryption Keys
- Create a global reference to BaseKeyProvider.
- To enable encryption, you must set the
localParticipantId
along with its corresponding key using thesetKey()
method of theBaseKeyProvider
class. - Save each participant’s ID and encryption key securely to your server for later use in decryption.
You must use a single, shared BaseKeyProvider instance for both encryption (local participant) and decryption (remote participants).
- Kotlin
- Java
class MainActivity : AppCompatActivity() {
// BaseKeyProvider reference
private lateinit var baseKeyProvider: BaseKeyProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val localParticipantId = "YOUR_LOCAL_PARTICIPANT_ID"
// Set up the BaseKeyProvider
keyProviderSetup(localParticipantId, "SECRET_KEY")
// Initialize the meeting instance
meeting = VideoSDK.initMeeting(
context, "MEETING_ID", "PARTICIPANT_NAME",
true, true, localParticipantId, null, false, null , null)
}
// Setup Method
private fun keyProviderSetup(localParticipantId: String, secretKey: String) {
baseKeyProvider = BaseKeyProvider()
// Per-Participant E2EE: Set key for local participant
baseKeyProvider.setKey(localParticipantId, secretKey) // Your Encryption Key
VideoSDK.setKeyProvider(baseKeyProvider)
}
}
public class MainActivity extends AppCompatActivity {
private BaseKeyProvider baseKeyProvider;
@Override
protected void onCreate(Bundle savedInstanceState) {
String localParticipantId = "YOUR_LOCAL_PARTICIPANT_ID";
// Set up the encryption key
keyProviderSetup(localParticipantId, "SECRET_KEY");
// Initialize the meeting instance
Meeting meeting = VideoSDK.initMeeting(
context, "MEETING_ID", "PARTICIPANT_NAME",
true, true, localParticipantId, null, false, null , null
);
}
private void keyProviderSetup(String: localParticipantId, String: secretKey){
baseKeyProvider = new BaseKeyProvider();
// Per-Participant E2EE: Set key for local participant
baseKeyProvider.setKey(localParticipantId , secretKey); // Your Encryption Key
VideoSDK.setKeyProvider(baseKeyProvider);
}
Make sure to set the encryption key using setKey()
before assigning the key provider with setKeyProvider()
, otherwise it will throw an Exception.
Step 2. Decrypting Participant Streams using Per-Participant Keys
- When a participant joins, retrieve the corresponding encryption key (previously stored on your server) using the participant’s ID to decrypt their media streams.
- Kotlin
- Java
private val meetingEventListener = object : MeetingEventListener() {
override fun onParticipantJoined(participant: Participant) {
// Retrieve the encryption key for the participant from your server
val secretKey = "SECRET_KEY"
// Set the key to decrypt incoming participant streams
baseKeyProvider.setKey(participant.id, secretKey)
}
}
private final MeetingEventListener meetingEventListener = new MeetingEventListener() {
@Override
public void onParticipantJoined(Participant participant) {
// Retrieve the corresponding encryption key for the joined participant using participant.getId()
String secretKey = "SECRET_KEY";
// Set the retrieved key to decrypt incoming participant streams
baseKeyProvider.setKey(participant.getId(), secretKey);
}
};
- Ensure all encryption keys are securely stored and retrieved from your server.
- Proper key management is essential for effective encryption and decryption, especially when using Per-Participant Keys.
Event for E2EE State Changes
To monitor encryption/decryption state changes for each participant's media stream:
- Kotlin
- Java
meeting.localParticipant.addEventListener(object : ParticipantEventListener() {
override fun onE2eeStateChanged(state: E2EEState, stream: Stream) {
super.onE2eeStateChanged(state)
Log.d("e2ee", "Local-Participant onE2eeStateChanged -> $state")
Log.d("e2ee", "Local-Participant Stream Kind -> ${stream.kind}")
}
})
meeting.getLocalParticipant().addEventListener(new ParticipantEventListener() {
@Override
public void onE2eeStateChanged(E2EEState state, Stream stream) {
Log.d("e2ee", "Local-Participant E2eeStateChanged -> " + state);
Log.d("e2ee", "Local-Participant Stream Kind -> " + stream.getKind());
}
};
The possible values for state
are:
State | Description |
---|---|
EncryptionSuccess | Media encryption is successfully applied. |
DecryptionSuccess | Incoming media is successfully decrypted. |
EncryptionFailed | Encryption encountered an error. |
DecryptionFailed | Decryption encountered an error. |
InternalError | Internal processing error occurred. |
To check whether E2EE is enabled
You can check whether E2EE is enabled by using isE2eeEnabled()
getter function of Meeting
class.
meeting.isE2eeEnabled();
Limitations
E2EE only applies to media. It does not apply to:
- Chat messages or metadata
- API calls and signaling data
These communications are still protected by TLS but are not encrypted end-to-end.
Recording and Transcription features are not supported when End-to-End Encryption (E2EE) is enabled.
Got a Question? Ask us on discord