I want to exchange data

Public key encryption involves protecting data with two keys, one public and one private. This allows you to share encrypted data with anyone who has the public key, but any data they send back can only be decrypted with the private key. If you need to encrypt data with a public key because the sender cannot store any secrets, use the Hybrid Encryption primitive.

We recommend the DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM key type for most hybrid encryption use cases. For all supported key types, see Supported Key Types.

The following examples get you started using the Hybrid Encryption primitive.

Python

examples/python/hybrid/hybrid.py
"""A command-line utility for encrypting a file using hybrid encryption.

It loads cleartext keys from disk - this is not recommended!
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# Special imports
from absl import app
from absl import flags
from absl import logging
import tink
from tink import cleartext_keyset_handle
from tink import hybrid


FLAGS = flags.FLAGS

flags.DEFINE_enum('mode', None, ['encrypt', 'decrypt'],
                  'The operation to perform.')
flags.DEFINE_string('keyset_path', None,
                    'Path to the keyset used for encryption.')
flags.DEFINE_string('input_path', None, 'Path to the input file.')
flags.DEFINE_string('output_path', None, 'Path to the output file.')
flags.DEFINE_string('context_info', None,
                    'Context info used for encryption.')


def main(argv):
  del argv  # Unused

  context_info = b'' if not FLAGS.context_info else bytes(
      FLAGS.context_info, 'utf-8')

  # Initialise Tink
  try:
    hybrid.register()
  except tink.TinkError as e:
    logging.exception('Error initialising Tink: %s', e)
    return 1

  # Read the keyset into a keyset_handle
  with open(FLAGS.keyset_path, 'rt') as keyset_file:
    try:
      text = keyset_file.read()
      keyset_handle = cleartext_keyset_handle.read(tink.JsonKeysetReader(text))
    except tink.TinkError as e:
      logging.exception('Error reading key: %s', e)
      return 1

  with open(FLAGS.input_path, 'rb') as input_file:
    data = input_file.read()

  if FLAGS.mode == 'encrypt':
    # Get the primitive
    try:
      primitive = keyset_handle.primitive(hybrid.HybridEncrypt)
    except tink.TinkError as e:
      logging.exception(
          'Error creating hybrid encrypt primitive from keyset: %s', e)
      return 1
    # Encrypt data
    with open(FLAGS.output_path, 'wb') as output_file:
      ciphertext = primitive.encrypt(data, context_info)
      output_file.write(ciphertext)

  if FLAGS.mode == 'decrypt':
    # Get the primitive
    try:
      primitive = keyset_handle.primitive(hybrid.HybridDecrypt)
    except tink.TinkError as e:
      logging.exception(
          'Error creating hybrid encrypt primitive from keyset: %s', e)
      return 1
    # Decrypt data
    with open(FLAGS.output_path, 'wb') as output_file:
      plaintext = primitive.decrypt(data, context_info)
      output_file.write(plaintext)


if __name__ == '__main__':
  flags.mark_flags_as_required(
      ['mode', 'keyset_path', 'input_path', 'output_path'])
  app.run(main)

Java

examples/java_src/hybrid/HybridExample.java
package hybrid;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.HybridDecrypt;
import com.google.crypto.tink.HybridEncrypt;
import com.google.crypto.tink.JsonKeysetReader;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.hybrid.HybridConfig;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;

/**
 * A command-line utility for hybrid encryption.
 *
 * <p>It loads cleartext keys from disk - this is not recommended!
 *
 * <p>It requires the following arguments:
 *
 * <ul>
 *   <li>mode: either 'encrypt' or 'decrypt'.
 *   <li>key-file: Read the key material from this file.
 *   <li>input-file: Read the input from this file.
 *   <li>output-file: Write the result to this file.
 *   <li>[optional] contex-info: Bind the encryption to this context info.
 */
public final class HybridExample {
  public static void main(String[] args) throws Exception {
    if (args.length != 4 && args.length != 5) {
      System.err.printf("Expected 4 or 5 parameters, got %d\n", args.length);
      System.err.println(
          "Usage: java HybridExample encrypt/decrypt key-file input-file output-file context-info");
      System.exit(1);
    }

    String mode = args[0];
    if (!mode.equals("encrypt") && !mode.equals("decrypt")) {
      System.err.println("Incorrect mode. Please select encrypt or decrypt.");
      System.exit(1);
    }
    File keyFile = new File(args[1]);
    File inputFile = new File(args[2]);
    byte[] input = Files.readAllBytes(inputFile.toPath());
    File outputFile = new File(args[3]);
    byte[] contextInfo = new byte[0];
    if (args.length == 5) {
      contextInfo = args[4].getBytes(UTF_8);
    }

    // Register all hybrid encryption key types with the Tink runtime.
    HybridConfig.register();

    // Read the keyset into a KeysetHandle.
    KeysetHandle handle = null;
    try {
      handle = CleartextKeysetHandle.read(JsonKeysetReader.withFile(keyFile));
    } catch (GeneralSecurityException | IOException ex) {
      System.err.println("Cannot read keyset, got error: " + ex);
      System.exit(1);
    }

    if (mode.equals("encrypt")) {
      // Get the primitive.
      HybridEncrypt encryptor = null;
      try {
        encryptor = handle.getPrimitive(HybridEncrypt.class);
      } catch (GeneralSecurityException ex) {
        System.err.println("Cannot create primitive, got error: " + ex);
        System.exit(1);
      }

      // Use the primitive to encrypt data.
      byte[] ciphertext = encryptor.encrypt(input, contextInfo);
      try (FileOutputStream stream = new FileOutputStream(outputFile)) {
        stream.write(ciphertext);
      }
      System.exit(0);
    }

    // Get the primitive.
    HybridDecrypt decryptor = null;
    try {
      decryptor = handle.getPrimitive(HybridDecrypt.class);
    } catch (GeneralSecurityException ex) {
      System.err.println("Cannot create primitive, got error: " + ex);
      System.exit(1);
    }

    // Use the primitive to decrypt data.
    byte[] plaintext = decryptor.decrypt(input, contextInfo);
    try (FileOutputStream stream = new FileOutputStream(outputFile)) {
      stream.write(plaintext);
    }
    System.exit(0);
  }

  private HybridExample() {}
}

C++

examples/cc/hybrid_encryption/hybrid_cli.cc
// A command-line utility for testing Tink Hybrid Encryption.

#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <utility>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "tink/cleartext_keyset_handle.h"
#include "tink/hybrid/hpke_config.h"
#include "tink/hybrid_decrypt.h"
#include "tink/hybrid_encrypt.h"
#include "tink/json_keyset_reader.h"
#include "tink/keyset_handle.h"
#include "tink/keyset_reader.h"
#include "tink/util/status.h"

ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format");
ABSL_FLAG(std::string, mode, "", "Mode of operation {encrypt|decrypt}");
ABSL_FLAG(std::string, input_filename, "", "Input file name");
ABSL_FLAG(std::string, output_filename, "", "Output file name");
ABSL_FLAG(std::string, context_info, "",
          "Context info for Hybrid Encryption/Decryption");

namespace {

using ::crypto::tink::CleartextKeysetHandle;
using ::crypto::tink::HybridDecrypt;
using ::crypto::tink::HybridEncrypt;
using ::crypto::tink::JsonKeysetReader;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::KeysetReader;
using ::crypto::tink::util::Status;
using ::crypto::tink::util::StatusOr;

constexpr absl::string_view kEncrypt = "encrypt";
constexpr absl::string_view kDecrypt = "decrypt";

// Creates a KeysetReader that reads a JSON-formatted keyset
// from the given file.
StatusOr<std::unique_ptr<KeysetReader>> GetJsonKeysetReader(
    const std::string& filename) {
  std::clog << "Creating a JsonKeysetReader...\n";
  auto key_input_stream = absl::make_unique<std::ifstream>();
  key_input_stream->open(filename, std::ifstream::in);
  return JsonKeysetReader::New(std::move(key_input_stream));
}

// Creates a KeysetHandle that for a keyset read from the given file,
// which is expected to contain a JSON-formatted keyset.
StatusOr<std::unique_ptr<KeysetHandle>> ReadKeyset(
    const std::string& filename) {
  StatusOr<std::unique_ptr<KeysetReader>> keyset_reader =
      GetJsonKeysetReader(filename);
  if (!keyset_reader.ok()) {
    return keyset_reader.status();
  }
  return CleartextKeysetHandle::Read(*std::move(keyset_reader));
}

// Reads `filename` and returns the read content as a string, or an error status
// if the file does not exist.
StatusOr<std::string> Read(const std::string& filename) {
  std::clog << "Reading the input...\n";
  std::ifstream input_stream;
  input_stream.open(filename, std::ifstream::in);
  if (!input_stream.is_open()) {
    return Status(absl::StatusCode::kInternal,
                  absl::StrCat("Error opening input file ", filename));
  }
  std::stringstream input;
  input << input_stream.rdbuf();
  return input.str();
}

// Writes the given `data_to_write` to the specified file `filename`.
Status Write(const std::string& data_to_write, const std::string& filename) {
  std::clog << "Writing the output...\n";
  std::ofstream output_stream(filename,
                              std::ofstream::out | std::ofstream::binary);
  if (!output_stream.is_open()) {
    return Status(absl::StatusCode::kInternal,
                  absl::StrCat("Error opening output file ", filename));
  }
  output_stream << data_to_write;
  return crypto::tink::util::OkStatus();
}

}  // namespace

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);

  std::string mode = absl::GetFlag(FLAGS_mode);
  std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename);
  std::string input_filename = absl::GetFlag(FLAGS_input_filename);
  std::string output_filename = absl::GetFlag(FLAGS_output_filename);
  std::string context_info = absl::GetFlag(FLAGS_context_info);

  if (mode.empty()) {
    std::cerr << "Mode must be specified with --mode=<" << kEncrypt << "|"
              << kDecrypt << ">." << std::endl;
    exit(1);
  }

  if (mode != kEncrypt && mode != kDecrypt) {
    std::cerr << "Unknown mode '" << mode << "'; "
              << "Expected either " << kEncrypt << " or " << kDecrypt << "."
              << std::endl;
    exit(1);
  }
  std::clog << "Using keyset from file " << keyset_filename << " to hybrid "
            << mode << " file " << input_filename << " with context info '"
            << context_info << "'." << std::endl;
  std::clog << "The resulting output will be written to " << output_filename
            << std::endl;

  Status result = crypto::tink::RegisterHpke();
  if (!result.ok()) {
    std::cerr << result.message() << std::endl;
    exit(1);
  }

  // Read the keyset from file.
  StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
      ReadKeyset(keyset_filename);
  if (!keyset_handle.ok()) {
    std::cerr << keyset_handle.status().message() << std::endl;
    exit(1);
  }

  // Read the input.
  StatusOr<std::string> input_file_content = Read(input_filename);
  if (!input_file_content.ok()) {
    std::cerr << input_file_content.status().message() << std::endl;
    exit(1);
  }

  // Compute the output.
  std::clog << mode << "ing...\n";
  std::string output;
  if (mode == kEncrypt) {
    // Get the hybrid encryption primitive.
    StatusOr<std::unique_ptr<HybridEncrypt>> hybrid_encrypt_primitive =
        (*keyset_handle)->GetPrimitive<HybridEncrypt>();
    if (!hybrid_encrypt_primitive.ok()) {
      std::cerr << hybrid_encrypt_primitive.status().message() << std::endl;
      exit(1);
    }
    // Generate the ciphertext.
    StatusOr<std::string> encrypt_result =
        (*hybrid_encrypt_primitive)->Encrypt(*input_file_content, context_info);
    if (!encrypt_result.ok()) {
      std::cerr << encrypt_result.status().message() << std::endl;
      exit(1);
    }
    output = encrypt_result.value();
  } else {  // operation == kDecrypt.
    // Get the hybrid decryption primitive.
    StatusOr<std::unique_ptr<HybridDecrypt>> hybrid_decrypt_primitive =
        (*keyset_handle)->GetPrimitive<HybridDecrypt>();
    if (!hybrid_decrypt_primitive.ok()) {
      std::cerr << hybrid_decrypt_primitive.status().message() << std::endl;
      exit(1);
    }
    // Recover the plaintext.
    StatusOr<std::string> decrypt_result =
        (*hybrid_decrypt_primitive)->Decrypt(*input_file_content, context_info);
    if (!decrypt_result.ok()) {
      std::cerr << decrypt_result.status().message() << std::endl;
      exit(1);
    }
    output = decrypt_result.value();
  }

  // Write the output to the output file.
  Status write_result = Write(output, output_filename);
  if (!write_result.ok()) {
    std::cerr << write_result.message() << std::endl;
    exit(1);
  }

  std::clog << "All done." << std::endl;
  return 0;
}