支持 Vault 的密钥引擎

Spring Vault 附带多个扩展,以支持 Vault 的各种密钥引擎。

具体来说,Spring Vault 附带以下引擎的扩展:

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

键值版本 1(“非版本化密钥”)

`kv` 密钥引擎用于在 Vault 的已配置物理存储中存储任意密钥。

以非版本化方式运行 `kv` 密钥引擎时,仅保留密钥的最新写入值。非版本化 kv 的好处是每个密钥的存储空间更小,因为不存储任何其他元数据或历史记录。此外,对以这种方式配置的后端发出的请求性能更高,因为存储调用更少,并且任何给定请求都不需要锁定。

Spring Vault 附带专用键值 API,用于封装各个键值 API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供 `vault kv get`、`vault 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 支持所有键值操作,例如 `put`、`get`、`delete`、`list`。

或者,由于其直接映射和简单的使用,可以通过 VaultTemplate 使用此 API,因为密钥和响应直接映射到输入和输出密钥。以下示例演示了在 `mykey` 处写入和读取密钥。`kv` 密钥引擎挂载在 `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 的更多详细信息。

键值版本 2(“版本化密钥”)

您可以运行两个版本之一的 `kv` 密钥引擎。本节说明如何使用版本 2。当运行 `kv` 后端的版本 2 时,密钥可以保留可配置数量的版本。您可以检索旧版本的元数据和数据。此外,您可以使用检查和设置操作来避免意外覆盖数据。

键值版本 1(“非版本化密钥”) 类似,Spring Vault 附带专用键值 API,用于封装各个键值 API 实现之间的差异。Spring Vault 附带专用键值 API,用于封装各个键值 API 实现之间的差异。`VaultKeyValueOperations` 遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供 `vault kv get`、`vault 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 支持所有键值操作,例如 `put`、`get`、`delete`、`list`。

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

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` 中存储密钥。
2 在版本化后端中存储数据会返回元数据,例如版本号。
3 版本化键值 API 允许检索由版本号标识的特定版本。
4 版本化键值密钥可以映射到值对象。
5 使用 CAS 更新版本化密钥时,输入必须引用先前获得的版本。

虽然可以通过 `VaultTemplate` 使用 `kv` v2 密钥引擎,但这并不是最方便的方法,因为 API 提供了不同的上下文路径方法以及输入/输出表示方式。具体来说,与实际密钥的交互需要包装和解包数据部分,并在挂载点和密钥之间引入 `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 的更多详细信息。

PKI(公钥基础设施)

`pki` 密钥引擎通过实现证书颁发机构操作来表示证书的后端。

`pki` 密钥引擎生成动态 X.509 证书。使用此密钥引擎,服务可以获取证书,而无需经历生成私钥和 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 密钥 API 的更多详细信息。

令牌认证后端

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

token 身份验证方法是内置的,可在/auth/token自动使用。它允许用户使用令牌进行身份验证,以及创建新令牌、通过令牌吊销密钥等等。

当任何其他身份验证方法返回身份时,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 的更多详细信息。

Transit 后端

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

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

Spring Vault 支持各种 Transit 操作。

  • 密钥创建

  • 密钥重新配置

  • 加密/解密/重新包装

  • 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。它用于为收敛加密提供 nonce 值,并为上下文值提供密钥派生的用途。

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 后端的更多详细信息。