می‌خواهم از رمزگذاری سمت کارخواه با Cloud KMS استفاده کنم

Tink به شما امکان می‌دهد رمزگذاری سمت کارخواه را برای برخی‌از داده‌ها انجام دهید و از کلیدهایتان بااستفاده از «سیستم مدیریت کلید» (KMS) ابری محافظت کنید. این ویژگی به این صورت کار می‌کند:

  1. کارخواه «کلید رمزگذاری داده» (DEK) تولید می‌کند.
  2. داده‌ها بااستفاده از DEK توسط کارخواه رمزگذاری می‌شود.
  3. DEK توسط «کلید رمزگذاری کلید» (KEK) که در KMS ابری ذخیره شده است رمزگذاری می‌شود.

بااستفاده از KMS ابری، KEK شما به‌طور ایمن در فضای ابری قرار می‌گیرد، جایی که می‌توانید آن را بسته به نیازهایتان بچرخانید، غیرفعال کنید، یا ازبین ببرید.

برای اطلاعات بیشتر درباره آنچه Tink برای تعامل با KMS ابری نیاز دارد، به بخش نمای کلی مدیریت کلید مراجعه کنید.

با Tink می‌توانید رمزگذاری سمت کارخواه را با Cloud KMS به دو روش انجام دهید:

  • استفاده از KMS envelope AEAD primitive (KmsEnvelopeAead) – برای هر رمزگذاری، Tink یک AEAD DEK جدید ایجاد می‌کند، آن را با KEK از KMS ابری رمزگذاری می‌کند، و آن را به‌عنوان بخشی از متن رمزگذاری‌شده ذخیره می‌کند، بنابراین نیازی نیست خودتان DEK را مدیریت کنید. درنتیجه، متن رمزی کمی طولانی‌تر است زیرا شامل DEK رمزگذاری‌شده است. علاوه‌براین، هربار که رمزگذاری یا رمزگشایی می‌کنید، Tink یک RPC به KMS می‌سازد (رمزگشایی یا رمزگذاری واقعی داده‌ها به‌صورت محلی انجام می‌شود).

  • استفاده از یک اولیه دیگر و رمزگذاری مجموعه کلید با KMS ابری – این روش انعطاف‌پذیرتر است زیرا می‌توانید از اولیه‌های مختلفی غیر از AEAD استفاده کنید و می‌توانید فراخوانی‌ها به KMS ابری را محدود کنید.

AEAD پاکت KMS

وقتی داده‌ها را با KmsEnvelopeAead اولیه رمزگذاری می‌کنید، Tink کارهای زیر را برای شما انجام می‌دهد:

  1. یک DEK تصادفی تولید می‌کند و داده‌هایتان را به‌طور محلی با آن رمزگذاری می‌کند.
  2. درخواستی به KMS شما ارسال می‌کند تا DEK را با KMS KEK رمزگذاری کند.
  3. کلید رمزگشایی داده‌های رمزگذاری‌شده با کلید رمزگذاری کلید را با داده‌های رمزگذاری‌شده الحاق می‌کند.

هم نوع DEK و هم URI مربوط به KEK در کلید KmsEnvelopeAead مشخص شده‌اند. هنگام رمزگشایی، Tink عملیات معکوس را انجام می‌دهد:

  1. کلید DEK رمزگذاری‌شده با KEK را استخراج می‌کند.
  2. درخواستی به KMS شما ارسال می‌کند تا DEK رمزگذاری‌شده با KEK را رمزگشایی کند.
  3. متن رمزگذاری‌شده را به‌طور محلی بااستفاده از DEK رمزگشایی می‌کند.

مثال زیر نشان می‌دهد که چگونه می‌توانید از KmsEnvelopeAead اولیه استفاده کنید.

C++‎

برای این مثال، به افزونه Google Cloud KMS نیاز دارید tink-cc-gcpkms.

// A command-line utility for testing Tink Envelope AEAD with Google Cloud KMS.
#include <fstream>
#include <iostream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>

#include "absl/flags/parse.h"
#include "absl/flags/flag.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "tink/aead.h"
#include "tink/aead/aead_config.h"
#include "tink/aead/aead_key_templates.h"
#include "tink/aead/kms_envelope_aead.h"
#include "tink/integration/gcpkms/gcp_kms_client.h"
#include "proto/tink.pb.h"

ABSL_FLAG(std::string, mode, "", "Mode of operation {encrypt|decrypt}");
ABSL_FLAG(std::string, kek_uri, "", "URI of the KMS Key Encryption Key to use");
ABSL_FLAG(std::string, input_filename, "", "Input file name");
ABSL_FLAG(std::string, output_filename, "", "Output file name");
ABSL_FLAG(std::string, credentials, "",
          "Optional Google Cloud KMS credentials file path; if not specified, "
          "use the default credentials");
ABSL_FLAG(std::string, associated_data, "", "Optional associated data");

namespace {

using ::crypto::tink::Aead;
using ::crypto::tink::AeadKeyTemplates;
using ::crypto::tink::integration::gcpkms::GcpKmsClient;
using ::google::crypto::tink::KeyTemplate;

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

void ValidateParams() {
  // ...
}

absl::StatusOr<std::string> ReadFile(absl::string_view filename) {
  // ...
}

absl::Status WriteToFile(absl::string_view data_to_write,
                         absl::string_view filename) {
  // ...
}

}  // namespace

namespace tink_cc_gcpkms_examples {

void KmsEnvelopAeadCli(absl::string_view mode, absl::string_view kek_uri,
                       absl::string_view input_filename,
                       absl::string_view output_filename,
                       absl::string_view credentials,
                       absl::string_view associated_data) {
  ABSL_CHECK_OK(crypto::tink::AeadConfig::Register());
  // Obtain a remote Aead that can use the KEK.
  absl::StatusOr<std::unique_ptr<GcpKmsClient>> gcp_kms_client =
      GcpKmsClient::New(kek_uri, credentials);
  ABSL_CHECK_OK(gcp_kms_client.status());
  absl::StatusOr<std::unique_ptr<Aead>> remote_aead =
      (*gcp_kms_client)->GetAead(kek_uri);
  ABSL_CHECK_OK(remote_aead.status());
  // Define the DEK template.
  KeyTemplate dek_key_template = AeadKeyTemplates::Aes256Gcm();
  // Create a KmsEnvelopeAead instance.
  absl::StatusOr<std::unique_ptr<Aead>> aead =
      crypto::tink::KmsEnvelopeAead::New(dek_key_template,
                                         *std::move(remote_aead));
  ABSL_CHECK_OK(aead.status());

  absl::StatusOr<std::string> input_file_content = ReadFile(input_filename);
  ABSL_CHECK_OK(input_file_content.status());
  if (mode == kEncrypt) {
    // Generate the ciphertext.
    absl::StatusOr<std::string> encrypt_result =
        (*aead)->Encrypt(*input_file_content, associated_data);
    ABSL_CHECK_OK(encrypt_result.status());
    ABSL_CHECK_OK(WriteToFile(encrypt_result.value(), output_filename));
  } else {  // mode == kDecrypt.
    // Recover the plaintext.
    absl::StatusOr<std::string> decrypt_result =
        (*aead)->Decrypt(*input_file_content, associated_data);
    ABSL_CHECK_OK(decrypt_result.status());
    ABSL_CHECK_OK(WriteToFile(decrypt_result.value(), output_filename));
  }
}

}  // namespace tink_cc_gcpkms_examples

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

  ValidateParams();

  std::string mode = absl::GetFlag(FLAGS_mode);
  std::string kek_uri = absl::GetFlag(FLAGS_kek_uri);
  std::string input_filename = absl::GetFlag(FLAGS_input_filename);
  std::string output_filename = absl::GetFlag(FLAGS_output_filename);
  std::string credentials = absl::GetFlag(FLAGS_credentials);
  std::string associated_data = absl::GetFlag(FLAGS_associated_data);

  ABSL_LOG(INFO) << "Using kek-uri " << kek_uri << " with "
                 << (credentials.empty()
                         ? "default credentials"
                         : absl::StrCat("credentials file ", credentials))
                 << " to envelope " << mode << " file " << input_filename
                 << " with associated data '" << associated_data << "'."
                 << '\n';
  ABSL_LOG(INFO) << "The resulting output will be written to "
                 << output_filename << '\n';

  tink_cc_gcpkms_examples::KmsEnvelopAeadCli(mode, kek_uri, input_filename,
                                             output_filename, credentials,
                                             associated_data);
  return 0;
}

رفتن

import (
	"fmt"
	"log"

	"github.com/tink-crypto/tink-go/v2/aead"
	"github.com/tink-crypto/tink-go/v2/testing/fakekms"
)

// The fake KMS should only be used in tests. It is not secure.
const keyURI = "fake-kms://CM2b3_MDElQKSAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EhIaEIK75t5L-adlUwVhWvRuWUwYARABGM2b3_MDIAE"

func Example_kmsEnvelopeAEAD() {
	// Get a KEK (key encryption key) AEAD. This is usually a remote AEAD to a KMS. In this example,
	// we use a fake KMS to avoid making RPCs.
	client, err := fakekms.NewClient(keyURI)
	if err != nil {
		log.Fatal(err)
	}
	kekAEAD, err := client.GetAEAD(keyURI)
	if err != nil {
		log.Fatal(err)
	}

	// Get the KMS envelope AEAD primitive.
	primitive := aead.NewKMSEnvelopeAEAD2(aead.AES256GCMKeyTemplate(), kekAEAD)

	// Use the primitive.
	plaintext := []byte("message")
	associatedData := []byte("example KMS envelope AEAD encryption")

	ciphertext, err := primitive.Encrypt(plaintext, associatedData)
	if err != nil {
		log.Fatal(err)
	}

	decrypted, err := primitive.Decrypt(ciphertext, associatedData)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(decrypted))
	// Output: message
}

جاوا

برای این مثال، به افزونه Google Cloud KMS نیاز دارید tink-java-gcpkms.

package envelopeaead;

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

import com.google.crypto.tink.Aead;
import com.google.crypto.tink.KmsClient;
import com.google.crypto.tink.aead.AeadConfig;
import com.google.crypto.tink.aead.KmsEnvelopeAead;
import com.google.crypto.tink.aead.PredefinedAeadParameters;
import com.google.crypto.tink.integration.gcpkms.GcpKmsClient;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;

/**
 * A command-line utility for encrypting small files with envelope encryption.
 *
 * <p>It requires the following arguments:
 *
 * <ul>
 *   <li>mode: Can be "encrypt" or "decrypt" to encrypt/decrypt the input to the output.
 *   <li>kek-uri: Use this Cloud KMS' key as the key-encrypting-key for envelope encryption.
 *   <li>gcp-credential-file: Use this JSON credential file to connect to Cloud KMS.
 *   <li>input-file: Read the input from this file.
 *   <li>output-file: Write the result to this file.
 *   <li>[optional] associated-data: Associated data used for the encryption or decryption.
 */
public final class EnvelopeAeadExample {
  private static final String MODE_ENCRYPT = "encrypt";
  private static final String MODE_DECRYPT = "decrypt";

  public static void main(String[] args) throws Exception {
    if (args.length != 5 && args.length != 6) {
      System.err.printf("Expected 5 or 6 parameters, got %d\n", args.length);
      System.err.println(
          "Usage: java EnvelopeAeadExample encrypt/decrypt kek-uri gcp-credential-file"
              + " input-file output-file [associated-data]");
      System.exit(1);
    }
    String mode = args[0];
    String kekUri = args[1];
    String gcpCredentialFilename = args[2];
    byte[] input = Files.readAllBytes(Paths.get(args[3]));
    File outputFile = new File(args[4]);
    byte[] associatedData = new byte[0];
    if (args.length == 6) {
      System.out.println("Associated data!");
      associatedData = args[5].getBytes(UTF_8);
    }
    // Initialise Tink: register all AEAD key types with the Tink runtime
    AeadConfig.register();

    // Read the GCP credentials and create a remote AEAD object.
    Aead remoteAead = null;
    try {
      KmsClient kmsClient = new GcpKmsClient().withCredentials(gcpCredentialFilename);
      remoteAead = kmsClient.getAead(kekUri);
    } catch (GeneralSecurityException ex) {
      System.err.println("Error initializing GCP client: " + ex);
      System.exit(1);
    }

    // Create envelope AEAD primitive using AES256 GCM for encrypting the data
    Aead aead = KmsEnvelopeAead.create(PredefinedAeadParameters.AES256_GCM, remoteAead);

    // Use the primitive to encrypt/decrypt files.
    if (MODE_ENCRYPT.equals(mode)) {
      byte[] ciphertext = aead.encrypt(input, associatedData);
      try (FileOutputStream stream = new FileOutputStream(outputFile)) {
        stream.write(ciphertext);
      }
    } else if (MODE_DECRYPT.equals(mode)) {
      byte[] plaintext = aead.decrypt(input, associatedData);
      try (FileOutputStream stream = new FileOutputStream(outputFile)) {
        stream.write(plaintext);
      }
    } else {
      System.err.println("The first argument must be either encrypt or decrypt, got: " + mode);
      System.exit(1);
    }

    System.exit(0);
  }

  private EnvelopeAeadExample() {}
}

Python

"""A command-line utility for encrypting small files using envelope encryption with GCP."""

from absl import app
from absl import flags
from absl import logging
import tink
from tink import aead
from tink.integration import gcpkms


FLAGS = flags.FLAGS

flags.DEFINE_enum(
    'mode', None, ['encrypt', 'decrypt'], 'The operation to perform.'
)
flags.DEFINE_string(
    'kek_uri', None, 'The Cloud KMS URI of the key encryption key.'
)
flags.DEFINE_string(
    'gcp_credential_path', None, 'Path to the GCP credentials JSON file.'
)
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(
    'associated_data', None, 'Optional associated data used for the encryption.'
)


def main(argv):
  del argv  # Unused.

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

  # Initialise Tink
  aead.register()

  try:
    # Read the GCP credentials and setup client
    client = gcpkms.GcpKmsClient(FLAGS.kek_uri, FLAGS.gcp_credential_path)
  except tink.TinkError as e:
    logging.exception('Error creating GCP KMS client: %s', e)
    return 1

  # Create envelope AEAD primitive using AES256 GCM for encrypting the data
  try:
    remote_aead = client.get_aead(FLAGS.kek_uri)
    env_aead = aead.KmsEnvelopeAead(
        aead.aead_key_templates.AES256_GCM, remote_aead
    )
  except tink.TinkError as e:
    logging.exception('Error creating primitive: %s', e)
    return 1

  with open(FLAGS.input_path, 'rb') as input_file:
    input_data = input_file.read()
    if FLAGS.mode == 'decrypt':
      output_data = env_aead.decrypt(input_data, associated_data)
    elif FLAGS.mode == 'encrypt':
      output_data = env_aead.encrypt(input_data, associated_data)
    else:
      logging.error(
          'Unsupported mode %s. Please choose "encrypt" or "decrypt".',
          FLAGS.mode,
      )
      return 1

    with open(FLAGS.output_path, 'wb') as output_file:
      output_file.write(output_data)


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

قدم بعدی چیست