Vault 机密引擎
Spring Vault 附带几个扩展来支持 Vault 的各种机密引擎。
具体来说,Spring Vault 附带了以下扩展:
- Key-Value 版本 1("无版本机密")
- Key-Value 版本 2("有版本机密")
- PKI(公钥基础设施)
- 令牌认证方法
- Transform(企业功能)
- Transit 后端
- System 后端
你可以通过 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
你可以在 Vault 参考文档中找到更多关于 Vault Transit 后端 的详细信息。