本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用字段级加密帮助保护敏感数据
借助 Amazon CloudFront,您可以使用 HTTPS 强制与源服务器 end-to-end 建立安全连接。字段级加密增加了一个额外的安全保护层,可让您在整个系统处理过程中保护特定的数据,以便只有某些应用程序才能查看它。
借助字段级加密,您可以让您的用户安全地向您的 Web 服务器上传敏感信息。用户提供的敏感信息在靠近用户的边缘进行加密,并在整个应用程序堆栈中保持加密状态。此加密确保只有需要数据的应用程序(并且具有用于解密的凭证)能够做到这一点。
要使用字段级加密,请在配置 CloudFront 分配时在 POST 请求中指定要加密的字段集以及用于加密这些字段的公钥。您最多可以在一个请求中加密 10 个数据字段。(您无法使用字段级加密在一个请求中加密所有数据;您必须指定要加密的各个字段。)
如果带有字段级加密的 HTTPS 请求转发到源,并且请求路由经过您的整个源应用程序或子系统,则敏感数据仍然进行加密,从而降低敏感数据泄露或意外丢失的风险。出于业务原因需要访问敏感数据的组件 (例如需要访问信用号码的支付处理系统) 可以使用适当的私有密钥来解密和访问数据。
注意
要使用字段级加密,您的源必须支持分块编码。
CloudFront 字段级加密使用非对称加密,也称为公钥加密。您向提供公钥 CloudFront,您指定的所有敏感数据都会自动加密。您提供的密钥 CloudFront 不能用于解密加密值;只有您的私钥才能解密加密值。
字段级加密概览
以下步骤概述了如何设置字段级加密。有关具体步骤,请参阅设置字段级加密。
-
获取公有密钥/私有密钥对。您必须获取和添加公有密钥,然后再开始在 CloudFront 中设置字段级加密。
-
创建字段级加密配置文件。您在中 CloudFront创建的字段级加密配置文件定义了要加密的字段。
-
创建字段级加密配置。配置指定要用于加密特定数据字段的配置文件(根据请求的内容类型或查询参数)。您还可以为不同方案选择所需的请求转发行为选项。例如,您可以设置请求网址中查询参数指定的配置文件名称不存在时的行为 CloudFront。
-
链接到缓存行为。将配置链接到分配的缓存行为以指定何时 CloudFront 应加密数据。
设置字段级加密
按照以下步骤操作,开始使用字段级加密。要了解有关字段级加密的配额(以前称为限制),请参阅限额。
第 1 步:创建 RSA 密钥对
要开始使用,您必须创建包含公有密钥和私有密钥的 RSA 密钥对。使用公钥可以 CloudFront 加密数据,而私钥可以让源站的组件解密已加密的字段。可以使用 OpenSSL 或其他工具创建密钥对。密钥大小必须为 2048 位。
例如,如果使用 OpenSSL,则可以使用以下命令生成一个长度为 2048 位的密钥对,并将其保存到文件 private_key.pem
中:
openssl genrsa -out private_key.pem 2048
生成的文件同时包含公有密钥和私有密钥。要从该文件中提取公有密钥,请运行以下命令:
openssl rsa -pubout -in private_key.pem -out public_key.pem
公有密钥文件 (public_key.pem
) 包含您在以下步骤中粘贴的已编码密钥值。
第 2 步:将您的公钥添加到 CloudFront
获得 RSA 密钥对后,将您的公钥添加到。 CloudFront
将您的公钥添加到 CloudFront (控制台)
登录Amazon Web Services Management Console并打开 CloudFront 控制台,网址为https://console.amazonaws.cn/cloudfront/v4/home
。 -
在导航窗格中,选择公有密钥。
-
选择 Add public key (添加公有密钥)。
-
对于密钥名称,请为密钥键入唯一的名称。该名称不能包含空格,且只能包含字母数字字符、下划线 (_) 和连字符 (-)。最大字符数为 128。
-
对于 Key value (密钥值),粘贴公有密钥的已编码密钥值,包括
-----BEGIN PUBLIC KEY-----
和-----END PUBLIC KEY-----
行。 -
对于注释,请添加一个可选的注释。例如,您可以包含公有密钥的到期日期。
-
选择 Add。
您可以 CloudFront 通过重复该过程中的步骤来添加更多要使用的密钥。
第 3 步:为字段级加密创建配置文件
向其中添加至少一个公钥后 CloudFront,创建一个配置文件,告知要加密CloudFront 哪些字段。
为字段级加密创建配置文件 (控制台)
-
在导航窗格中,选择字段级加密。
-
选择 Create profile (创建配置文件)。
-
填写以下字段:
- 配置文件名称
-
为配置文件键入唯一名称。该名称不能包含空格,且只能包含字母数字字符、下划线 (_) 和连字符 (-)。最大字符数为 128。
- 公有密钥名称
-
在下拉列表中,选择您在步骤 2 CloudFront 中添加的公钥的名称。 CloudFront 使用密钥加密您在此配置文件中指定的字段。
- 提供商名称
-
键入短语以帮助识别密钥,例如从其获取密钥对的提供商。在应用程序解密数据字段时,需要该信息以及私有密钥。提供商名称不能包含空格,且只能包含字母数字字符、冒号 (:)、下划线 (_) 和连字符 (-)。最大字符数为 128。
- 要匹配的字段名称模式
-
键入数据字段的名称,或者键入用于识别请求中希望 CloudFront 加密的数据字段名称的模式。选择 + 选项以添加您想要使用此密钥加密的所有字段。
对于字段名称模式,您可以键入数据字段的全名,比如 DateOfBirth,或者只键入带有通配符 (*) 的名称的第一部分,例如 CreditCard *。除了可选的通配符 (*) 外,字段名称模式必须仅包含字母数字字符、方括号 ([ 和 ])、句点 (.)、下划线 (_) 和连字符 (-)。
请确保您未针对不同的字段名称模式使用重叠字符。例如,如果您有一个字段名称模式 ABC*,您就不能再添加另一个字段名称模式 AB*。此外,字段名称区分大小写,并且您可以使用的字符的最大数目为 128。
- 评论
-
(可选)键入有关该配置文件的注释。您可以使用的最大字符数为 128。
-
填写字段后,选择 Create profile (创建配置文件)。
-
如果您要添加更多配置文件,请选择添加配置文件。
第 4 步:创建配置
创建一个或多个字段级加密配置文件后,创建一个配置,用于指定请求的内容类型,包括要加密的数据、用于加密的配置文件以及其他指定 CloudFront 要如何处理加密的选项。
例如,当 CloudFront 无法加密数据时,您可以指定在以下情况下是 CloudFront应屏蔽请求还是应将请求转发到您的源:
-
当请求的内容类型不在配置中时 — 如果您尚未向配置中添加内容类型,则可以指定是否 CloudFront 应在不加密数据字段的情况下将具有该内容类型的请求转发到源,还是阻止请求并返回错误。
注意
如果您在配置中添加了内容类型,但尚未指定要用于该类型的配置文件,请 CloudFront 始终将具有该内容类型的请求转发到源。
-
当查询参数中提供的配置文件名称未知时 — 当您使用分配中不存在的配置文件名称来指定
fle-profile
查询参数时,您可以指定是在 CloudFront 不加密数据字段的情况下将请求发送到源,还是阻止请求并返回错误。
在配置中,您还可以指定在 URL 中提供配置文件作为查询参数的操作是否覆盖您已为该查询映射到内容类型的配置文件。默认情况下,如果指定了内容类型,则 CloudFront 使用已映射到内容类型的配置文件。这样一来,您便拥有一个默认使用的配置文件,但您可以决定要强制执行其他配置文件的某些请求。
因此,举例来说,您可以(在您的配置中)指定 SampleProfile
作为要使用的查询参数配置文件。然后https://d1234.cloudfront.net
,你可以使用网址来https://d1234.cloudfront.net?fle-profile=SampleProfile
CloudFront 代替这个请求,而不是你为请求的内容类型设置的个人资料。SampleProfile
您可以为单个账户最多创建 10 个配置,然后将其中一个配置与该账户的任何分配的缓存行为相关联。
为字段级加密创建配置 (控制台)
-
在 Field-level encryption (字段级加密) 页面上,选择 Create configuration (创建配置)。
注意:如果您尚未创建至少一个配置文件,您将不会看到配置创建选项。
-
填写以下字段以指定要使用的配置文件。(某些字段无法更改。)
- 内容类型(无法更改)
-
内容类型将设置为
application/x-www-form-urlencoded
并且无法更改。 - 默认配置文件 ID(可选)
-
在下拉列表中,选择您要映射到 Content type (内容类型) 字段中的内容类型的配置文件。
- 内容格式(无法更改)
-
内容格式将设置为
URLencoded
并且无法更改。
-
如果要更改以下选项的 CloudFront 默认行为,请选中相应的复选框。
- 当请求的内容类型未配置时,将请求转发到原始地址
-
如果您要允许请求转到源,并且您尚未指定要用于请求的内容类型的配置文件,则选中该复选框。
- 使用提供的查询参数覆盖内容类型的配置文件
-
如果您要允许某个查询参数中提供的配置文件覆盖您为内容类型指定的配置文件,则选中该复选框。
-
如果您选中该复选框以允许查询参数覆盖默认配置文件,则您必须完成以下额外配置字段。您最多可以创建五个查询参数映射供查询使用。
- 查询参数
-
键入要包含在
fle-profile
查询参数的 URL 中的值。对于此查询的字段级加密,该值告诉 CloudFront 使用与此查询参数关联的配置文件 ID (在下一个字段中指定)。您可以使用的最大字符数为 128。该值不能包含空格,并且必须仅使用字母数字或以下字符:短划线 (-)、句点 (.)、下划线 (_)、星号 (*)、加号 (+)、百分号 (%)。
- 配置文件 ID
-
在下拉列表中,选择您要与为 Query argument (查询参数) 键入的值相关联的配置文件。
- 当查询参数中指定的配置文件不存在时,将请求转发到源
-
如果要允许请求转到源,并且没有在 CloudFront 中定义在查询参数中指定的配置文件,请选择该复选框。
第 5 步:将配置添加到缓存行为
要使用字段级加密,需要将配置链接到分配的缓存行为,方法为添加配置 ID 作为分配的值。
重要
要将字段级加密配置链接到缓存行为,必须将分配配置为始终使用 HTTPS,并接受来自查看器的 HTTP POST
和 PUT
请求。也就是说,必须满足以下条件:
-
缓存行为的 Viewer Protocol Policy (查看器协议策略) 必须设置为 Redirect HTTP to HTTPS (将 HTTP 重定向到 HTTPS) 或 HTTPS Only (仅 HTTPS)。(在Amazon CloudFormation或 CloudFront API 中,
ViewerProtocolPolicy
必须设置为redirect-to-https
或https-only
。) -
缓存行为的 Allowed HTTP Methods(允许的 HTTP 方法)必须设置为 GET、HEAD、OPTIONS、PUT、POST、PATCH、DELETE。(在Amazon CloudFormation或 CloudFront API 中,
AllowedMethods
必须设置为GET
、HEAD
、OPTIONS
、PUT
、POST
、PATCH
、DELETE
。 这些可以按任意顺序指定。) -
源设置的 Origin Protocol Policy (源协议策略) 必须设置为 Match Viewer (匹配查看器) 或 HTTPS Only (仅 HTTPS)。(在Amazon CloudFormation或 CloudFront API 中,
OriginProtocolPolicy
必须设置为match-viewer
或https-only
。)
有关更多信息,请参阅您创建或更新分配时指定的值。
在源解密数据字段
CloudFront 使用加密数据字段。Amazon Encryption SDK在您的整个应用程序堆栈中,数据保持加密,并且只能由具有用于解密数据的凭证的应用程序进行访问。
加密后,密码文本采用 base64 编码。当您的应用程序解密源上的文本时,应用程序必须首先解码密码文本,然后使用 Amazon 加密开发工具包解密数据。
以下代码示例说明了应用程序如何解密源上的数据。请注意以下几点:
-
为简化示例,此示例从工作目录中的文件内加载公有密钥和私有密钥 (采用 DER 格式)。实际上,您会将私有密钥存储在安全的离线位置 (如离线硬件安全模块),并将公有密钥分配给您的开发团队。
-
CloudFront 在加密数据时使用特定的信息,并且应在源端使用相同的参数集来解密数据。初始化时 CloudFront使用的参数 MasterKey 包括以下内容:
-
PROVIDER_NAME:您在创建字段级加密配置文件时指定该值。在此处使用相同的值。
-
KEY_NAME:您在将公钥上传到时为其创建了一个名称 CloudFront,然后在配置文件中指定了密钥名称。在此处使用相同的值。
-
算法: CloudFront
RSA/ECB/OAEPWithSHA-256AndMGF1Padding
用作加密算法,因此必须使用相同的算法来解密数据。
-
-
如果您在运行以下示例程序时使用密码文本作为输入,则解密的数据会输出到您的控制台。有关更多信息,请参阅 加密开发工具包中的 Java 代码示例Amazon。
代码示例
import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import org.apache.commons.codec.binary.Base64; import com.amazonaws.encryptionsdk.AwsCrypto; import com.amazonaws.encryptionsdk.CryptoResult; import com.amazonaws.encryptionsdk.jce.JceMasterKey; /** * Sample example of decrypting data that has been encrypted by CloudFront field-level encryption. */ public class DecryptExample { private static final String PRIVATE_KEY_FILENAME = "private_key.der"; private static final String PUBLIC_KEY_FILENAME = "public_key.der"; private static PublicKey publicKey; private static PrivateKey privateKey; // CloudFront uses the following values to encrypt data, and your origin must use same values to decrypt it. // In your own code, for PROVIDER_NAME, use the provider name that you specified when you created your field-level // encryption profile. This sample uses 'DEMO' for the value. private static final String PROVIDER_NAME = "DEMO"; // In your own code, use the key name that you specified when you added your public key to CloudFront. This sample // uses 'DEMOKEY' for the key name. private static final String KEY_NAME = "DEMOKEY"; // CloudFront uses this algorithm when encrypting data. private static final String ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; public static void main(final String[] args) throws Exception { final String dataToDecrypt = args[0]; // This sample uses files to get public and private keys. // In practice, you should distribute the public key and save the private key in secure storage. populateKeyPair(); System.out.println(decrypt(debase64(dataToDecrypt))); } private static String decrypt(final byte[] bytesToDecrypt) throws Exception { // You can decrypt the stream only by using the private key. // 1. Instantiate the SDK final AwsCrypto crypto = new AwsCrypto(); // 2. Instantiate a JCE master key final JceMasterKey masterKey = JceMasterKey.getInstance( publicKey, privateKey, PROVIDER_NAME, KEY_NAME, ALGORITHM); // 3. Decrypt the data final CryptoResult <byte[], ? > result = crypto.decryptData(masterKey, bytesToDecrypt); return new String(result.getResult()); } // Function to decode base64 cipher text. private static byte[] debase64(final String value) { return Base64.decodeBase64(value.getBytes()); } private static void populateKeyPair() throws Exception { final byte[] PublicKeyBytes = Files.readAllBytes(Paths.get(PUBLIC_KEY_FILENAME)); final byte[] privateKeyBytes = Files.readAllBytes(Paths.get(PRIVATE_KEY_FILENAME)); publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(PublicKeyBytes)); privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); } }