Skip to content

marchof/three4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Three4J - Threema Gateway Client for Java

A Java client library for the Threema Gateway which supports exchanging end-to-end encrypted messages including images and arbitrary files. The primary purpose of this implementation is to provide a small Maven artifact which can be easily integrated in any Java applications. The implementation is based on the Java 11 HTTP client and Salty Coffee for NaCl encryption.

The following resources have been used to implement this API:

The API JavaDoc is available online.

Maven Artifacts

The latest Three4J version can be obtained with the following Maven dependency:

<dependency>
    <groupId>com.mountainminds</groupId>
    <artifactId>three4j</artifactId>
    <version>1.4.0</version>
</dependency>

Latest builds from master are available in this snapshot repository: https://s01.oss.sonatype.org/content/repositories/snapshots/

Prerequisites

Three4J requires Java 11 or later.

To use this library you need a Threema Gateway account with at least one Threema ID and the corresponding key pair. Three4J comes with a main class to generate a new key pair:

$ java com.mountainminds.three4j.KeyGenerator

PLEASE KEEP THE GENERATED KEYS CONFIDENTIAL IF YOU USE THEM WITH A THREEMA ID
IF YOU LOOSE THE KEY OF A THREEMA ID IT CANNOT BE USED ANY MORE FOR MESSAGING

 public key: e25f5e489c458a9eb9d0c07ae44fba34a91b4bd20093e854400998abxxxxxxxx
private key: 93f9873676db87ed2e3ec603645a360c529eb998b35afec17d7cd066xxxxxxxx

Please consult Threema's documentation how to create a gateway Threema ID.

API Usage Guide

Setup

To use the gateway API client you need a gateway Threema ID and the corresponding secret which was issued by the gateway admin interface:

ThreemaId from = ThreemaId.of("*YOURGWY"); // Insert your ID here
String secret = // e.g. "JSH5y9DfvOROm2Iw", retrieve this from a secure location

All requests are send through a Gateway instance:

Gateway gw = new Gateway(from, secret);

If you want to send end-to-end encrypted messages (which is recommended) you need your private 32 byte Threema key. The hexadecimal string representation has 64 characters. Make sure you store this key securely and do not make it available to others:

String myPrivateKeyStr = // retrieve this from a secure location
PrivateKey myPrivateKey = KeyEncoder.decodePrivateKey(myPrivateKeyStr);

Lookups

Sending a Threema message requires you to know the eight character long Threema ID of the receiver. Users may choose to register their telephone number or email address with their account which can then be queried to lookup their Threema ID. Note that we do not disclose the actual data but only send hash values:

ThreemaId receiverId = gw.getIdByPhoneNumber(Hash.ofPhone("+41791234567"));
System.out.println(receiverId);

Or by Email address:

receiverId = gw.getIdByEmailAddress(Hash.ofEmail("test@example.com"));
System.out.println(receiverId);

Depending on the client the receiver might be able to process certain message types only. You can check the capabilities of a given Threema ID:

System.out.println(gw.getCapabilities(receiverId));

Encrypted Text Messages

To send a encrypted message we need the public key of the receiver which can be obtained via the gateway API. For better information security you should consider obtaining the public key physically from the receivers device e.g. from the QR code.

PublicKey receiverPublicKey = gw.getPublicKey(receiverId);

To ensure end-to-end encryption you create and encrypt the message locally before you send it to the gateway:

String text = String.format("Secret message at %s.", Instant.now());

PlainMessage msg = new PlainMessage.Text(text);
EncryptedMessage encrypted = msg.encrypt(myPrivateKey, receiverPublicKey);
MessageId messageId = gw.sendMessage(receiverId, encrypted);

System.out.println(messageId);

Encrypted Image Messages

Sending images requires two steps. First we uploading the image as a encrypted blob. Similarly as for the actual message the encryption key for the blob is calculated from our private key and the receivers public key.

byte[] image = Files.readAllBytes(Path.of("src/test/resources/image.jpg"));

Blob blob = Blob.newImage(myPrivateKey, receiverPublicKey);
UploadedBlob uploadedBlob = gw.enrcryptAndUploadBlob(blob, image);

A reference to the uploaded blob needs then to be used in the image message:

PlainMessage imgMsg = new PlainMessage.Image(uploadedBlob);
EncryptedMessage encrypted = imgMsg.encrypt(myPrivateKey, receiverPublicKey);
gw.sendMessage(receiverId, encrypted);

We can also download and decrypt our image blob again:

byte[] downloadedImage = gw.downloadAndDecryptBlob(uploadedBlob);
Files.write(Path.of("target/download.jpg"), downloadedImage);

Encrypted File Messages

We can encrypt and send arbitrary files. Files are encrypted with a random key which is then transmitted with the corresponding message. An optional preview image can be added which must be encrypted with the same key than the file:

byte[] file = Files.readAllBytes(Path.of("src/test/resources/document.pdf"));
UploadedBlob uploadedFileBlob = gw.enrcryptAndUploadBlob(Blob.newFile(), file);

byte[] thumbnail = Files.readAllBytes(Path.of("src/test/resources/thumbnail.png"));
Blob thumbnailBlob = uploadedFileBlob.thumbnail();
UploadedBlob uploadedThumbnailBlob = gw.enrcryptAndUploadBlob(thumbnailBlob, thumbnail);

Construction a file message requires a bit of meta data like the MIME type of the file.

PlainMessage.File fileMsg = new PlainMessage.File(uploadedFileBlob, "application/pdf", RenderingType.DEFAULT);
fileMsg.setThumbnail(uploadedThumbnailBlob);
fileMsg.setFileName("document.pdf");

EncryptedMessage encrypted = fileMsg.encrypt(myPrivateKey, receiverPublicKey);
gw.sendMessage(receiverId, encrypted);

Simple, Unencrypted Messages

With a basic mode gateway ID you can directly send a plain text message to a given Threema ID without local encryption. The key pair is managed for you on the gateway server. Please rather consider using end-to-end encryption as described above.

gw.sendSimpleMessage(ThreemaId.of("ABCDEFGH"), "Not so secret message.");

Alternatively you can also use a international telephone number or a email address to send a message to if the users has registered them with Threema. Note that the telephone number or the email address is disclosed to the Threema gateway.

gw.sendSimpleMessageToPhoneNumber("41791234567", "Not so secret message.");
gw.sendSimpleMessageToEmailAddress("test@example.com", "Not so secret message.");

Account Information

Threema charges you for messages and blob uploads via the gateway. You can query the remaining credits via API:

System.out.println("Remaining credits: " + gw.getRemainingCredits());

Threema QR Code

To establich trust with your users you can create a QR code with your Threema ID and your public key. When the user scans that QR code your gateway ID will show "green" in their contact list. The qrcode() method creates the text content which need to be converted to a QR graphic with a library of your choice (e.g. zxing).

PublicKey publicKey = KeyEncoder.getPublicKey(privateKey);
String qrtext = KeyEncoder.qrcode(threemaid, publicKey);
System.out.println(qrtext);

Callback Handling

You can configure your own HTTP server to receive messages from the Threema gateway. The corresponding endpoint must be visible from the public internet of course. The payload can be decoded with the GatewayCallback class:

byte[] body = // unprocessed body received from the HTTP server of your choice

GatewayCallback callback = new GatewayCallback(body, secret);
PublicKey publicKey = gw.getPublicKey(callback.getFrom());
PlainMessage message = callback.getMessage().decrypt(publicKey, myPrivateKey);

System.out.println(message);

Security Disclaimer

This project has been implemented for personal use only. There was no independent security audit. Please perform your own security audit before using this software to send sensitive content!

Also make sure you never disclose a private key of a Threema ID. The private key will allow to send messages in your name and decode all content that has been sent to you.

License

This code is provided "as is" under the MIT License, without warranty of any kind.

Trademarks

The Threema Messenger and the Messaging Gateway are products of Threema GmbH, Switzerland. This project has no affiliation to Threema GmbH. Please have a look at their website and consider using their products for secure end-to-end encrypted communication.