跳到主要内容
版本:4.1.0

Vault 机密引擎

Doubao 中英对照 Vault Secrets Engines

Spring Vault 附带几个扩展来支持 Vault 的各种机密引擎。

具体来说,Spring Vault 附带了以下扩展:

你可以通过 VaultTemplate 上的方法直接使用所有其他机密引擎(VaultTemplate.read(…)、VaultTemplate.write(…))。

Key-Value 版本 1("无版本机密")​

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

以非版本方式运行 kv 机密引擎时,只保留键最近写入的值。非版本 kv 的好处是每个键的存储大小更小,因为不会存储额外的元数据或历史记录。此外,以这种方式配置的机密引擎的请求性能更高,因为存储调用更少,任何给定请求都没有锁定。

Spring Vault 附带一个专用的 Key-Value API,以封装各个 Key-Value API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 的设计。这是 Vault 的主要命令行工具,提供 vault kv get、vault kv put 等命令。

你可以通过指定版本和挂载路径将此 API 用于两种 Key-Value 引擎版本。以下示例使用 Key-Value 版本 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 支持所有 Key-Value 操作,例如 put、get、delete、list。

或者,该 API 可以通过 VaultTemplate 使用,因为它具有直接的映射和简单的用法,键和响应直接映射到输入和输出键。以下示例说明了在 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 Key-Value 版本 1 API 的详细信息。

备注

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

Key-Value 版本 2("有版本机密")​

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

与 Key-Value 版本 1("无版本机密") 类似,Spring Vault 附带一个专用的 Key-Value API,以封装各个 Key-Value API 实现之间的差异。Spring Vault 附带一个专用的 Key-Value API,以封装各个 Key-Value API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 的设计。这是 Vault 的主要命令行工具,提供 vault kv get、vault kv put 等命令。

你可以通过指定版本和挂载路径将此 API 用于两种 Key-Value 引擎版本。以下示例使用 Key-Value 版本 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 支持所有 Key-Value 操作,例如 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);
  • 在 secret/ 挂载下可用的 elvis 处存储机密。

  • 在有版本的机密引擎中存储数据会返回元数据,例如版本号。

  • 有版本的 Key-Value API 允许检索由版本号标识的特定版本。

  • 有版本的键-值机密可以映射到值对象。

  • 使用 CAS 更新有版本的机密时,输入必须引用先前获得的版本。

虽然可以通过 VaultTemplate 使用 kv v2 机密引擎。但它不是最方便的方法,因为该 API 对上下文路径以及输入/输出的表示提供了不同的方法。具体来说,与实际机密的交互需要包裹和解包 data 部分,并在挂载和机密键之间引入 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 Key-Value 版本 2 API 的详细信息。

备注

Vault 通过 Vault 的 sys/internal/ui/mounts/… 端点确定挂载路径。请确保你的策略允许访问该路径,否则你将无法使用 Key-Value 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
  • 使用 VaultCertificateRequest 构建器构造证书请求。

  • 向 Vault 请求证书。Vault 充当证书颁发机构,并以签名的 X.509 证书作为响应。实际响应是一个 CertificateBundle。

  • 你可以直接将生成的证书作为 Java KeyStore 获取,其中包含公钥和私钥以及颁发者证书。KeyStore 有广泛的用途,这使该格式适合配置(例如 HTTP 客户端、数据库驱动或 SSL 保护的 HTTP 服务器)。

  • CertificateBundle 允许通过 Java Cryptography Extension API 直接访问私钥以及公钥和颁发者证书。

  • 一旦证书不再使用(或已被泄露),你可以通过其序列号吊销它。Vault 会将已吊销的证书包含在其 CRL 中。

你可以在 Vault 参考文档中找到更多关于 Vault PKI 机密 API 的详细信息。

令牌认证方法​

此方法是一种认证方法,不与实际机密交互。相反,它提供对访问令牌管理的访问。你可以在认证方法章节中阅读更多关于基于令牌的认证的内容。

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

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

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

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
  • 通过应用角色默认值创建一个令牌。

  • 使用构建器 API,你可以为要请求的令牌定义细粒度的设置。请求令牌会返回一个 VaultToken,用作 Vault 令牌的值对象。

  • 你可以通过 Token API 续期令牌。通常,这由 SessionManager 完成,以跟踪 Vault 会话令牌。

  • 如果需要,可以通过 Token API 吊销令牌。通常,这由 SessionManager 完成,以跟踪 Vault 会话令牌。

你可以在 Vault 参考文档中找到更多关于 Vault Token Auth Method 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
  • 首先,我们需要一个密钥开始。每个密钥都需要指定类型。aes128-gcm96 支持加密、解密、密钥派生和收敛加密,其中此示例需要加密和解密。

  • 接下来,我们加密一个包含应加密的明文的 String。输入 String 使用默认 Charset 将字符串编码为其二进制表示。请求令牌会返回一个 VaultToken,用作 Vault 令牌的值对象。encrypt 方法返回 Base64 编码的密文,通常以 vault: 开头。

  • 要将密文解密为明文,请调用 decrypt 方法。它解密密文并返回使用默认字符集解码的 String。

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

要加密和解密二进制数据,请使用可以保存二进制值的 Plaintext 和 Ciphertext 值对象:

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
  • 假设密钥 my-aes-key 已经就位,我们加密 Plaintext 对象。作为回报,encrypt 方法返回一个 Ciphertext 对象。

  • Ciphertext 对象可以直接用于解密,并返回一个 Plaintext 对象。

Plaintext 和 Ciphertext 带有一个上下文对象 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
  • 签名需要非对称密钥。你可以使用任何椭圆曲线密码学或 RSA 密钥类型。密钥创建后,你就拥有了创建签名所需的全部先决条件。

  • 签名为明文消息创建。返回的 Signature 包含使用 Base64 字符的 ASCII 安全字符串。

  • 要验证签名,验证需要 Signature 对象和明文消息。返回值告诉你签名是否有效。

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