Java / Android

Using Themis in Android applications

  1. Build Themis for Android as described in Building and installing Themis
  2. Import Themis AAR to your application project in Android Studio or reference it in your gradle.build file. More about using libraries here.

Keypair generation

You can generate keypairs easily for secure message and secure session like this:

Keypair pair = KeypairGenerator.generateKeypair();
PrivateKey privateKey = pair.getPrivateKey();
PublicKey publicKey = pair.getPublicKey();

WARNING: When you distribute private keys to your users, make sure they are sufficiently protected. You can find guidelines here.

NOTE: When using public keys of other peers, make sure they come from trusted sources.

Usage

Secure message

Sending many messages to same recipient

  1. Create SecureMessage object with your PrivateKey and recipient's PublicKey:

    SecureMessage encryptor = new SecureMessage(yourPrivateKey, peerPublicKey);
    
  2. Encrypt each outgoing message:

     byte[] encryptedMessage = encryptor.wrap(messageToSend);
    
  3. Decrypt each incoming message:

     byte[] receivedMessage = encryptor.unwrap(wrappedMessage);
    

Sending messages to many recipients

  1. Create SecureMessage object with your PrivateKey:

    SecureMessage encryptor = new SecureMessage(yourPrivateKey);
    
  2. Encrypt each outgoing message specifying recipients' PublicKey:

    byte[] encryptedMessage = encryptor.wrap(messageToSend, peerPublicKey);
    
  3. Decrypt each incoming message specifying sender's PublicKey:

    byte[] receivedMessage = encryptor.unwrap(wrappedMessage, peerPublicKey);
    

Secure cell

  1. Create SecureCell object to protect your data

    SecureCell cell = new SecureCell(yourSecretByteArrayKey);
    

    or

    SecureCell cell = new SecureCell("your secret password string");
    

    You may specify mode as well:

     SecureCell cell = new SecureCell("your secret password string", SecureCell.MODE_TOKEN_PROTECT);
    

    NOTE: If not specified, Secure Cell will use SecureCell.MODE_SEAL by default. More about Secure Cell modes and which to choose here.

  2. Protect your data:

    SecureCellData cellData = cell.protect(context, data);
    

    NOTE: Context is optional.

    The result of the function call is SecureCellData object which is a simple container for protected data. You may get actual protected data:

    byte[] protectedData = cellData.getProtectedData();
    

    Depending on the mode selected it may also have additional data (which is opaque to the user but necessary for successful decryption):

    if (cellData.hasAdditionalData() {
        byte[] additionalData = cellData.getAdditionalData();
    }
    

    You may also use one object to encrypt different data with different keys:

    SecureCellData cellData1 = cell.protect(key1, context1, data1);
    ...
    SecureCellData cellData2 = cell.protect(key2, context2, data2);
    ...
    
  3. Recover your data:

    byte[] data = cell.unprotect(context, cellData);
    

    NOTE: Context should be same as in protect call for successful decryption.

Secure session

Secure sockets

If your application already uses Java sockets for communication you can easily add security by replacing them with our SecureSocket and SecureServerSocket

  1. Implement ISessionCallbacks interface:
  2. getPublicKeyForId which will return peer's trusted public key when needed by the system
  3. stateChanged is just a notification callback. You may use it for informational purpose, to update your UI or just have a dummy (do-nothing) implementation

    Example using anonymous class:

    ISessionCallbacks callbacks = new ISessionCallbacks() {
        @Override
        public PublicKey getPublicKeyForId(SecureSession session, byte[] id) {
            // get trusted PublicKey of user id
            PublicKey publicKey = getUserPublicKeyFromDatabaseOrOtherStorageOrSource(id);
            return publicKey; // or null if key is not found
        }
    
        @Override
        public void stateChanged(SecureSession session) {
            // update UI: for example, draw a nice lock indicating to the user that his communication is now secured
        }
    }
    
  4. Replace all your sockets with our secure versions:

    on client:

    // Socket clientSocket = new Socket(...);
    Socket clientSocket = new SecureSocket(..., clientId, clientPrivateKey, callbacks);
    

    on server:

    // ServerSocket serverSocket = new ServerSocket(...);
    ServerSocket serverSocket = new SecureServerSocket(..., serverId, serverPrivateKey, callbacks);
    
  5. Enjoy

Basic secure session

This API is useful when your application already has an established network processing path and this path is more than just using sockets. You just want to add some function calls which will wrap/unwrap outgoing/incoming buffers with data.

  1. As in case with Secure sockets. Implement ISessionCallbacks interface:

    ISessionCallbacks callbacks = new ISessionCallbacks() {
        @Override
        public PublicKey getPublicKeyForId(SecureSession session, byte[] id) {
            // get trusted PublicKey of user id
            PublicKey publicKey = getUserPublicKeyFromDatabaseOrOtherStorageOrSource(id);
            return publicKey; // or null if key is not found
        }
    
        @Override
        public void stateChanged(SecureSession session) {
            // update UI: for example, draw a nice lock indicating to the user that his communication is now secured
        }
    }
    
  2. Create SecureSession object:

    SecureSession session = new SecureSession(yourId, yourPrivateKey, callbacks);
    
  3. On client side, initiate secure session negotiation by generating and sending connect request:

    byte[] connectRequest = session.generateConnectRequest();
    // send connectRequest to the server
    
  4. On both sides begin receiving and parsing incoming data:

    // receive some data and store it in receiveBuffer
    SecureSession.UnwrapResult result = session.unwrap(receiveBuffer);
    
    switch (result.getDataType()) {
        case USER_DATA:
            // this is actual data that was encrypted by your peer using SecureSession.wrap
            byte[] data = result.getData();
            // process data according to your application flow for incoming data
            break;
        case PROTOCOL_DATA:
            // this is internal secure session protocol data. An opaque response was generated, just send it to your peer
            byte[] data = result.getData();
            // send the data to your peer as is
            break;
        case NO_DATA:
            // this is internal secure session protocol data, but no response is needed (usually happens on the client side when protocol negotiation completes)
            // do nothing
            break;
    }
    
  5. When protocol negotiation completes, you may send encrypted data to your peer:

    byte[] wrappedData = session.wrap(yourData);
    // send wrappedData to your peer
    

Secure session with transport callbacks

This API is useful when you want to clearly decouple security and network communication in your application:

  1. Implement ITransportSessionCallbacks interface. This interface extends ISessionCallbacks interface, so you have to implement two additional functions:

    ITransportSessionCallbacks callbacks = new ITransportSessionCallbacks() {
        // implement getPublicKeyForId and stateChanged as in basic ISessionCallbacks
        ...
    
        @Override
        public void write(byte[] buffer) {
            // it will be called when secure session needs to send something to your peer
            // just send buffer to your peer
        }
    
        @Override
        public byte[] read() {
            // here you should issue read request to your underlying transport (for example, read data from socket or pipe)
            // return the buffer with read data
        }
    }
    
  2. Create SecureTransportSession object:

    SecureTransportSession session = new SecureTransportSession(yourId, yourPrivateKey, callbacks);
    
  3. On client side initiate secure session negotiation by sending connect request:

    session.connect();
    
  4. After negotiation completes you may send/receive data on both sides

    // sending data
    session.write(dataToSend);
    
    ...
    
    // receiving data (probably, through a receive loop)
    byte[] receivedData = session.read();
    

More examples

Android testcases (in the tests directory) are simple self-describing easy-to-understand examples of our APIs usage scenarios. Feel free to explore them a bit.