Vault Secrets Engines
Spring Vault 提供了多个扩展来支持 Vault 的各种密钥引擎。
Spring Vault 特别附带了以下扩展:
-
转换(企业功能)
-
系统后台
你可以通过 VaultTemplate 上的方法直接使用所有其他密钥引擎(VaultTemplate.read(…),VaultTemplate.write(…))。
键值版本 1(“未版本化的密钥”)
kv secrets 引擎用于在 Vault 配置的物理存储中存储任意密钥。
当以非版本化方式运行 kv secrets 引擎时,只会保留最近一次写入的键值。 非版本化 kv 的好处是每个键的存储大小减少了,因为没有存储额外的元数据或历史记录。 此外,发送到以此方式配置的 secrets 引擎的请求性能更高,因为存储调用更少,并且任何给定请求都不会发生锁定。
Spring Vault 提供了一个专用的键值(Key-Value)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 secrets引擎挂载在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(“带版本的密钥”)
您可以运行 kv secrets 引擎的两个版本之一。 本节介绍如何使用版本 2。当运行版本 2 的 kv 引擎时,一个密钥可以保留可配置数量的版本。 您可以检索旧版本的元数据和数据。 此外,您可以使用检查并设置操作来避免意外覆盖数据。
Similar to 键值版本 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 | 将密钥存储在 elvis 中,该密钥在 secret/ 挂载下可用。 |
| 2 | 将数据存储在带版本控制的密钥引擎中时,会返回诸如版本号之类的元数据。 |
| 3 | 版本化的键值 API 允许通过版本号检索特定版本。 |
| 4 | 带有版本控制的键值秘密可以映射到值对象中。 |
| 5 | 使用 CAS 更新带版本的密钥时,输入必须引用之前获取的版本。 |
虽然可以通过VaultTemplate使用kvv2版本的密钥引擎是可行的。 但这并不是最方便的方法,因为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 的详细信息。
Vault 通过 Vault 的 sys/internal/ui/mounts/… 端点确定挂载路径。确保您的策略允许访问该路径,否则您将无法使用键值 API。 |
公钥基础设施(PKI)
pki 密钥引擎通过实现证书颁发机构的操作,代表了一个用于证书的密钥引擎。
PKI secrets 引擎生成动态的 X.509 证书。 通过此 secrets 引擎,服务可以获取证书,而无需经历通常的手动流程:生成私钥和 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 secrets API 的详细信息。
Tokens认证方法
这是一种不与实际密钥交互的认证方法。 相反,它提供对访问Tokens管理的访问。 你可以阅读更多关于基于Tokens的认证的信息,在认证方法章节中。
内置的 token 认证方法在 /auth/token 处自动可用。 它允许用户使用Tokens进行身份验证,还可以创建新Tokens、通过Tokens撤销密钥等更多功能。
当任何其他身份验证方法返回身份时,Vault 核心将调用Tokens方法为该身份创建一个新的唯一Tokens。
您还可以使用Tokens存储来绕过任何其他身份验证方法。您可以直接创建Tokens,还可以对Tokens执行各种其他操作,例如续订和吊销。
Spring Vault 使用此后端来续订和撤销由配置的身份验证方法提供的会话Tokens。
以下示例展示了如何在应用程序中请求、续订和吊销 Vault Tokens:
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 | 通过应用角色默认值创建一个Tokens。 |
| 2 | 使用构建器 API,您可以为请求的Tokens定义细粒度的设置。 请求Tokens将返回一个 VaultToken,它用作 Vault Tokens的值对象。 |
| 3 | 你可以通过 Token API 更新Tokens。通常,这是通过 SessionManager 来跟踪 Vault 会话Tokens完成的。 |
| 4 | 如果需要,可以通过 Token API 撤销Tokens。通常,这是由 SessionManager 完成的,以跟踪 Vault 会话Tokens。 |
你可以在 Vault 参考文档中找到更多关于 Vault Token 身份验证方法 API 的详细信息。
后端传输
传输秘密引擎处理传输中数据的加密功能。 Vault 不会存储发送到该秘密引擎的数据。 它也可以被视为“加密即服务”或“加密作为服务”。 传输秘密引擎还可以签名和验证数据、生成数据的哈希值和 HMAC,并充当随机字节源。
主要的使用场景是通过传输加密应用程序的数据,同时将这些加密后的数据存储在某些主要的数据存储中。 这减轻了应用程序开发人员正确加密和解密的负担,并将该负担转移给 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将字符串编码为其二进制表示形式。 请求Tokens会返回一个VaultToken,它作为VaultTokens的值对象使用。 encrypt方法返回Base64编码的密文,通常以vault:开头。 |
| 3 | 要将密文解密为明文,请调用 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)
| 1 | 假设键my-aes-key已经就位,我们正在加密Plaintext对象。 作为返回,encrypt方法返回一个Ciphertext对象。 |
| 2 | Ciphertext 对象可直接用于解密,并返回一个 Plaintext 对象。 |
Plaintext 和 Ciphertext 带有一个上下文对象,VaultTransitContext。 它用于为收敛加密提供一个随机数(nonce)值,并为利用密钥派生提供上下文值。
Transit允许对明文进行签名并验证给定明文的签名。 签名操作需要使用非对称密钥,通常采用椭圆曲线加密(ECC)或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)
你可以在 Vault 参考文档中找到有关 Vault Transit 后端 的更多详细信息。