支持 Vault 的秘密引擎

Spring Vault 附带了几个扩展,以支持 Vault 的各种 Secret Engines。

具体来说,Spring Vault 附带了针对以下 Secret Engines 的扩展:

您可以通过 VaultTemplate 上的方法直接使用所有其他后端(VaultTemplate.read(…)VaultTemplate.write(…))。

键值版本 1(“未版本化 Secret”)

kv Secret Engine 用于在 Vault 配置的物理存储中存储任意 Secret。

当以非版本化方式运行 kv Secret Engine 时,只保留键的最新写入值。非版本化 kv 的好处是每个键的存储大小减少,因为不存储额外的元数据或历史记录。此外,以此方式配置的后端请求性能更高,因为存储调用更少,并且任何给定请求都没有锁定。

Spring Vault 附带了一个专用的键值 API,用于封装各个键值 API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供诸如 vault kv getvault kv put 等命令。

您可以通过指定版本和挂载路径将此 API 与两个键值引擎版本一起使用。以下示例使用键值版本 1

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultKeyValueOperations keyValueOperations = operations.opsForKeyValue("secret",
							VaultKeyValueOperationsSupport.KeyValueBackend.KV_1);

keyValueOperations.put("elvis", Collections.singletonMap("password", "409-52-2002"));

VaultResponse read = keyValueOperations.get("elvis");
read.getRequiredData().get("social-security-number");

VaultKeyValueOperations 支持所有键值操作,例如 putgetdeletelist

或者,由于其直接映射和简单使用,可以通过 VaultTemplate 使用该 API,因为键和响应直接映射到输入和输出键。以下示例演示了在 mykey 处写入和读取 Secret。kv Secret Engine 挂载在 secret

VaultOperations operations = new VaultTemplate(new VaultEndpoint());

operations.write("secret/elvis", Collections.singletonMap("social-security-number", "409-52-2002"));

VaultResponse read = operations.read("secret/elvis");
read.getRequiredData().get("social-security-number");

您可以在 Vault 参考文档中找到有关 Vault 键值版本 1 API 的更多详细信息。

Vault 通过 Vault 的 sys/internal/ui/mounts/… 端点确定挂载路径。请确保您的策略允许访问该路径,否则您将无法使用键值 API。

键值版本 2(“版本化 Secret”)

您可以运行 kv Secret Engine 的两个版本之一。本节解释了如何使用版本 2。当运行 kv 后端版本 2 时,一个键可以保留可配置数量的版本。您可以检索旧版本的元数据和数据。此外,您可以使用检查和设置操作来避免无意中覆盖数据。

键值版本 1(“未版本化 Secret”) 类似,Spring Vault 附带了一个专用的键值 API,用于封装各个键值 API 实现之间的差异。Spring Vault 附带了一个专用的键值 API,用于封装各个键值 API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供诸如 vault kv getvault kv put 等命令。

您可以通过指定版本和挂载路径将此 API 与两个键值引擎版本一起使用。以下示例使用键值版本 2

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultKeyValueOperations keyValueOperations = operations.opsForKeyValue("secret",
							VaultKeyValueOperationsSupport.KeyValueBackend.KV_2);

keyValueOperations.put("elvis", Collections.singletonMap("social-security-number", "409-52-2002"));

VaultResponse read = keyValueOperations.get("elvis");
read.getRequiredData().get("social-security-number");

VaultKeyValueOperations 支持所有键值操作,例如 putgetdeletelist

您还可以与版本化键值 API 的具体细节进行交互。如果您想获取特定 Secret 或需要访问元数据,这将非常有用。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultVersionedKeyValueOperations versionedOperations = operations.opsForVersionedKeyValue("secret");

Versioned.Metadata metadata = versionedOperations.put("elvis",							(1)
					Collections.singletonMap("social-security-number", "409-52-2002"));

Version version = metadata.getVersion();												(2)

Versioned<Object> ssn = versionedOperations.get("elvis", Version.from(42));				(3)

Versioned<SocialSecurityNumber> mappedSsn = versionedOperations.get("elvis",			(4)
											Version.from(42), SocialSecurityNumber.class);

Versioned<Map<String,String>> versioned = Versioned.create(Collections					(5)
						.singletonMap("social-security-number", "409-52-2002"),
						Version.from(42));

versionedOperations.put("elvis", version);
1 将 Secret 存储在 elvis 中,该 Secret 在 secret/ 挂载下可用。
2 将数据存储在版本化后端会返回元数据,例如版本号。
3 版本化键值 API 允许检索由版本号标识的特定版本。
4 版本化键值 Secret 可以映射到值对象中。
5 当使用 CAS 更新版本化 Secret 时,输入必须引用之前获取的版本。

虽然可以通过 VaultTemplate 使用 kv v2 Secret Engine。但这不是最方便的方法,因为该 API 提供了不同的上下文路径方法以及输入/输出的表示方式。具体来说,与实际 Secret 的交互需要对数据部分进行包装和解包,并在挂载和 Secret 键之间引入一个 data/ 路径段。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());

operations.write("secret/data/elvis", Collections.singletonMap("data",
			Collections.singletonMap("social-security-number", "409-52-2002")));

VaultResponse read = operations.read("secret/data/ykey");
Map<String,String> data = (Map<String, String>) read.getRequiredData().get("data");
data.get("social-security-number");

您可以在 Vault 参考文档中找到有关 Vault 键值版本 2 API 的更多详细信息。

Vault 通过 Vault 的 sys/internal/ui/mounts/… 端点确定挂载路径。请确保您的策略允许访问该路径,否则您将无法使用键值 API。

PKI(公钥基础设施)

pki Secret Engine 通过实现证书颁发机构操作来表示证书的后端。

PKI Secret Engine 生成动态 X.509 证书。通过此 Secret Engine,服务无需经过通常生成私钥和 CSR、提交给 CA 并等待验证和签名过程完成的手动过程即可获取证书。Vault 内置的身份验证和授权机制提供了验证功能。

Spring Vault 通过 VaultPkiOperations 支持颁发、签名、吊销证书和 CRL 检索。所有其他 PKI 功能都可以通过 VaultOperations 使用。

以下示例简要解释了如何颁发和吊销证书

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultPkiOperations pkiOperations = operations.opsForPki("pki");

VaultCertificateRequest request = VaultCertificateRequest.builder()								(1)
			.ttl(Duration.ofHours(48))
			.altNames(Arrays.asList("prod.dc-1.example.com", "prod.dc-2.example.com"))
			.withIpSubjectAltName("1.2.3.4")
			.commonName("hello.example.com")
			.build();

VaultCertificateResponse response = pkiOperations.issueCertificate("production", request); 		(2)
CertificateBundle certificateBundle = response.getRequiredData();

KeyStore keyStore = certificateBundle.createKeyStore("my-keystore");							(3)

KeySpec privateKey = certificateBundle.getPrivateKeySpec();										(4)
X509Certificate certificate = certificateBundle.getX509Certificate();
X509Certificate caCertificate = certificateBundle.getX509IssuerCertificate();

pkiOperations.revoke(certificateBundle.getSerialNumber());										(5)
1 使用 VaultCertificateRequest 构建器构建证书请求。
2 从 Vault 请求证书。Vault 充当证书颁发机构,并响应一个签名的 X.509 证书。实际响应是一个 CertificateBundle
3 您可以直接将生成的证书作为 Java KeyStore 获取,其中包含公钥和私钥以及颁发者证书。KeyStore 具有广泛的用途,这使得此格式适合配置(例如 HTTP 客户端、数据库驱动程序或 SSL 安全的 HTTP 服务器)。
4 CertificateBundle 允许通过 Java 密码学扩展 API 直接访问私钥以及公钥和颁发者证书。
5 一旦证书不再使用(或已泄露),您可以通过其序列号吊销它。Vault 将吊销的证书包含在其 CRL 中。

您可以在 Vault 参考文档中找到有关 Vault PKI Secret API 的更多详细信息。

令牌认证后端

此后端是一个不与实际 Secret 交互的认证后端。相反,它提供对访问令牌管理的访问。您可以在认证方法章节中阅读有关基于令牌的认证的更多信息。

token 认证方法是内置的,并自动在 /auth/token 处可用。它允许用户使用令牌进行认证,以及创建新令牌、按令牌撤销 Secret 等。

当任何其他认证方法返回身份时,Vault 核心会调用令牌方法为该身份创建一个新的唯一令牌。

您还可以使用令牌存储绕过任何其他认证方法。您可以直接创建令牌,并对令牌执行各种其他操作,例如续订和撤销。

Spring Vault 使用此后端来续订和撤销由配置的 认证方法 提供的会话令牌。

以下示例展示了如何在应用程序中请求、续订和撤销 Vault 令牌

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultTokenOperations tokenOperations = operations.opsForToken();

VaultTokenResponse tokenResponse = tokenOperations.create();                          (1)
VaultToken justAToken = tokenResponse.getToken();

VaultTokenRequest tokenRequest = VaultTokenRequest.builder().withPolicy("policy-for-myapp")
									.displayName("Access tokens for myapp")
									.renewable()
									.ttl(Duration.ofHours(1))
									.build();

VaultTokenResponse appTokenResponse = tokenOperations.create(tokenRequest);          (2)
VaultToken appToken = appTokenResponse.getToken();

tokenOperations.renew(appToken);                                                     (3)

tokenOperations.revoke(appToken);                                                    (4)
1 通过应用角色默认值创建令牌。
2 使用构建器 API,您可以为要请求的令牌定义细粒度设置。请求令牌会返回一个 VaultToken,它用作 Vault 令牌的值对象。
3 您可以通过令牌 API 续订令牌。通常,这由 SessionManager 完成,以跟踪 Vault 会话令牌。
4 如果需要,可以通过令牌 API 撤销令牌。通常,这由 SessionManager 完成,以跟踪 Vault 会话令牌。

您可以在 Vault 参考文档中找到有关 Vault 令牌认证方法 API 的更多详细信息。

传输后端

传输 Secret Engine 处理传输中数据的加密功能。Vault 不存储发送到此 Secret Engine 的数据。它也可以被视为“加密即服务”或“加密作为服务”。传输 Secret Engine 还可以对数据进行签名和验证,生成数据的哈希和 HMAC,并充当随机字节源。

传输的主要用例是加密应用程序中的数据,同时仍将加密数据存储在某个主要数据存储中。这减轻了应用程序开发人员正确加密和解密的负担,并将负担转移到 Vault 的运营商身上。

Spring Vault 支持广泛的传输操作

  • 密钥创建

  • 密钥重新配置

  • 加密/解密/重新封装

  • HMAC 计算

  • 签名和签名验证

transit 中的所有操作都以密钥为中心。Transit 引擎支持密钥版本控制和 多种密钥类型。请注意,密钥类型可能会限制可使用的操作。

以下示例展示了如何创建密钥以及如何加密和解密数据

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultTransitOperations transitOperations = operations.opsForTransit("transit");

transitOperations.createKey("my-aes-key", VaultTransitKeyCreationRequest.ofKeyType("aes128-gcm96"));	(1)

String ciphertext = transitOperations.encrypt("my-aes-key", "plaintext to encrypt");					(2)

String plaintext = transitOperations.decrypt("my-aes-key", ciphertext);									(3)
1 首先,我们需要一个密钥。每个密钥都需要指定类型。aes128-gcm96 支持加密、解密、密钥派生和会聚加密,其中我们在此示例中需要加密和解密。
2 接下来,我们加密一个包含要加密的纯文本的 String。输入 String 使用默认的 Charset 将字符串编码为其二进制表示。请求令牌返回一个 VaultToken,它用作 Vault 令牌的值对象。encrypt 方法返回 Base64 编码的密文,通常以 vault: 开头。
3 要将密文解密为纯文本,请调用 decrypt 方法。它解密密文并返回一个使用默认字符集解码的 String

前面的示例使用简单的字符串进行加密操作。虽然这是一种简单的方法,但它存在字符集配置错误的风险,并且不是二进制安全的。当纯文本使用二进制表示形式(例如图像、压缩数据或二进制数据结构)时,需要二进制安全性。

要加密和解密二进制数据,请使用可以包含二进制值的 PlaintextCiphertext 值对象

byte [] plaintext = "plaintext to encrypt".getBytes();

Ciphertext ciphertext = transitOperations.encrypt("my-aes-key", Plaintext.of(plaintext));			(1)

Plaintext decrypttedPlaintext = transitOperations.decrypt("my-aes-key", ciphertext);				(2)
1 假设密钥 my-aes-key 已经存在,我们正在加密 Plaintext 对象。作为回报,encrypt 方法返回一个 Ciphertext 对象。
2 Ciphertext 对象可以直接用于解密并返回 Plaintext 对象。

PlaintextCiphertext 附带一个上下文对象 VaultTransitContext。它用于为 会聚加密 提供一个随机数,并用于上下文值以利用密钥派生。

Transit 允许对纯文本进行签名并验证给定纯文本的签名。签名操作需要非对称密钥,通常使用椭圆曲线密码学或 RSA。

签名使用公钥/私钥拆分来确保真实性。
签名者使用其私钥创建签名。否则,任何人都可以以您的名义签署消息。验证者使用公钥部分验证签名。实际签名通常是哈希值。

在内部,计算哈希并使用私钥加密以创建最终签名。验证解密签名消息,计算其自己的纯文本哈希,并比较两个哈希值以检查签名是否有效。
byte [] plaintext = "plaintext to sign".getBytes();

transitOperations.createKey("my-ed25519-key", VaultTransitKeyCreationRequest.ofKeyType("ed25519"));	(1)

Signature signature = transitOperations.sign("my-ed25519-key", Plaintext.of(plaintext));			(2)

boolean valid = transitOperations.verify("my-ed25519-key", Plaintext.of(plaintext), signature);		(3)
1 签名需要非对称密钥。您可以使用任何椭圆曲线密码学或 RSA 密钥类型。一旦密钥创建完成,您就拥有了创建签名的所有先决条件。
2 为纯文本消息创建签名。返回的 Signature 包含一个使用 Base64 字符的 ASCII 安全字符串。
3 要验证签名,验证需要一个 Signature 对象和纯文本消息。作为返回值,您将获得签名是否有效。

您可以在 Vault 参考文档中找到有关 Vault Transit 后端 的更多详细信息。

© . This site is unofficial and not affiliated with VMware.