支持 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
。
或者,由于其直接映射和简单使用,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 的更多详细信息。
键值版本 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 | 将机密存储在 elvis 中,该机密在 secret/ 挂载下可用。 |
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 Key-Value 版本 2 API 的更多详细信息。
PKI(公钥基础设施)
pki
secrets 引擎通过实现证书颁发机构操作来表示证书的后端。
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 密钥库。密钥库有广泛的用途,这使得此格式适合于配置(例如 HTTP 客户端、数据库驱动程序或 SSL 安全的 HTTP 服务器)。 |
4 | CertificateBundle 允许通过 Java 密码扩展 API 直接访问私钥、公钥和颁发者证书。 |
5 | 一旦证书不再使用(或被泄露),您可以通过其序列号吊销它。Vault 将吊销的证书包含在其 CRL 中。 |
您可以在 Vault 参考文档中找到有关 Vault PKI secrets API 的更多详细信息。
令牌身份验证后端
token
身份验证方法是内置的,并且自动在 /auth/token
中可用。它允许用户使用令牌进行身份验证,还可以创建新令牌、按令牌吊销 secrets,等等。
当任何其他身份验证方法返回身份时,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 。 |
前面的示例对加密操作使用简单的字符串。虽然这是一种简单的方法,但它存在字符集错误配置的风险,并且不二进制安全。当明文对数据使用二进制表示形式(例如图像、压缩数据或二进制数据结构)时,需要二进制安全性。
要加密和解密二进制数据,请使用可以保存二进制值的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
。它用于为融合加密提供随机数,并为上下文值提供利用密钥派生的功能。
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 后端的更多详细信息。