Last updated: March 15, 2026

To set up OMEMO encryption on XMPP, install an OMEMO-capable client (Gajim on desktop: sudo apt install gajim gajim-omemo, or Conversations on Android), enable the OMEMO plugin in your account settings, then verify your contact’s fingerprint through a separate trusted channel. Once enabled, your client automatically generates identity keys and handles per-message encryption with forward secrecy – the detailed steps for each client, key management, and troubleshooting follow below.

Table of Contents

Prerequisites

Before you begin, make sure you have the following ready:

Step 1 - Understand OMEMO Encryption

OMEMO (Optimized Messaging Key Establishment) builds on the Signal Protocol and provides several security properties that matter for sensitive communications. Each device generates its own identity key pair, meaning your encryption keys remain tied to specific devices rather than just your account.

The protocol offers forward secrecy, even if your long-term keys are compromised, past conversations remain secure because each message uses a unique session key. It also provides deniable authentication, allowing recipients to verify messages came from you without being able to prove it to third parties.

Most modern XMPP clients support OMEMO, including Conversations (Android), Gajim (desktop), Dino (Linux), and Psi+ (cross-platform).

Step 2 - Client Installation and Basic Setup

Before configuring OMEMO, ensure you have a XMPP account with OMEMO support. You can register on servers like xmpp.jp, jabber.de, or conversations.im, all of which support the required XEP-0384 extension.

For desktop users, Gajim provides a straightforward installation process. On Ubuntu:

sudo apt update
sudo apt install gajim gajim-omemo

After launching Gajim, add your XMPP account through the account wizard. Navigate to Accounts → Your Account → Settings → OMEMO and enable the plugin if not already active.

For Android, install Conversations from F-Droid or the Play Store. After adding your account, tap the shield icon in any chat to enable OMEMO for that conversation.

Step 3 - Generate Keys and First-Time Configuration

When you first enable OMEMO, your client generates identity keys. This process creates three key types:

Gajim displays a fingerprint when you first set up OMEMO. Save this fingerprint or verify it through another channel, this establishes trust for future communications.

To verify a contact’s fingerprint in Gajim:

  1. Open a chat with the contact
  2. Click the shield icon or menu option
  3. Select “Verify Identity”
  4. Compare the displayed fingerprint with what your contact sees

The fingerprint appears as a 64-character hex string. For example:

e8f2a8b3c9d1f4e5a7b6c8d9e0f1a2b3
c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9

Take a screenshot or write it down. This verification step is crucial, without it, you cannot be certain you’re communicating with the right person.

Step 4 - Manage Multiple Devices

One of OMEMO’s strengths is multi-device support. Your keys sync across devices through PEP (Personal Eventing Protocol), allowing you to read encrypted messages on any device without compromising security.

When adding a new device:

  1. Set up OMEMO on the new device
  2. The new device appears in your other devices’ trusted list
  3. Re-verify fingerprints for contacts if needed

To view your own devices in Gajim, go to Accounts → Your Account → Devices. You can trust or distrust each device. Remove devices you no longer use, stale device entries can cause delivery issues.

For contacts with multiple devices, OMEMO encrypts messages individually for each device. This increases bandwidth slightly but ensures all recipient devices can decrypt the message.

Troubleshooting Common OMEMO Issues

Several issues frequently arise when first adopting OMEMO:

Messages stuck in “sending” state: This often indicates a missing pre-key. Request new pre-keys through your client’s server settings, or restart your client to refresh the key exchange.

“Untrusted” messages: Your client received a message from a device it hasn’t verified. Ask your contact to re-send from a verified device or manually trust their new device through the fingerprint verification process.

Decryption failures - Typically caused by session corruption. Both parties can resolve this by removing and re-adding each other, which forces a fresh key exchange.

For advanced debugging, Gajim’s plugin manager includes an OMEMO debug console. Access it through Tools → Plugins → OMEMO → Debug to view key fingerprints and session states.

Step 5 - Automate Key Verification

For power users managing multiple contacts, manual verification becomes tedious. Some approaches help improve the process:

Export OMEMO fingerprints from Gajim's config
import json

with open('~/.config/gajim/plugins/omemo/trust.db', 'r') as f:
 trusts = json.load(f)

for jid, data in trusts.items():
 print(f"{jid}: {data['fingerprint']}")

Store this export securely, anyone with access can impersonate your contacts if they control the keys.

Security Considerations

OMEMO protects message content but reveals metadata. Your server knows who communicates with whom and when, even if it cannot read the messages. For higher anonymity requirements, consider combining XMPP with Tor or using servers that support onion routing.

Additionally, OMEMO does not encrypt group chat metadata (who is in the room). The actual messages are encrypted, but membership information remains visible to the server and other members.

Regularly rotate your pre-keys through your client’s settings. Most clients handle this automatically, but periodic manual verification of critical contacts remains good practice.

Advanced - Server-Side Support and Protocol Requirements

Not all XMPP servers support OMEMO equally. Before setting up, verify your server supports required XEPs:

XEP Purpose Support Level
XEP-0384 OMEMO Core Required
XEP-0163 Personal Eventing Protocol (PEP) Required
XEP-0060 Publish-Subscribe Required (underlying PEP)
XEP-0191 Blocking Command Recommended
XEP-0198 Stream Management Recommended

To check server support, use a XMPP client that displays capabilities:

Using xmpp-console (if installed)
disco info <your-server>

Or via web XMPP console
Visit https://conversejs.org/ and check your server's supported extensions

If your server doesn’t support PEP (XEP-0163), OMEMO cannot function because identity keys must be published to your account’s private PEP storage.

Step 6 - Build Custom Applications with OMEMO

Developers integrating OMEMO into custom applications should use the pyomemo or omemo-python libraries:

Sending encrypted messages programmatically
from omemo.exceptions import OMEMOException
from omemo.state import OMEMOState

Initialize OMEMO state
state = OMEMOState(account_jid='user@xmpp.example.com')

Encrypt message for recipient
plaintext = "This is a secret message"
recipient_jid = 'friend@xmpp.example.com'

try:
 # Get recipient's identity keys
 recipient_keys = state.get_recipient_keys(recipient_jid)

 # Encrypt for each of recipient's devices
 encrypted_messages = state.encrypt_message(
 plaintext,
 recipient_jid,
 recipient_keys
 )

 # Send encrypted messages via XMPP
 for device_id, encrypted_payload in encrypted_messages.items():
 send_xmpp_message(
 to=recipient_jid,
 encrypted_payload=encrypted_payload,
 device_id=device_id
 )

except OMEMOException as e:
 print(f"Encryption failed: {e}")

This approach allows building custom tools that use OMEMO’s encryption without being limited to standard chat clients.

Threat Model and Limitations

OMEMO provides strong message encryption but has known limitations:

Protected:

Not protected:

Adversary assumptions:

For maximum security, combine OMEMO with:

Step 7 - Server-Side Logging and Metadata

Even with OMEMO encryption, your XMPP server records metadata:

[2026-03-15 14:32:15] alice@example.com -> bob@example.com (encrypted)
[2026-03-15 14:32:45] bob@example.com -> alice@example.com (encrypted)

The server knows:

Mitigation:

  1. Trust your server: Use servers run by organizations you trust (EFF-endorsed servers, institutional servers)
  2. Use Tor: Hide your IP address from server
  3. Run your own: Host your own XMPP server for your organization
  4. Audit logs: Request your server operator’s logging policy
  5. Message expiration: Configure clients to delete messages after configurable period

Step 8 - Key Verification Automation

For organizations managing multiple users, manual fingerprint verification doesn’t scale. Several approaches help:

QR Code Distribution: Many modern XMPP clients (particularly Conversations) support QR code scanning for fingerprint verification. Generate QR codes for distribution:

Using qrencode
echo "alice@example.com:e8f2a8b3c9d1f4e5a7b6c8d9e0f1a2b3" | \
 qrencode -o alice_fingerprint.png

Distribute QR codes through your organization’s channels (printed badge at conference, email, shared document). Other users scan the code to automatically trust the fingerprint.

Out-of-Band Fingerprint Lists: Publish fingerprint lists through authenticated channels:

Publish fingerprints in JSON format
{
 "verified_users": [
 {
 "jid": "alice@example.com",
 "fingerprints": [
 {
 "device": 12345,
 "fingerprint": "e8f2a8b3c9d1f4e5a7b6c8d9e0f1a2b3c4d5e6f"
 }
 ],
 "verified_date": "2026-03-15",
 "verifier": "bob@example.com"
 }
 ]
}

Distribute this list through authenticated channels. Users can import fingerprints to automate verification.

Step 9 - Practical Organization Setup

For a small organization deploying XMPP + OMEMO:

#!/bin/bash
deploy_xmpp_omemo.sh - Deploy XMPP server with OMEMO support

1. Install Prosody XMPP server (excellent PEP support)
sudo apt-get install prosody prosody-modules

2. Enable required modules in prosody config
cat >> /etc/prosody/prosody.cfg.lua << EOF
modules_enabled = {
 "roster",
 "offline",
 "pep", -- Personal Eventing Protocol (required for OMEMO)
 "private", -- Private XML storage
 "vcard",
 "version",
 "disco",
 "time",
};
EOF

3. Create virtual hosts
prosodyctl register alice example.com password123
prosodyctl register bob example.com password456

4. Restart service
sudo systemctl restart prosody

5. Clients now connect to example.com:5222 (normal) or :5223 (legacy SSL)
Users add their accounts in OMEMO-capable clients

After setup, users connect with their organization’s domain and OMEMO automatically protects communications.

Troubleshooting with Network Analysis Tools

When OMEMO isn’t working, network analysis helps diagnose issues:

Capture XMPP traffic (without reading encrypted content)
tcpdump -i eth0 'port 5222 or port 5269' -A | grep -E "(stanza|stream)"

Check if PEP is functioning
Look for <pubsub> stanzas in traffic
tcpdump -i eth0 'port 5222' -A | grep -E "<pubsub|<items node"

Verify device list is published
Should see OMEMO device list under `/devices` node

Common issues and fixes:

Issue Cause Fix
“OMEMO not supported” Server lacks PEP Change servers or deploy own
Messages stuck “sending” Missing pre-keys Regenerate pre-keys in settings
“Untrusted device” New device not verified Verify fingerprint with contact
Decryption fails Corrupted session Remove and re-add contact
Slow delivery Congested server Wait or switch servers

Step 10 - High-Security Setup for Activists

For journalists, activists, and high-risk users requiring maximum OMEMO security:

#!/bin/bash
secure_xmpp_setup.sh

1. Use Tor to hide XMPP server discovery
export ALL_PROXY=socks5://127.0.0.1:9050

2. Use onion-only XMPP servers if available
xmpp5u7z6zzq3x5r.onion (Conversations' server over Tor)

3. Configure client to require OMEMO encryption for all conversations
In Gajim - Accounts → Your Account → Encryption → OMEMO mandatory

4. Disable automatic account creation
Existing accounts only; no registration

5. Enable message expiration
Set to delete all messages from disk after 1 week

6. Disable message carbons (copies to other devices)
Prevents metadata about your devices

7. Regular key rotation
Manually regenerate OMEMO keys monthly

8. Verify contacts through multiple independent channels
In-person if possible; phone call minimum

Frequently Asked Questions

How long does it take to guide?

For a straightforward setup, expect 30 minutes to 2 hours depending on your familiarity with the tools involved. Complex configurations with custom requirements may take longer. Having your credentials and environment ready before starting saves significant time.

What are the most common mistakes to avoid?

The most frequent issues are skipping prerequisite steps, using outdated package versions, and not reading error messages carefully. Follow the steps in order, verify each one works before moving on, and check the official documentation if something behaves unexpectedly.

Do I need prior experience to follow this guide?

Basic familiarity with the relevant tools and command line is helpful but not strictly required. Each step is explained with context. If you get stuck, the official documentation for each tool covers fundamentals that may fill in knowledge gaps.

Is this approach secure enough for production?

The patterns shown here follow standard practices, but production deployments need additional hardening. Add rate limiting, input validation, proper secret management, and monitoring before going live. Consider a security review if your application handles sensitive user data.

Where can I get help if I run into issues?

Start with the official documentation for each tool mentioned. Stack Overflow and GitHub Issues are good next steps for specific error messages. Community forums and Discord servers for the relevant tools often have active members who can help with setup problems.

Related Articles