I want to digitally sign data

If you want to ensure that nobody can tamper with your data and prove that the data came from you, we recommend the Digital Signature primitive. It is asymmetric, meaning it uses paired keys, one public and one private. The private key signs the data, and the public key verifies it.

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

The following examples get you started using the Digital Signature primitive.

Python

examples/python/signature/signature.py
"""A utility for signing and verifying files using digital signatures.

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

from __future__ import absolute_import
from __future__ import division
# Placeholder for import for type annotations
from __future__ import print_function

import binascii

# 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 signature


FLAGS = flags.FLAGS

flags.DEFINE_enum('mode', None, ['sign', 'verify'],
                  'The operation to perform.')
flags.DEFINE_string('keyset_path', None,
                    'Path to the keyset used for the signature operation.')
flags.DEFINE_string('data_path', None,
                    'Path to the file with the input data.')
flags.DEFINE_string('signature_path', None,
                    'Path to the signature file.')


def main(argv):
  del argv  # Unused.

  # Initialise Tink
  try:
    signature.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.data_path, 'rb') as data_file:
    data = data_file.read()

  if FLAGS.mode == 'sign':
    # Get the primitive
    try:
      cipher = keyset_handle.primitive(signature.PublicKeySign)
    except tink.TinkError as e:
      logging.exception('Error creating primitive: %s', e)
      return 1

    # Sign data
    sig = cipher.sign(data)
    with open(FLAGS.signature_path, 'wb') as signature_file:
      signature_file.write(binascii.hexlify(sig))
    return 0

  # Get the primitive
  try:
    cipher = keyset_handle.primitive(signature.PublicKeyVerify)
  except tink.TinkError as e:
    logging.exception('Error creating primitive: %s', e)
    return 1

  # Verify data
  with open(FLAGS.signature_path, 'rb') as signature_file:
    try:
      expected_signature = binascii.unhexlify(signature_file.read().strip())
    except binascii.Error as e:
      logging.exception('Error reading expected code: %s', e)
      return 1
  try:
    cipher.verify(expected_signature, data)
    logging.info('Signature verification succeeded.')
    return 0
  except binascii.Error as e:
    logging.exception('Error reading expected signature: %s', e)
  except tink.TinkError as e:
    logging.info('Signature verification failed.')
    return 1


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

Java

examples/java_src/signature/SignatureExample.java
package signature;

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

import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetReader;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.signature.SignatureConfig;
import com.google.crypto.tink.subtle.Hex;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.List;

/**
 * A command-line utility for digitally signing and verifying a file.
 *
 * <p>It loads cleartext keys from disk - this is not recommended!
 *
 * <p>It requires the following arguments:
 *
 * <ul>
 *   <li>mode: either 'sign' or 'verify'.
 *   <li>key-file: Read the key material from this file.
 *   <li>input-file: Read the input from this file.
 *   <li>signature-file: name of the file containing a hexadecimal signature of the input file.
 */
public final class SignatureExample {
  public static void main(String[] args) throws Exception {
    if (args.length != 4) {
      System.err.printf("Expected 4 parameters, got %d\n", args.length);
      System.err.println(
          "Usage: java SignatureExample sign/verify key-file input-file signature-file");
      System.exit(1);
    }

    String mode = args[0];
    if (!mode.equals("sign") && !mode.equals("verify")) {
      System.err.println("Incorrect mode. Please select sign or verify.");
      System.exit(1);
    }
    File keyFile = new File(args[1]);
    byte[] msg = Files.readAllBytes(Paths.get(args[2]));
    File signatureFile = new File(args[3]);

    // Register all signature key types with the Tink runtime.
    SignatureConfig.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("sign")) {
      // Get the primitive.
      PublicKeySign signer = null;
      try {
        signer = handle.getPrimitive(PublicKeySign.class);
      } catch (GeneralSecurityException ex) {
        System.err.println("Cannot create primitive, got error: " + ex);
        System.exit(1);
      }

      // Use the primitive to sign data.
      byte[] signature = signer.sign(msg);
      try (FileOutputStream stream = new FileOutputStream(signatureFile)) {
        stream.write(Hex.encode(signature).getBytes(UTF_8));
      }
      System.exit(0);
    }

    List<String> lines = Files.readAllLines(signatureFile.toPath());
    if (lines.size() != 1) {
      System.err.printf("The signature file should contain only one line,  got %d", lines.size());
      System.exit(1);
    }
    byte[] signature = Hex.decode(lines.get(0).trim());

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

    // Use the primitive to verify data.
    try {
      verifier.verify(signature, msg);
    } catch (GeneralSecurityException ex) {
      System.err.println("Signature verification failed.");
      System.exit(1);
    }

    System.exit(0);
  }

  private SignatureExample() {}
}