本教程介绍如何创建一个与 Cloud Key Management Service (Cloud KMS) API 通信的应用,以对存储在 Google Cloud 上 Memorystore for Redis 中的内容进行加密。如需详细了解本教程中使用的概念,请参阅与本教程关联的文档:应用级加密:Memorystore for Redis。
本教程面向熟悉 Google Cloud、Linux、密钥管理服务、Redis、Git、Maven 和 Java 的应用开发者和安全专业人员。
本教程的内容采用循序渐进的方式编排,类似于测试驱动开发方法。首先,运行测试。然后引入更改,使测试失败。最后,您将采取步骤消除测试失败。
目标
- 创建并测试一对 Memorystore for Redis 实例。
- 转换数据并将其加载到 Redis 中。
- 创建自己的密钥管理系统 (KMS)。
- 了解加密密钥管理流程。
费用
本教程使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用。
完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理。
准备工作
-
在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。
-
确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能。
- 启用 Compute Engine, Cloud KMS, and Memorystore for Redis API。
-
在 Cloud Console 中,激活 Cloud Shell。
创建项目虚拟机实例
在 Cloud Shell 中,设置
gcloud
命令行工具配置:gcloud config set project PROJECT_NAME gcloud config set compute/zone us-central1-f gcloud config set compute/region us-central1
将 PROJECT_NAME 替换为本教程的 Google Cloud 项目的名称。
创建虚拟机 (VM) 实例:
gcloud compute instances create ale-instance \ --machine-type=n1-standard-1 \ --image-family=debian-10 \ --image-project=debian-cloud \ --boot-disk-size=200GB
在“虚拟机实例”页面的用户控制台中,点击 SSH 以登录虚拟机实例。
系统会打开一个新窗口,本教程称其为“项目窗口”(与“Cloud Shell 窗口”相对)。要完成本教程中的某些步骤,您需要在项目窗口和 Cloud Shell 窗口之间切换。
安装 Git 和 Maven
-
sudo apt-get install git wget maven
您可能会收到以下警告:
E: Could not get lock /var/lib/dpkg/lock
如果虚拟机启动脚本仍在运行,就可能会出现此错误。如果您收到此错误,请等待一两分钟,然后重新运行上一个命令。
安装 Java 开发工具包 (JDK)
在项目窗口中,更改目录:
cd /tmp
下载 Java 安装软件包:
wget https://download.java.net/java/GA/jdk14.0.1/664493ef4a6946b186ff29eb326336a2/7/GPL/openjdk-14.0.1_linux-x64_bin.tar.gz
输出内容类似如下:
--2020-05-17 19:21:39-- https://download.java.net/java/GA/jdk14.0.1/664493ef4a6946b186ff29eb326336a2/7/GPL/openjdk-14.0.1_linux-x64_bin.tar.gz Resolving download.java.net (download.java.net)... 23.213.168.108 Connecting to download.java.net (download.java.net)|23.213.168.108|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 198665889 (189M) [application/x-gzip] Saving to: 'openjdk-14.0.1_linux-x64_bin.tar.gz' oenjdk-14.0.1_linux-x64_ 80%[======================> ] 151.72M 4.86MB/s eta 8s
显示 Java 虚拟机 (JVM) 目录的内容:
ls /usr/lib/jvm/
您可能会收到类似于以下内容的输出:
ls: /usr/lib/jvm/: No such file or directory
此输出表示该文件夹不存在。在这种情况下,请创建以下文件夹:
sudo mkdir /usr/lib/jvm
安装 JDK:
cd /usr/lib/jvm sudo tar xzf /tmp/openjdk-14.0.1_linux-x64_bin.tar.gz export JAVA_HOME=/usr/lib/jvm/jdk-14.0.1/
克隆 tinkCryptoHelper 代码库
在以下步骤中,您将克隆 tinkCryptoHelper(本教程中使用的 Java 应用)的代码库。
在项目窗口中,转到主目录:
cd ~
克隆 tinkCryptoHelper 代码库:
git clone https://github.com/google/tinkCryptoHelper.git cd tinkCryptoHelper
创建 tinkCryptoHelper 应用的构建
在以下步骤中,您可以使用 mvn
工具首次执行从 Java 源代码到可执行的 Java 软件包的 tinkCryptoHelper 构建过程。
在项目窗口中,构建软件包:
mvn package
输出以下列内容结尾:
[INFO] -------------------------------------------------------------------- [INFO] BUILD SUCCESS [INFO] -------------------------------------------------------------------- [INFO] Total time: 13.880 s [INFO] Finished at: 2020-05-17T19:29:13+00:00 [INFO] Final Memory: 19M/116M [INFO] --------------------------------------------------------------------
测试系统
在以下部分中,您将通过运行测试来评估更改配置后系统的行为。代码库中的代码有 14 项集成测试。每项测试的行为各不相同,以确保系统正常运行。重要的是,测试依赖于配置。例如,如果您没有将 Redis 数据库附加到系统,则测试不会尝试测试 Redis 连接。
基本功能测试
创建 tinkCryptoHelper 的构建后,即可测试它的基本功能。由于您没有在线的 Redis 数据库,因此连接测试不会失败。
在项目窗口中,运行测试:
mvn test
输出内容类似如下:
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.google.samples.kms.ale.AppTest AES256_GCM encryption cipherlength: 64 Envelope encryption cipherlength: 224 [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.057 s - in com.google.samples.kms.ale.AppTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] -------------------------------------------------------------------- [INFO] BUILD SUCCESS [INFO] -------------------------------------------------------------------- [INFO] Total time: 7.087 s [INFO] Finished at: 2020-05-17T19:34:21+00:00 [INFO] Final Memory: 12M/56M
关键结果为
Tests run: 14, Failures: 0, Errors: 0, Skipped: 0
。此输出表明所有测试均已成功运行。
测试无实例的 Redis 连接
全集成测试的下一步要求应用 (tinkCryptoHelper) 与 Redis 通信。该应用会从 prefs.xml
文件获取 Redis 连接字符串信息。在您在上一部分中运行的第一个测试期间,tinkCryptoHelper 应用根据 Java 源代码的类结构,在不同位置创建了多个 prefs.xml
文件。
作为练习,您将 prefs.xml
文件中的 redisIsOnline
设置为 true
。Redis 实例不在线,因为您仍需创建它。此测试会导致连接测试失败。
在项目窗口中,列出源代码结构中的 XML 文件,以确保找到并修改正确的
prefs.xml
文件:find ~/.java -name '*.xml'
输出内容类似如下:
/home/USERNAME/.java/.userPrefs/com/google/samples/kms/ale/prefs.xml /home/USERNAME/.java/.userPrefs/com/google/samples/kms/prefs.xml /home/USERNAME/.java/.userPrefs/com/google/samples/prefs.xml /home/USERNAME/.java/.userPrefs/com/google/prefs.xml /home/USERNAME/.java/.userPrefs/com/prefs.xml
在此输出中,
USERNAME
是您登录的用户名。如需准备使用 Maven 测试 Redis,请使用
nano
(或其他 UNIX 编辑器,例如vi
)打开以下文件:nano ~/.java/.userPrefs/com/google/samples/kms/ale/prefs.xml
此配置文件的内容类似如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd"> <map MAP_XML_VERSION="1.0"> <entry key="com.google.samples.kms.ale.AppTest" value="true"/> </map>
在配置文件的
map
部分中,再添加一个条目:<entry key="redisIsOnline" value="true"/>
map
部分现在类似于以下内容:<map MAP_XML_VERSION="1.0"> <entry key="com.google.samples.kms.ale.AppTest" value="true"/> <entry key="redisIsOnline" value="true"/> </map>
保存并退出该文件。
运行测试:
mvn test
由于 Redis 不在线,因此输出会显示如下错误:
[ERROR] AppTest.testRedis3_2:86 » JedisConnection Failed connecting to host 127.0.0.2:... [ERROR] AppTest.testRedis4_0:79 » JedisConnection Failed connecting to host 127.0.0.1:... [ERROR] AppTest.testRedisRoundtripRedis3_2:93->testRedisRoundtripRedisSeries:105->testRedisRoundtripClear:149 » JedisConnection [ERROR] AppTest.testRedisRoundtripRedis4_0:99->testRedisRoundtripRedisSeries:105->testRedisRoundtripClear:149 » JedisConnection [INFO]
您导致了失败的发生。在下一部分中,您将修正该故障。
创建 Redis 实例
在上一步中,测试失败,因为没有可供连接的 Redis 实例。如需更正此问题,您需要创建 Redis 实例。如需使用 tinkCryptoHelper 应用执行测试,您需要两个不同发布版本的 Redis 实例。测试在这两个实例上运行。
创建 Redis 实例
在 Cloud Shell 中,创建两个不同版本的 Redis 实例:
gcloud redis instances create redis32 --redis-version redis_3_2 --region us-central1 gcloud redis instances create redis40 --redis-version redis_4_0 --region us-central1
创建 Redis 3.2 和 Redis 4.0 实例需要几分钟时间。
查看有关实例的信息:
gcloud redis instances list --region us-central1
输出内容类似如下:
INSTANCE_NAME VERSION REGION TIER SIZE_GB HOST PORT NETWORK RESERVED_IP STATUS CREATE_TIME redis32 REDIS_3_2 us-central1 BASIC 1 10.126.141.179 6379 default 10.126.141.176/29 READY 2020-05-17T19:47:15 redis40 REDIS_4_0 us-central1 BASIC 1 10.55.16.163 6379 default 10.55.16.160/29 READY 2020-05-17T19:50:44
从输出中复制 IP 地址。在接下来的步骤中,您需要使用主机 IP 地址。
为 Redis 实例配置 IP 地址
下一步是告知 tinkCryptoHelper 应用在哪里查找 Redis 实例。
在项目窗口中,打开您之前打开的
prefs.xml
文件:nano ~/.java/.userPrefs/com/google/samples/kms/ale/prefs.xml
在配置文件的
map
部分中,添加您之前添加的条目后面的两行内容:<entry key="redisHost3_2" value="IP_ADDRESS3.2" /> <entry key="redisHost4_0" value="IP_ADDRESS4.0" />
请替换以下内容:
IP_ADDRESS3.2
:您之前复制的redis32
实例的值IP_ADDRESS4.0
:您之前复制的redis40
实例的值
保存并退出该文件。
运行测试:
mvn test
输出内容类似如下:
Set of 5000 cleartext values took on average 2µs on host 10.126.141.179 Get of 5000 cleartext values took on average 1µs on host 10.126.141.179 Set of 5000 encrypted values took on average 40µs on host 10.126.141.179 ... Envelope encryption cipherlength: 224 [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.312 s - in com.google.samples.kms.ale.AppTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 12.470 s [INFO] Finished at: 2020-05-17T20:55:46+00:00 [INFO] Final Memory: 12M/56M [INFO] ------------------------------------------------------------------------
您已成功使用 Redis 完成集成测试。
转换数据并将其加载到 Redis
在上一部分中,测试具有几个副作用,其中一个是在 Redis 实例中插入了数据。要完成集成测试,就得先完成此过程。如果您想使用自己的数据测试系统,以下步骤演示了如何使用 tinkCryptoHelper 对数据进行加密,然后使用您自己的数据更新 Redis 数据库。Git 代码库中提供了一个示例 data.csv
文件,但您可以创建自己的示例文件。
在项目窗口中,创建一个 JAR 文件:
mvn assembly:single
将
data.csv
文件转换为与 Redisimport
命令兼容的文件:$JAVA_HOME/bin/java -jar target/cryptoHelper-1.0-jar-with-dependencies.jar data.csv redis_import.txt
redis_import.txt
文件包含一系列命令,可用于将数据插入 Redis。安装包含命令行界面 (CLI) 的 Redis 工具:
sudo apt-get install redis-tools
如果您不想保留 Redis 数据库中的现有数据,可执行以下操作:
使用 Redis CLI 连接到 Redis:
redis-cli -h IP_ADDRESS
将
IP_ADDRESS
替换为您之前复制的其中一个 IP 地址。删除数据库中的数据:
FLUSHALL exit
加载数据:
cat redis_import.txt | redis-cli -h IPADDRESS --pipe
如需测试数据是否存在,请再次连接到 Redis 并获取密钥:
连接到 Redis:
redis-cli -h IP_ADDRESS
获取密钥:
get 1
输出如下所示:
Hello World
如果您使用的是自己的数据,密钥和值很可能不同。
创建自己的 KMS
本教程中的 KMS 加密密钥属于 Google 拥有的 Cloud 项目,名为 tink-test-infrastructure
,而不是您创建的项目。此示例项目有一个 KMS,用于使加密密钥可供公开使用。
如需创建自己的 KMS 实例和密钥,您可以创建一个服务帐号并将该帐号与 Cloud KMS CryptoKey Encrypter/Decrypter IAM 角色关联 (roles/cloudkms.cryptoKeyEncrypterDecrypter
)。使用此角色,服务帐号可以创建 KMS 密钥环并添加密钥。
设置服务帐号
在 Cloud Shell 中,创建一个新的服务帐号:
gcloud iam service-accounts create tink-509 \ --description="Account for KMS" \ --display-name="tinkAccount"
在 Cloud Console 中,转到 IAM 和管理页面。
点击添加,然后将服务帐号
tink-509
添加到 Cloud KMS CryptoKey Encrypter/Decrypter 角色。保存更改并退出 Cloud Console。
在项目窗口中,保存新服务帐号的 JSON 凭据密钥文件:
备份旧的凭据密钥文件:
mv kmsServiceAccountCredentials.json kmsServiceAccountCredentials.json.old
确保您已登录。运行以下命令,然后按照 OAuth 说明操作:
gcloud config set project PROJECT_ID gcloud auth login
将
PROJECT_ID
替换为您的 Cloud 项目 ID。创建新的凭据密钥文件:
gcloud iam service-accounts keys create kmsServiceAccountCredentials.json \ --iam-account tink-509@app-lev-enc.iam.gserviceaccount.com
输出内容类似如下:
created key [c09dfea3892d6c309333f1998caf35845dc50608] of type [json] as [kmsServiceAccountCredentials.json] for [tink-509@app-lev-enc.iam.gserviceaccount.com]
创建 Cloud KMS 密钥环和密钥
在 Cloud Shell 中,创建一个 Cloud KMS 密钥环:
gcloud kms keyrings create "unit-and-integration-testing" --location "global" gcloud kms keys create aead-key --purpose=encryption --location "global" \ --keyring unit-and-integration-testing
将
roles/cloudkms.cryptoKeyEncrypterDecrypter
IAM 角色添加到 IAM 政策:gcloud kms keys add-iam-policy-binding \ aead-key --location global --keyring unit-and-integration-testing \ --member serviceAccount:tink-509@app-lev-enc.iam.gserviceaccount.com \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter
此角色可让您的服务帐号访问密钥。
清除之前的 Redis 数据
在之前的测试中,Redis 数据库填充了示例数据,但加密基于旧的密钥。您需要清除数据并删除旧密钥。
在 Cloud Shell 中,列出所有 Redis 实例:
gcloud redis instances list --region us-central1
记下您创建的实例的 IP 地址。
连接到两个 Redis 实例:
redis-cli -h IP_ADDRESS
运行前述命令两次,并将
IP_ADDRESS
替换为每个实例的 IP 地址。删除数据库中的数据:
FLUSHALL exit
当您在本教程后面部分运行最终测试时,Redis 表中的数据是使用新密钥加密的。
指定 tinkCryptoHelper 查找新 KMS 的位置
在项目窗口中,使用 UNIX 编辑器打开 KMS
pref.xml
文件:nano ~/.java/.userPrefs/com/google/samples/kms/prefs.xml
将以下行添加为第二个条目:
<entry key="keyResourceIdUri" value="gcp-kms://projects/PROJECT_ID/locations/global/keyRings/unit-and-integration-testing/cryptoKeys/aead-key"/>
将
PROJECT_ID
替换为您的 Cloud 项目 ID。
运行最终测试
在项目窗口中,运行测试:
mvn test
输出内容类似如下:
... Envelope encryption cipherlength: 224 [INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.312 s - in com.google.samples.kms.ale.AppTest ...
您已成功创建 KMS 和密钥,然后使用它们测试了 tinkCryptoHelper。
清理
为避免系统因本教程中使用的资源向您的 Google Cloud 帐号收取费用,您可以删除该项目。
删除项目
- 在 Cloud Console 中,转到管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。
后续步骤
- 试用其他 Google Cloud 功能。查阅我们的教程。