Supporting for Vault’s Secret Engines

Spring Vault 附带了多个扩展以支持 Vault 的各种秘密引擎。 具体来说,Spring Vault 附带了以下扩展:

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

Key-Value Version 1 ("unversioned secrets")

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

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

Spring Vault 附带一个专门的键值 API,以封装单个键值 API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供诸如 vault kv getvault kv put 等命令。

你可以通过指定版本和装载路径使用这两个键值引擎版本。以下示例使用键值版本 1:

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.util.Collections;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponse;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class KeyValueV1 {

	void vaultOperations() {

		// tag::vaultOperations[]
		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");
		// end::vaultOperations[]
	}

	void keyValueApi() {

		// tag::keyValueApi[]
		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");
		// end::keyValueApi[]
	}

}

VaultKeyValueOperations 支持所有键值操作,例如 putgetdeletelist

另外,由于其直接映射和简单用法,API 还可以通过 VaultTemplate 使用,因为键和响应直接映射到输入和输出键。以下示例演示在 mykey 编写和读取一个密码。kv 密码引擎装载在 secret

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.util.Collections;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponse;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class KeyValueV1 {

	void vaultOperations() {

		// tag::vaultOperations[]
		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");
		// end::vaultOperations[]
	}

	void keyValueApi() {

		// tag::keyValueApi[]
		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");
		// end::keyValueApi[]
	}

}

您可以在 Vault 参考文档中找到有关 Vault Key-Value version 1 API 的更多详细信息。

Key-Value Version 2 ("versioned secrets")

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

类似于 Key-Value Version 1 ("unversioned secrets"),Spring Vault 附带一个专门的键值 API,以封装单个键值 API 实现之间的差异。Spring Vault 附带一个专门的键值 API,以封装单个键值 API 实现之间的差异。VaultKeyValueOperations 遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供诸如 vault kv getvault kv put 等命令。

你可以通过指定版本和装载路径使用这两个键值引擎版本。以下示例使用键值版本 2:

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.util.Collections;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultVersionedKeyValueOperations;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.Versioned;
import org.springframework.vault.support.Versioned.Version;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class KeyValueV2 {

	void vaultOperations() {

		// tag::vaultOperations[]
		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");
		// end::vaultOperations[]
	}

	void keyValueApi() {

		// tag::keyValueApi[]
		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");
		// end::keyValueApi[]
	}

	void versionedApi() {

		// tag::versionedApi[]
		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);
		// end::versionedApi[]
	}

	static class SocialSecurityNumber{

		@JsonProperty("social-security-number")
		String ssn;
	}

}

VaultKeyValueOperations 支持所有键值操作,例如 putgetdeletelist

你还可以与版本化键值 API 的具体细节进行交互。如果你想要获取一个特定密码,或者你需要访问元数据,这很有用。

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.util.Collections;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultVersionedKeyValueOperations;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.Versioned;
import org.springframework.vault.support.Versioned.Version;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class KeyValueV2 {

	void vaultOperations() {

		// tag::vaultOperations[]
		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");
		// end::vaultOperations[]
	}

	void keyValueApi() {

		// tag::keyValueApi[]
		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");
		// end::keyValueApi[]
	}

	void versionedApi() {

		// tag::versionedApi[]
		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);
		// end::versionedApi[]
	}

	static class SocialSecurityNumber{

		@JsonProperty("social-security-number")
		String ssn;
	}

}
1 将秘密存储在`elvis`下,可通过 `secret/`挂载访问该存储。
2 将数据存储在版本化后端中,将返回版本号等元数据。
3 版本化的键值 API 允许按版本号检索特定版本。
4 版本化键值秘密可以映射到值对象中。
5 在使用 CAS 更新版本化秘密时,输入必须引用以前获得的版本。

虽然可以通过 VaultTemplate 使用 kv v2 密码引擎。这不是最方便的方法,因为 API 为上下文路径和输入/输出表示方式提供了一个不同的方法。具体来说,与实际秘密的交互需要对数据部分进行包装和解包,并在装载和秘密键之间引入一个 data/ 路径段。

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.util.Collections;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonProperty;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultVersionedKeyValueOperations;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.Versioned;
import org.springframework.vault.support.Versioned.Version;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class KeyValueV2 {

	void vaultOperations() {

		// tag::vaultOperations[]
		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");
		// end::vaultOperations[]
	}

	void keyValueApi() {

		// tag::keyValueApi[]
		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");
		// end::keyValueApi[]
	}

	void versionedApi() {

		// tag::versionedApi[]
		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);
		// end::versionedApi[]
	}

	static class SocialSecurityNumber{

		@JsonProperty("social-security-number")
		String ssn;
	}

}

您可以在 Vault 参考文档中找到有关 Vault Key-Value version 2 API 的更多详细信息。

PKI (Public Key Infrastructure)

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

PKI 密码引擎生成动态 X.509 证书。使用此密码引擎,服务无需经过生成私钥和 CSR、提交给 CA 以及等待验证和签名过程完成的通常手动过程即可获得证书。Vault 的内置身份验证和授权机制提供验证功能。

Spring Vault 支持通过 VaultPkiOperations 颁发、签名、撤销证书和检索 CRL。所有其他 PKI 功能都可以通过 VaultOperations 使用。

以下示例简要说明了如何颁发和撤销证书:

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.security.spec.KeySpec;
import java.time.Duration;
import java.util.Arrays;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultPkiOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.CertificateBundle;
import org.springframework.vault.support.VaultCertificateRequest;
import org.springframework.vault.support.VaultCertificateResponse;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class PKI {

	void pkiApi() {

		// tag::pkiApi[]
		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)
		// end::pkiApi[]
	}

}
1 使用 `VaultCertificateRequest`构建器构造证书请求。
2 从 Vault 请求证书。Vault 充当证书颁发机构,并使用签名 X.509 证书进行响应。实际的响应是 CertificateBundle
3 你可以将生成的证书直接获取为 Java 密钥库,其中包含公钥和私钥以及颁发者证书。KeyStore 具有广泛的用途,这使得该格式适用于配置(例如,HTTP 客户端、数据库驱动程序或 SSL 安全的 HTTP 服务器)。
4 `CertificateBundle`允许直接通过 Java Cryptography Extension API 访问私钥以及公钥和颁发者证书。
5 一旦证书不再使用(或受到破坏),你可以通过其序列号撤销该证书。Vault 将撤销的证书包含在其 CRL 中。

可以在 Vault 参考文档中找到有关@ {s13} 的更多详细信息。

Token Authentication Backend

此后台是一个不与实际秘密交互的身份验证后台。相反,它允许访问访问令牌管理。你可以在 authentication methods chapter 中阅读有关 Token-based authentication 的更多信息。

token 身份验证方法是内置的,并且在 /auth/token 中自动可用。它使用户能够使用令牌进行身份验证,以及创建新令牌、按令牌撤销秘密等。

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

您还可以使用令牌存储来规避任何其他认证方法。您可以直接创建令牌,以及对令牌执行各种其他操作,例如续订和吊销。

Spring Vault 使用此后端续订和吊销由已配置的 authentication method 提供的会话令牌。

以下示例展示了如何从应用程序中请求、续订和吊销 Vault 令牌:

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.security.spec.KeySpec;
import java.time.Duration;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTokenOperations;
import org.springframework.vault.support.CertificateBundle;
import org.springframework.vault.support.VaultCertificateResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.support.VaultTokenResponse;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class Token {

	void tokenApi() {

		// tag::tokenApi[]
		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)

		// end::tokenApi[]
	}

}
1 通过应用角色默认值创建令牌。
2 使用构建器 API,您可以为要请求的令牌定义细粒度的设置。请求令牌会返回 VaultToken,其用作 Vault 令牌的值对象。
3 您可以通过令牌 API 续订令牌。通常,这通过 SessionManager 来执行,以跟踪 Vault 会话令牌。
4 如果需要,可以通过令牌 API 撤销令牌。通常,这通过 SessionManager 来执行,以跟踪 Vault 会话令牌。

可以在 Vault 参考文档中找到有关@ {s14} 的更多详细信息。

Transit Backend

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

传输的主要用例是从应用程序加密数据,同时仍将加密数据存储在某个主数据存储中。这解决了应用程序开发人员的正确加密和解密负担,并将负担转嫁到了 Vault 操作员身上。

Spring Vault 支持广泛的传输操作:

  • Key creation

  • Key reconfiguration

  • Encryption/Decryption/Rewrapping

  • HMAC computation

  • Signing and signature verification

@ {s15} 中的所有操作都围绕密钥展开。Transit 引擎支持密钥和@ {s16} 的版本控制。请注意,密钥类型可能对可以使用哪些操作施加限制。

以下示例展示了如何创建密钥以及如何加密和解密数据:

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext;
import org.springframework.vault.support.Plaintext;
import org.springframework.vault.support.Signature;
import org.springframework.vault.support.VaultTransitKeyCreationRequest;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class Transit {

	void encryptSimple() {

		// tag::encryptSimple[]
		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)

		// end::encryptSimple[]
	}

	void encryptPlaintext(VaultTransitOperations transitOperations) {

		// tag::encryptPlaintext[]
		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)

		// end::encryptPlaintext[]
	}

	void signVerify(VaultTransitOperations transitOperations) {

		// tag::signVerify[]
		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)

		// end::signVerify[]
	}

}
1 首先,我们需要一个密钥。每个密钥都需要指定类型。aes128-gcm96 支持加密、解密、密钥派生和融合加密,其中我们需要加密和解密。
2 接下来,我们加密 String,它包含应加密的纯文本。输入 String 使用默认 Charset 将字符串编码为其二进制表示。请求令牌会返回 VaultToken,其用作 Vault 令牌的值对象。encrypt 方法返回 Base64 编码的密文,通常以 vault: 开头。
3 要将密文解密为纯文本,请调用 decrypt 方法。它解密密文并返回使用默认字符集解码的 String

上述示例使用简单字符串进行加密操作。虽然这种方法很简单,但它有字符集错误配置的风险,并且对于二进制文件不安全。当纯文本使用二进制表示来表示诸如图像、压缩数据或二进制数据结构等数据时,需要二进制安全性。

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

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext;
import org.springframework.vault.support.Plaintext;
import org.springframework.vault.support.Signature;
import org.springframework.vault.support.VaultTransitKeyCreationRequest;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class Transit {

	void encryptSimple() {

		// tag::encryptSimple[]
		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)

		// end::encryptSimple[]
	}

	void encryptPlaintext(VaultTransitOperations transitOperations) {

		// tag::encryptPlaintext[]
		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)

		// end::encryptPlaintext[]
	}

	void signVerify(VaultTransitOperations transitOperations) {

		// tag::signVerify[]
		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)

		// end::signVerify[]
	}

}
1 假设密钥 my-aes-key 已准备就绪,我们正在加密 Plaintext 对象。作为回报,encrypt 方法返回 Ciphertext 对象。
2 Ciphertext 对象可以直接用于解密,并返回 Plaintext 对象。

@ {s17}@ {s18} 附带一个上下文对象@ {s19}。它用于为@ {s20} 提供一个随机值,并提供一个上下文值以利用密钥派生。

传输允许对纯文本进行签名并验证给定纯文本的签名。签名操作需要不对称密钥,通常使用椭圆曲线密码术或 RSA。

签名使用公钥/私钥分割来确保真实性。签名者使用其私钥创建签名。否则,任何人都会以你的名义签署消息。验证者使用公钥部分验证签名。实际签名通常是一个哈希值。在内部,使用私钥计算和加密散列以创建最终签名。验证解密签名消息,为明文计算它们自己的散列,然后比较两个散列值以检查签名是否有效。

/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.vault.documentation;

import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext;
import org.springframework.vault.support.Plaintext;
import org.springframework.vault.support.Signature;
import org.springframework.vault.support.VaultTransitKeyCreationRequest;

/**
 * @author Mark Paluch
 */
//@formatter:off
public class Transit {

	void encryptSimple() {

		// tag::encryptSimple[]
		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)

		// end::encryptSimple[]
	}

	void encryptPlaintext(VaultTransitOperations transitOperations) {

		// tag::encryptPlaintext[]
		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)

		// end::encryptPlaintext[]
	}

	void signVerify(VaultTransitOperations transitOperations) {

		// tag::signVerify[]
		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)

		// end::signVerify[]
	}

}
1 签名需要非对称密钥。您可以使用任何椭圆曲线加密或 RSA 密钥类型。创建密钥后,您便具备创建签名的所有必要条件。
2 签名是针对纯文本消息创建的。返回的 Signature 包含使用 Base64 字符的 ASCII 安全字符串。
3 要验证签名,此验证需要一个签名对象和纯文本消息。作为返回值,您将获得签名是否有效。

可以在 Vault 参考文档中找到有关@ {s21} 的更多详细信息。