diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..8a3e6a9 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,51 @@ +cached_key: &cached_key gradle-cached-{{ checksum "build.gradle" }} + +default_job: &default_job + docker: + - image: circleci/openjdk:8-jdk + +version: 2 +jobs: + build: + <<: *default_job + steps: + - checkout + - restore_cache: + key: *cached_key + - run: ./gradlew resolveAllDependencies + - save_cache: + key: *cached_key + paths: + - ~/.gradle + test: + <<: *default_job + steps: + - checkout + - restore_cache: + key: *cached_key + - run: ./gradlew test + deploy: + <<: *default_job + steps: + - checkout + - restore_cache: + key: *cached_key + - run: ./gradlew publishToGithub + +# workflows +workflows: + version: 2 + build-test-deploy: + jobs: + - build + - test: + requires: + - build + - deploy: + requires: + - test + filters: + branches: + only: + - staging + - master diff --git a/build.gradle b/build.gradle index 6e9beaf..eed6b97 100644 --- a/build.gradle +++ b/build.gradle @@ -20,11 +20,32 @@ buildscript { apply plugin: 'java' apply plugin: 'maven' -apply plugin: 'com.jfrog.bintray' apply plugin: 'maven-publish' +apply plugin: 'idea' +apply plugin: 'git-repo' -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +gitPublishConfig { + org = "LeapXpert" + repo = "lxp-artifacts" + branch = "master" +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + + repositories { + maven { + url "file://${gitPublishConfig.home}/${gitPublishConfig.org}/${gitPublishConfig.repo}/releases" + } + } +} task copyProjectVersion() { def releaseVersion = version @@ -32,11 +53,21 @@ task copyProjectVersion() { ant.replace(file: "$buildDir/resources/main/java-vault-client.properties", token: "@@VAULT_CLIENT_RELEASE@@", value: releaseVersion) } } + +task resolveAllDependencies { + doLast { + configurations.all { + if (it.canBeResolved) { + it.resolve() + } + } + } +} + tasks.jar.dependsOn copyProjectVersion apply from: file('gradle/dependencies.gradle') apply from: file('gradle/check.gradle') apply from: file('gradle/integration.gradle') -apply from: file('gradle/bintray.gradle') -group = groupId \ No newline at end of file +group = groupId diff --git a/gradle.properties b/gradle.properties index 62042c2..782e941 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,6 @@ # limitations under the License. # -version=2.0.1 +version=2.0.2 groupId=com.nike artifactId=vault-client diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle index 2b57116..342f41f 100644 --- a/gradle/buildscript.gradle +++ b/gradle/buildscript.gradle @@ -16,10 +16,12 @@ repositories { jcenter() + maven { url "https://jitpack.io" } } dependencies { classpath "net.saliman:gradle-cobertura-plugin:2.3.0" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.6.3' + classpath group: 'com.github.LeapXpert', name: 'gradle-git-repo-plugin', version: '2.0.2-FORK' } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 018e90b..9db9cc9 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -20,9 +20,9 @@ repositories { dependencies { compile "org.apache.commons:commons-lang3:3.4" - compile "com.squareup.okhttp3:okhttp:3.9.0" + compile "com.squareup.okhttp3:okhttp:3.9.1" compile "com.google.code.gson:gson:2.5" - compile "com.google.code.findbugs:jsr305:3.0.1" + compile "com.google.code.findbugs:jsr305:3.0.2" compile "org.slf4j:slf4j-api:1.7.25" testCompile "junit:junit:4.12" @@ -32,4 +32,4 @@ dependencies { testCompile "org.assertj:assertj-core:2.3.0" testCompile "com.squareup.okhttp3:mockwebserver:3.7.0" testCompile "commons-io:commons-io:2.4" -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3b1fc4f..c6d4df5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-4.1-bin.zip diff --git a/src/main/java/com/nike/vault/client/VaultAdminClient.java b/src/main/java/com/nike/vault/client/VaultAdminClient.java index bd8fa63..23447b0 100644 --- a/src/main/java/com/nike/vault/client/VaultAdminClient.java +++ b/src/main/java/com/nike/vault/client/VaultAdminClient.java @@ -302,10 +302,7 @@ public VaultClientTokenResponse lookupToken(final String token) { parseAndThrowErrorResponse(response); } - final Type mapType = new TypeToken>() { - }.getType(); - final Map rootData = parseResponseBody(response, mapType); - return getGson().fromJson(getGson().toJson(rootData.get("data")), VaultClientTokenResponse.class); + return parseResponse(response, VaultClientTokenResponse.class); } /** @@ -337,18 +334,4 @@ public void disableAuditBackend(final String path) { parseAndThrowErrorResponse(response); } } - - /** - * Barebones method that can be used to make any call to Vault. The caller is responsible for interpreting - * and de-serializing the response. The Gson instance used by the client is accessible via {@link #getGson()} - * - * @param path Path to the resource - * @param method HTTP method - * @param requestBody Request body to be serialized as JSON. Set to null if no request body - * @return HTTP response object - */ - public Response execute(final String path, final String method, final Object requestBody) { - final HttpUrl url = buildUrl("", path); - return execute(url, method, requestBody); - } } diff --git a/src/main/java/com/nike/vault/client/VaultClient.java b/src/main/java/com/nike/vault/client/VaultClient.java index 5e5fd85..c9383cb 100644 --- a/src/main/java/com/nike/vault/client/VaultClient.java +++ b/src/main/java/com/nike/vault/client/VaultClient.java @@ -152,10 +152,7 @@ public VaultListResponse list(final String path) { parseAndThrowErrorResponse(response); } - final Type mapType = new TypeToken>() { - }.getType(); - final Map rootData = parseResponseBody(response, mapType); - return gson.fromJson(gson.toJson(rootData.get("data")), VaultListResponse.class); + return parseResponse(response, VaultListResponse.class); } /** @@ -234,10 +231,7 @@ public VaultClientTokenResponse lookupSelf() { parseAndThrowErrorResponse(response); } - final Type mapType = new TypeToken>() { - }.getType(); - final Map rootData = parseResponseBody(response, mapType); - return gson.fromJson(gson.toJson(rootData.get("data")), VaultClientTokenResponse.class); + return parseResponse(response, VaultClientTokenResponse.class); } /** @@ -405,6 +399,14 @@ protected void parseAndThrowErrorResponse(final Response response) { } } + protected T parseResponse(Response response, Class responseType) { + final Type mapType = new TypeToken>() { + }.getType(); + final Map rootData = parseResponseBody(response, mapType); + return getGson() + .fromJson(getGson().toJson(rootData.get("data")), responseType); + } + /** * POJO for representing error response body from Vault. */ @@ -424,4 +426,18 @@ protected String responseBodyAsString(Response response) { return "ERROR failed to print response body as str: " + ioe.getMessage(); } } + + /** + * Barebones method that can be used to make any call to Vault. The caller is responsible for interpreting + * and de-serializing the response. The Gson instance used by the client is accessible via {@link #getGson()} + * + * @param path Path to the resource + * @param method HTTP method + * @param requestBody Request body to be serialized as JSON. Set to null if no request body + * @return HTTP response object + */ + public Response execute(final String path, final String method, final Object requestBody) { + final HttpUrl url = buildUrl("", path); + return execute(url, method, requestBody); + } } diff --git a/src/main/java/com/nike/vault/client/VaultClientFactory.java b/src/main/java/com/nike/vault/client/VaultClientFactory.java index 3946b49..4dcfcc1 100644 --- a/src/main/java/com/nike/vault/client/VaultClientFactory.java +++ b/src/main/java/com/nike/vault/client/VaultClientFactory.java @@ -308,4 +308,187 @@ public static VaultAdminClient getAdminClient(final UrlResolver vaultUrlResolver .build(), headers.build()); } + + + + + /** + * Basic factory method that will build a Vault admin client that + * looks up the Vault URL from one of the following places: + *
    + *
  • Environment Variable - VAULT_ADDR
  • + *
  • Java System Property - vault.addr
  • + *
+ * Default recommended credential provider and http client are used. + * + * @return Vault admin client + */ + public static VaultCryptoClient getCryptoClient() { + return getCryptoClient(new DefaultVaultUrlResolver(), + new DefaultVaultCredentialsProviderChain(), + DEFAULT_MAX_REQUESTS, + DEFAULT_MAX_REQUESTS, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_HEADERS + ); + } + + /** + * Factory method allows setting of the Vault URL resolver, but will use + * the default recommended credentials provider chain and http client. + * + * @param vaultUrlResolver URL resolver for Vault + * @return Vault admin client + */ + public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver) { + return getCryptoClient(vaultUrlResolver, + new DefaultVaultCredentialsProviderChain(), + DEFAULT_MAX_REQUESTS, + DEFAULT_MAX_REQUESTS, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_HEADERS + ); + } + + /** + * Factory method that allows for a user defined Vault URL resolver and credentials provider. + * + * @param vaultUrlResolver URL resolver for Vault + * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault + * @return Vault admin client + */ + public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider vaultCredentialsProvider) { + return getCryptoClient(vaultUrlResolver, + vaultCredentialsProvider, + DEFAULT_MAX_REQUESTS, + DEFAULT_MAX_REQUESTS, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_HEADERS + ); + } + + /** + * Factory method that allows for a user defined Vault URL resolver and credentials provider. + * + * @param vaultUrlResolver URL resolver for Vault + * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault + * @param maxRequestsPerHost Max Requests per Host used by the dispatcher + * @return Vault admin client + */ + public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider vaultCredentialsProvider, + final int maxRequestsPerHost) { + return getCryptoClient(vaultUrlResolver, + vaultCredentialsProvider, + DEFAULT_MAX_REQUESTS, + maxRequestsPerHost, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_HEADERS); + } + + /** + * Factory method that allows a user to define default HTTP headers to be added to every HTTP request made from the + * VaultClient. The user can also define their Vault URL resolver and credentials provider. + * + * @param vaultUrlResolver URL resolver for Vault + * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault + * @param defaultHeaders Map of default header names and values to add to every HTTP request + * @return Vault client + */ + public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider vaultCredentialsProvider, + final Map defaultHeaders) { + return getCryptoClient(vaultUrlResolver, + vaultCredentialsProvider, + DEFAULT_MAX_REQUESTS, + DEFAULT_MAX_REQUESTS, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + defaultHeaders); + } + + /** + * Factory method that allows the user to completely configure the VaultClient. + * + * @param vaultUrlResolver URL resolver for Vault + * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault + * @param maxRequestsPerHost Max Requests per Host used by the dispatcher + * @param defaultHeaders Map of default header names and values to add to every HTTP request + * @return Vault admin client + */ + public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider vaultCredentialsProvider, + final int maxRequestsPerHost, + final Map defaultHeaders) { + return getCryptoClient(vaultUrlResolver, + vaultCredentialsProvider, + DEFAULT_MAX_REQUESTS, + maxRequestsPerHost, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + DEFAULT_TIMEOUT, + defaultHeaders); + } + + /** + * Factory method that allows the user to completely configure the VaultClient. + * + * @param vaultUrlResolver URL resolver for Vault + * @param vaultCredentialsProvider Credential provider for acquiring a token for interacting with Vault + * @param maxRequests Max HTTP Requests allowed in-flight + * @param maxRequestsPerHost Max HTTP Requests per Host + * @param connectTimeoutMillis HTTP connect timeout in milliseconds + * @param readTimeoutMillis HTTP read timeout in milliseconds + * @param writeTimeoutMillis HTTP write timeout in milliseconds + * @param defaultHeaders Map of default header names and values to add to every HTTP request + * @return Vault admin client + */ + public static VaultCryptoClient getCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider vaultCredentialsProvider, + final int maxRequests, + final int maxRequestsPerHost, + final int connectTimeoutMillis, + final int readTimeoutMillis, + final int writeTimeoutMillis, + final Map defaultHeaders) { + if (defaultHeaders == null) { + throw new IllegalArgumentException("Default headers cannot be null."); + } + + Dispatcher dispatcher = new Dispatcher(); + dispatcher.setMaxRequests(maxRequests); + dispatcher.setMaxRequestsPerHost(maxRequestsPerHost); + + + List connectionSpecs = new ArrayList<>(); + connectionSpecs.add(TLS_1_2_OR_NEWER); + // for unit tests + connectionSpecs.add(CLEARTEXT); + + Headers.Builder headers = new Headers.Builder(); + for (Map.Entry header : defaultHeaders.entrySet()) { + headers.add(header.getKey(), header.getValue()); + } + + return new VaultCryptoClient(vaultUrlResolver, + vaultCredentialsProvider, + new OkHttpClient.Builder() + .connectTimeout(connectTimeoutMillis, DEFAULT_TIMEOUT_UNIT) + .writeTimeout(writeTimeoutMillis, DEFAULT_TIMEOUT_UNIT) + .readTimeout(readTimeoutMillis, DEFAULT_TIMEOUT_UNIT) + .dispatcher(dispatcher) + .connectionSpecs(connectionSpecs) + .build(), + headers.build()); + } } diff --git a/src/main/java/com/nike/vault/client/VaultCryptoClient.java b/src/main/java/com/nike/vault/client/VaultCryptoClient.java new file mode 100644 index 0000000..8ddaaa2 --- /dev/null +++ b/src/main/java/com/nike/vault/client/VaultCryptoClient.java @@ -0,0 +1,170 @@ +package com.nike.vault.client; + +import static com.nike.vault.client.model.VaultCreateKeyRequest.TYPE_AES256_GCM96; + +import com.google.gson.reflect.TypeToken; +import com.nike.vault.client.auth.VaultCredentialsProvider; +import com.nike.vault.client.http.HttpMethod; +import com.nike.vault.client.http.HttpStatus; +import com.nike.vault.client.model.VaultAsymmetricKeyResponse; +import com.nike.vault.client.model.VaultCreateKeyRequest; +import com.nike.vault.client.model.VaultDecryptDataRequest; +import com.nike.vault.client.model.VaultDecryptDataResponse; +import com.nike.vault.client.model.VaultEncryptDataRequest; +import com.nike.vault.client.model.VaultEncryptDataResponse; +import com.nike.vault.client.model.VaultKeyExportResponse; +import com.nike.vault.client.model.VaultKeyResponse; +import com.nike.vault.client.model.VaultSymmetricKeyResponse; +import java.lang.reflect.Type; +import java.util.Map; +import javax.annotation.Nonnull; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Response; + +public class VaultCryptoClient extends VaultClient { + + private static final String TRANSIT_PATH_PREFIX = "v1/transit/"; + + public static final String KEY_TYPE_ENCRYPTION_KEY = "encryption-key"; + public static final String KEY_TYPE_SIGNING_KEY = "signing-key"; + public static final String KEY_TYPE_HMAC_KEY = "hmac-key"; + + /** + * Explicit constructor that allows for full control over construction of the Vault client. + * + * @param vaultUrlResolver URL resolver for Vault + * @param credentialsProvider Credential provider for acquiring a token for interacting with Vault + * @param httpClient HTTP client for calling Vault + */ + public VaultCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider credentialsProvider, + final OkHttpClient httpClient) { + super(vaultUrlResolver, credentialsProvider, httpClient); + } + + /** + * Explicit constructor that allows for full control over construction of the Vault client. + * + * @param vaultUrlResolver URL resolver for Vault + * @param credentialsProvider Credential provider for acquiring a token for interacting with Vault + * @param httpClient HTTP client for calling Vault + * @param defaultHeaders Default HTTP headers to be included in each request made by the returned VaultClient + */ + public VaultCryptoClient(final UrlResolver vaultUrlResolver, + final VaultCredentialsProvider credentialsProvider, + final OkHttpClient httpClient, + final Headers defaultHeaders) { + super(vaultUrlResolver, credentialsProvider, httpClient, defaultHeaders); + } + + /** + * Creates a new key. + * + * @param name Key name + * @param vaultCreateKeyRequest Request object with optional parameters + */ + public void createKey(@Nonnull final String name, @Nonnull final VaultCreateKeyRequest vaultCreateKeyRequest) { + final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "keys/" + name); + final Response response = execute(url, HttpMethod.POST, vaultCreateKeyRequest); + + if (response.code() != HttpStatus.NO_CONTENT) { + parseAndThrowErrorResponse(response); + } + } + + /** + * Retrieve key information + * + * @param name Name of the key to lookup + * @return Key information response, can be either Symmetric or Asymmetric keys + */ + public VaultKeyResponse getKeyInfo(@Nonnull final String name) { + final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "keys/" + name); + final Response response = execute(url, HttpMethod.GET, null); + + if (response.code() != HttpStatus.OK) { + parseAndThrowErrorResponse(response); + } + + final Type mapType = new TypeToken>() { + }.getType(); + final Map rootData = parseResponseBody(response, mapType); + VaultKeyResponse keyResponse = getGson() + .fromJson(getGson().toJson(rootData.get("data")), VaultKeyResponse.class); + switch (keyResponse.getType()) { + case TYPE_AES256_GCM96: + return getGson() + .fromJson(getGson().toJson(rootData.get("data")), VaultSymmetricKeyResponse.class); + default: + return getGson() + .fromJson(getGson().toJson(rootData.get("data")), VaultAsymmetricKeyResponse.class); + } + + } + + /** + * Retrieve key information + * + * @param name Name of the key to lookup + * @param type Type of key to export - KEY_TYPE_ENCRYPTION_KEY/KEY_TYPE_SIGNING_KEY/KEY_TYPE_HMAC_KEY + * @return Raw encryption key, if key was created with permission to export + */ + public VaultKeyExportResponse exportKey(@Nonnull final String name, @Nonnull final String type) { + final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "export/" + type + "/" + name); + final Response response = execute(url, HttpMethod.GET, null); + + if (response.code() != HttpStatus.OK) { + parseAndThrowErrorResponse(response); + } + + return parseResponse(response, VaultKeyExportResponse.class); + } + + public void deleteKey(@Nonnull final String name) { + final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "keys/" + name); + final Response response = execute(url, HttpMethod.DELETE, null); + + if (response.code() != HttpStatus.NO_CONTENT) { + parseAndThrowErrorResponse(response); + } + } + + /** + * Encrypt data with a key stored in Vault + * @param keyName Name of the key to use for the cryptographic operation + * @param encryptRequest Encryption data + * @return Encryption response + */ + @Nonnull + public VaultEncryptDataResponse encrypt(@Nonnull String keyName, @Nonnull VaultEncryptDataRequest encryptRequest) { + final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "encrypt/" + keyName); + final Response response = execute(url, HttpMethod.POST, encryptRequest); + + if (response.code() != HttpStatus.OK) { + parseAndThrowErrorResponse(response); + } + + return parseResponse(response, VaultEncryptDataResponse.class); + } + + /** + * Decrypt data with a key stored in Vault + * @param keyName Name of the key to use for the cryptographic operation + * @param decryptRequest Decryption data + * @return Decryption response + */ + @Nonnull + public VaultDecryptDataResponse decrypt(@Nonnull String keyName, @Nonnull VaultDecryptDataRequest decryptRequest) { + final HttpUrl url = buildUrl(TRANSIT_PATH_PREFIX, "decrypt/" + keyName); + final Response response = execute(url, HttpMethod.POST, decryptRequest); + + if (response.code() != HttpStatus.OK) { + parseAndThrowErrorResponse(response); + } + + return parseResponse(response, VaultDecryptDataResponse.class); + } + +} diff --git a/src/main/java/com/nike/vault/client/model/VaultAsymmetricKeyResponse.java b/src/main/java/com/nike/vault/client/model/VaultAsymmetricKeyResponse.java new file mode 100644 index 0000000..3d758cb --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultAsymmetricKeyResponse.java @@ -0,0 +1,94 @@ +package com.nike.vault.client.model; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultAsymmetricKeyResponse extends VaultKeyResponse { + public class AsymmetricKeyInformation { + public class AsymmetricKey { + private String creationTime; + private String name; + private String publicKey; + + public String getCreationTime() { + return creationTime; + } + + public void setCreationTime(String creationTime) { + this.creationTime = creationTime; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("creationTime", creationTime) + .append("name", name) + .append("publicKey", publicKey) + .toString(); + } + } + + @SerializedName("1") + private AsymmetricKey keyData; + + public AsymmetricKey getKeyData() { + return keyData; + } + + public void setKeyData( + AsymmetricKey keyData) { + this.keyData = keyData; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("keyData", keyData) + .toString(); + } + } + private AsymmetricKeyInformation keys; + + public AsymmetricKeyInformation getKeys() { + return keys; + } + + public void setKeys(AsymmetricKeyInformation keys) { + this.keys = keys; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("keys", keys) + .append("deletionAllowed", deletionAllowed) + .append("derived", derived) + .append("exportable", exportable) + .append("latestVersion", latestVersion) + .append("minDecryptionVersion", minDecryptionVersion) + .append("minEncryptionVersion", minEncryptionVersion) + .append("name", name) + .append("supportsDecryption", supportsDecryption) + .append("supportsDerivation", supportsDerivation) + .append("supportsEncryption", supportsEncryption) + .append("supportsSigning", supportsSigning) + .append("type", type) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultCreateKeyRequest.java b/src/main/java/com/nike/vault/client/model/VaultCreateKeyRequest.java new file mode 100644 index 0000000..96dc79c --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultCreateKeyRequest.java @@ -0,0 +1,91 @@ +package com.nike.vault.client.model; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultCreateKeyRequest { + + /** + * AES-256 wrapped with GCM using a 12-byte nonce size (symmetric, supports derivation) + */ + public static final String TYPE_AES256_GCM96 = "aes256-gcm96"; + /** + * ECDSA using the P-256 elliptic curve (asymmetric) + */ + public static final String TYPE_ACDSA_P256 = "ecdsa-p256"; + /** + * ED25519 (asymmetric, supports derivation) + */ + public static final String TYPE_ED25519 = "ed25519"; + /** + * RSA with bit size of 2048 (asymmetric) + */ + public static final String TYPE_RSA_2048 = "rsa-2048"; + /** + * RSA with bit size of 4096 (asymmetric) + */ + public static final String TYPE_RSA_4096 = "rsa-4096"; + + private boolean convergentEncryption; + private boolean derived; + private boolean exportable; + private String type; + + /** + * If enabled, the key will support convergent encryption, where the same plaintext creates the + * same ciphertext. This requires derived to be set to true. When enabled, each + * encryption(/decryption/rewrap/datakey) operation will derive a nonce value rather than randomly + * generate it. Note that while this is useful for particular situations, all nonce values used + * with a given context value must be unique or it will compromise the security of your key, and + * the key space for nonces is 96 bit -- not as large as the AES key itself. + * + * @return convergent flag + */ + public boolean isConvergentEncryption() { + return convergentEncryption; + } + + public void setConvergentEncryption(boolean convergentEncryption) { + this.convergentEncryption = convergentEncryption; + } + + /** + * Specifies if key derivation is to be used. If enabled, all encrypt/decrypt requests to this + * named key must provide a context which is used for key derivation. + * + * @return Derriviation flag + */ + public boolean isDerived() { + return derived; + } + + public void setDerived(boolean derived) { + this.derived = derived; + } + + public boolean isExportable() { + return exportable; + } + + public void setExportable(boolean exportable) { + this.exportable = exportable; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("convergentEncryption", convergentEncryption) + .append("derived", derived) + .append("exportable", exportable) + .append("type", type) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultDecryptDataRequest.java b/src/main/java/com/nike/vault/client/model/VaultDecryptDataRequest.java new file mode 100644 index 0000000..0bf1da5 --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultDecryptDataRequest.java @@ -0,0 +1,39 @@ +package com.nike.vault.client.model; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultDecryptDataRequest { + + private String ciphertext; + private String context; + + /** + * @return Ciphertext to decrypt. + */ + public String getCiphertext() { + return ciphertext; + } + + public void setCiphertext(String ciphertext) { + this.ciphertext = ciphertext; + } + + /** + * @return Base64 encoded context for key derivation. This is required if key derivation is enabled. + */ + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("ciphertext", ciphertext) + .append("context", context) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultDecryptDataResponse.java b/src/main/java/com/nike/vault/client/model/VaultDecryptDataResponse.java new file mode 100644 index 0000000..20a46fc --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultDecryptDataResponse.java @@ -0,0 +1,22 @@ +package com.nike.vault.client.model; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultDecryptDataResponse { + private String plaintext; + + public String getPlaintext() { + return plaintext; + } + + public void setPlaintext(String plaintext) { + this.plaintext = plaintext; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("plaintext", plaintext) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultEncryptDataRequest.java b/src/main/java/com/nike/vault/client/model/VaultEncryptDataRequest.java new file mode 100644 index 0000000..62813e6 --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultEncryptDataRequest.java @@ -0,0 +1,54 @@ +package com.nike.vault.client.model; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultEncryptDataRequest { + + private String plaintext; + private String context; + private int keyVersion; + + /** + * @return Base64 encoded plaintext to be encoded. + */ + public String getPlaintext() { + return plaintext; + } + + public void setPlaintext(String plaintext) { + this.plaintext = plaintext; + } + + /** + * @return Base64 encoded context for key derivation. This is required if key derivation is + * enabled. + */ + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + /** + * @return Version of the key to use for encryption. If not set, uses the latest version. + * Must be greater than or equal to the key's min_encryption_version, if set. + */ + public int getKeyVersion() { + return keyVersion; + } + + public void setKeyVersion(int keyVersion) { + this.keyVersion = keyVersion; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("plaintext", plaintext) + .append("context", context) + .append("keyVersion", keyVersion) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultEncryptDataResponse.java b/src/main/java/com/nike/vault/client/model/VaultEncryptDataResponse.java new file mode 100644 index 0000000..f4d7169 --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultEncryptDataResponse.java @@ -0,0 +1,22 @@ +package com.nike.vault.client.model; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultEncryptDataResponse { + private String ciphertext; + + public String getCiphertext() { + return ciphertext; + } + + public void setCiphertext(String ciphertext) { + this.ciphertext = ciphertext; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("ciphertext", ciphertext) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultKeyExportResponse.java b/src/main/java/com/nike/vault/client/model/VaultKeyExportResponse.java new file mode 100644 index 0000000..41b3c94 --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultKeyExportResponse.java @@ -0,0 +1,68 @@ +package com.nike.vault.client.model; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +/** + * Created by gutzeit on 28/12/2017. All rights reserved to LeapXpert. + */ +public class VaultKeyExportResponse { + public class KeyInformation { + + @SerializedName("1") + private String keyData; + + public String getKeyData() { + return keyData; + } + + public void setKeyData( + String keyData) { + this.keyData = keyData; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("keyData", keyData) + .toString(); + } + } + + private KeyInformation keys; + private String name; + private String type; + + public KeyInformation getKeys() { + return keys; + } + + public void setKeys(KeyInformation keys) { + this.keys = keys; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("keys", keys) + .append("name", name) + .append("type", type) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultKeyResponse.java b/src/main/java/com/nike/vault/client/model/VaultKeyResponse.java new file mode 100644 index 0000000..b59d317 --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultKeyResponse.java @@ -0,0 +1,132 @@ +package com.nike.vault.client.model; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultKeyResponse { + protected boolean deletionAllowed; + protected boolean derived; + protected boolean exportable; + protected int latestVersion; + protected int minDecryptionVersion; + protected int minEncryptionVersion; + protected String name; + protected boolean supportsDecryption; + protected boolean supportsDerivation; + protected boolean supportsEncryption; + protected boolean supportsSigning; + protected String type; + + public boolean isDeletionAllowed() { + return deletionAllowed; + } + + public void setDeletionAllowed(boolean deletionAllowed) { + this.deletionAllowed = deletionAllowed; + } + + public boolean isDerived() { + return derived; + } + + public void setDerived(boolean derived) { + this.derived = derived; + } + + public boolean isExportable() { + return exportable; + } + + public void setExportable(boolean exportable) { + this.exportable = exportable; + } + + public int getLatestVersion() { + return latestVersion; + } + + public void setLatestVersion(int latestVersion) { + this.latestVersion = latestVersion; + } + + public int getMinDecryptionVersion() { + return minDecryptionVersion; + } + + public void setMinDecryptionVersion(int minDecryptionVersion) { + this.minDecryptionVersion = minDecryptionVersion; + } + + public int getMinEncryptionVersion() { + return minEncryptionVersion; + } + + public void setMinEncryptionVersion(int minEncryptionVersion) { + this.minEncryptionVersion = minEncryptionVersion; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isSupportsDecryption() { + return supportsDecryption; + } + + public void setSupportsDecryption(boolean supportsDecryption) { + this.supportsDecryption = supportsDecryption; + } + + public boolean isSupportsDerivation() { + return supportsDerivation; + } + + public void setSupportsDerivation(boolean supportsDerivation) { + this.supportsDerivation = supportsDerivation; + } + + public boolean isSupportsEncryption() { + return supportsEncryption; + } + + public void setSupportsEncryption(boolean supportsEncryption) { + this.supportsEncryption = supportsEncryption; + } + + public boolean isSupportsSigning() { + return supportsSigning; + } + + public void setSupportsSigning(boolean supportsSigning) { + this.supportsSigning = supportsSigning; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("deletionAllowed", deletionAllowed) + .append("derived", derived) + .append("exportable", exportable) + .append("latestVersion", latestVersion) + .append("minDecryptionVersion", minDecryptionVersion) + .append("minEncryptionVersion", minEncryptionVersion) + .append("name", name) + .append("supportsDecryption", supportsDecryption) + .append("supportsDerivation", supportsDerivation) + .append("supportsEncryption", supportsEncryption) + .append("supportsSigning", supportsSigning) + .append("type", type) + .toString(); + } +} diff --git a/src/main/java/com/nike/vault/client/model/VaultSymmetricKeyResponse.java b/src/main/java/com/nike/vault/client/model/VaultSymmetricKeyResponse.java new file mode 100644 index 0000000..a152393 --- /dev/null +++ b/src/main/java/com/nike/vault/client/model/VaultSymmetricKeyResponse.java @@ -0,0 +1,60 @@ +package com.nike.vault.client.model; + +import com.google.gson.annotations.SerializedName; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class VaultSymmetricKeyResponse extends VaultKeyResponse { + + public class SymmetricKeyInformation { + + @SerializedName("1") + private long keyData; + + public long getKeyData() { + return keyData; + } + + public void setKeyData( + long keyData) { + this.keyData = keyData; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("keyData", keyData) + .toString(); + } + } + + private SymmetricKeyInformation keys; + + public SymmetricKeyInformation getKeys() { + return keys; + } + + public void setKeys(SymmetricKeyInformation keys) { + this.keys = keys; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("keys", keys) + .append("deletionAllowed", deletionAllowed) + .append("derived", derived) + .append("exportable", exportable) + .append("latestVersion", latestVersion) + .append("minDecryptionVersion", minDecryptionVersion) + .append("minEncryptionVersion", minEncryptionVersion) + .append("name", name) + .append("supportsDecryption", supportsDecryption) + .append("supportsDerivation", supportsDerivation) + .append("supportsEncryption", supportsEncryption) + .append("supportsSigning", supportsSigning) + .append("type", type) + .toString(); + } + + +} diff --git a/src/test/java/com/nike/vault/client/VaultCryptoClientTest.java b/src/test/java/com/nike/vault/client/VaultCryptoClientTest.java new file mode 100644 index 0000000..be962c2 --- /dev/null +++ b/src/test/java/com/nike/vault/client/VaultCryptoClientTest.java @@ -0,0 +1,195 @@ +package com.nike.vault.client; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.nike.vault.client.auth.VaultCredentials; +import com.nike.vault.client.auth.VaultCredentialsProvider; +import com.nike.vault.client.http.HttpStatus; +import com.nike.vault.client.model.VaultAsymmetricKeyResponse; +import com.nike.vault.client.model.VaultCreateKeyRequest; +import com.nike.vault.client.model.VaultDecryptDataRequest; +import com.nike.vault.client.model.VaultDecryptDataResponse; +import com.nike.vault.client.model.VaultEncryptDataRequest; +import com.nike.vault.client.model.VaultEncryptDataResponse; +import com.nike.vault.client.model.VaultKeyExportResponse; +import com.nike.vault.client.model.VaultKeyResponse; +import com.nike.vault.client.model.VaultSymmetricKeyResponse; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class VaultCryptoClientTest { + + private VaultCryptoClient vaultClient; + + private MockWebServer mockWebServer; + + @Before + public void setup() throws IOException { + mockWebServer = new MockWebServer(); + mockWebServer.start(); + final String vaultUrl = "http://localhost:" + mockWebServer.getPort(); + final VaultCredentialsProvider vaultCredentialsProvider = mock(VaultCredentialsProvider.class); + vaultClient = VaultClientFactory.getCryptoClient(new StaticVaultUrlResolver(vaultUrl), + vaultCredentialsProvider); + + when(vaultCredentialsProvider.getCredentials()).thenReturn(new TestVaultCredentials()); + } + + @After + public void teardown() throws IOException { + mockWebServer.shutdown(); + } + + @Test + public void export_symmetric_returns_ok_if_created() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.OK); + response.setBody(getResponseJson("symmetric_export")); + mockWebServer.enqueue(response); + + VaultKeyExportResponse actualResponse = + vaultClient.exportKey("test-key", VaultCryptoClient.KEY_TYPE_ENCRYPTION_KEY); + + assertThat(actualResponse).isNotNull(); + assertThat(actualResponse.getKeys().getKeyData()).isNotEmpty(); + } + + @Test + public void export_asymmetric_returns_ok_if_created() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.OK); + response.setBody(getResponseJson("asymmetric_export")); + mockWebServer.enqueue(response); + + VaultKeyExportResponse actualResponse = + vaultClient.exportKey("test-key", VaultCryptoClient.KEY_TYPE_ENCRYPTION_KEY); + + assertThat(actualResponse).isNotNull(); + assertThat(actualResponse.getKeys().getKeyData()).isNotEmpty(); + } + + @Test + public void create_key_returns_204_when_successful() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.NO_CONTENT); + mockWebServer.enqueue(response); + + final VaultCreateKeyRequest createKeyRequest = new VaultCreateKeyRequest(); + createKeyRequest.setConvergentEncryption(true); + createKeyRequest.setDerived(true); + createKeyRequest.setExportable(true); + createKeyRequest.setType(VaultCreateKeyRequest.TYPE_ACDSA_P256); + + vaultClient.createKey("test-key", createKeyRequest); + + // Silence is success! + } + + @Test + public void query_key_information_symmetric_returns_ok_if_created() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.OK); + response.setBody(getResponseJson("symmetric_key")); + mockWebServer.enqueue(response); + + VaultKeyResponse actualResponse = vaultClient.getKeyInfo("test-key"); + + assertThat(actualResponse).isNotNull(); + assertThat(actualResponse instanceof VaultSymmetricKeyResponse); + VaultSymmetricKeyResponse symmetricResponse = (VaultSymmetricKeyResponse) actualResponse; + assertThat(symmetricResponse.getKeys().getKeyData()).isEqualTo(1511100122l); + assertThat(symmetricResponse.getMinDecryptionVersion() == 1).isTrue(); + assertThat(symmetricResponse.isSupportsSigning()).isFalse(); + } + + @Test + public void query_key_information_asymmetric_returns_ok_if_created() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.OK); + response.setBody(getResponseJson("asymmetric_key")); + mockWebServer.enqueue(response); + + VaultKeyResponse actualResponse = vaultClient.getKeyInfo("test-key"); + + assertThat(actualResponse).isNotNull(); + assertThat(actualResponse instanceof VaultAsymmetricKeyResponse); + VaultAsymmetricKeyResponse asymmetricResponse = (VaultAsymmetricKeyResponse) actualResponse; + assertThat(asymmetricResponse.getKeys().getKeyData().getName().equalsIgnoreCase("rsa-4096")); + assertThat(asymmetricResponse.getKeys().getKeyData().getCreationTime() + .equalsIgnoreCase("2017-11-19T20:20:15.854167+08:00")); + assertThat(asymmetricResponse.getMinDecryptionVersion() == 1).isTrue(); + assertThat(asymmetricResponse.isSupportsSigning()).isTrue(); + } + + @Test + public void encrypt_returns_ok_if_created() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.OK); + response.setBody(getResponseJson("encrypt")); + mockWebServer.enqueue(response); + + VaultEncryptDataRequest request = new VaultEncryptDataRequest(); + request.setPlaintext("QWxsIHdlIGFyZSBidXQgc3BlY2tzIGluIGEgaHVnZSB1bml2ZXJzZSAuLi4="); + + VaultEncryptDataResponse actualResponse = vaultClient.encrypt("test-key", request); + + assertThat(actualResponse).isNotNull(); + assertThat(actualResponse.getCiphertext()).isEqualTo("vault:v1:ZqkozlOaopGR4jf+/OhVS1iHyczM19Ax6ku1VJq4il7X7Wrqqc21dqKN8MTiOV4Iku8784X12NvOIfQcSvIWV2mW44q0HMlG"); + } + + @Test + public void decrypt_returns_ok_if_created() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.OK); + response.setBody(getResponseJson("decrypt")); + mockWebServer.enqueue(response); + + VaultDecryptDataRequest request = new VaultDecryptDataRequest(); + request.setCiphertext("vault:v1:ZqkozlOaopGR4jf+/OhVS1iHyczM19Ax6ku1VJq4il7X7Wrqqc21dqKN8MTiOV4Iku8784X12NvOIfQcSvIWV2mW44q0HMlG"); + + VaultDecryptDataResponse actualResponse = vaultClient.decrypt("test-key", request); + + assertThat(actualResponse).isNotNull(); + assertThat(actualResponse.getPlaintext()).isEqualTo("QWxsIHdlIGFyZSBidXQgc3BlY2tzIGluIGEgaHVnZSB1bml2ZXJzZSAuLi4="); + } + + @Test + public void delete_key_returns_204_when_successful() { + final MockResponse response = new MockResponse(); + response.setResponseCode(HttpStatus.NO_CONTENT); + mockWebServer.enqueue(response); + + vaultClient.deleteKey("test-key"); + // Silence is success! + } + + private String getResponseJson(final String title) { + InputStream inputStream = getClass().getResourceAsStream( + String.format("/com/nike/vault/client/%s.json", title)); + try { + return IOUtils.toString(inputStream, Charset.forName("UTF-8")); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + private static class TestVaultCredentials implements VaultCredentials { + + @Override + public String getToken() { + return "TOKEN"; + } + } + +} diff --git a/src/test/resources/com/nike/vault/client/asymmetric_export.json b/src/test/resources/com/nike/vault/client/asymmetric_export.json new file mode 100644 index 0000000..d849d81 --- /dev/null +++ b/src/test/resources/com/nike/vault/client/asymmetric_export.json @@ -0,0 +1 @@ +{"request_id":"e8cae20b-dd66-7530-7ac8-3b616bfa3f08","lease_id":"","renewable":false,"lease_duration":0,"data":{"keys":{"1":"-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA4PsBLbgfIIXfYAV0brI/f4/zlciwJv2hgv9aQf63dS14erVa\nuP5NwqEJtEvPvUIk1RQQyKyiST14IQDLDg8pFiAsbTshda1e/pjIjtsAEnDUFW1V\nRRnqc4Ex8vRaT6stbZLGCQlWUnBO5zRCoRZZk/XnPXOIyg8qnvSnDUkqptL50IiK\nNXXNHN6jEicLKIKQ4JaSy2zZuDKM+wDpbrjMLeL5nvVS2E4mV8ilmA+5LRudoomk\n7fBie3iD92z/EHeXyczQ1y0kZsOBb7bWKI3YDyr/ol1bTPp0mcE1IxsL8n1mhzPm\njDW0NHlUtIhpCFq86zm/a0lGBf0dNxliHnB+5QIDAQABAoIBAFFRaHOmAVo4CS0j\ny5nXQ6xP07Nn/oOZWS+ILI9+CcGI1etEca47/M9EdcV9QXEe30FFJ2vhOidO2ITV\nTI+gWzFsH6K7pLRsHdHYV2WLMtN3hLDZ++AmJd/p6qvuNlZlgN4CFyJdBZ52iY54\nDT08XtRkJVjI0cB3Cui5dUgQEiKJoZvChkVo/xTbEGyQcIYvbC01iMj+iM7TrF4k\nlxIlfJnFCzOSV34OfkyrN2WJxQ7STkOIRctAfscwfh9toUuhOR9pYceC1LvLU9hv\nOq+5LWKk5DmzSTUvDapHWcWNIJ0pkK4E4gEnw/Jxxwiw21EKmLfJa2/RU++rfSaH\nz8VuQ2ECgYEA5B2aePUe2AXI/XaGqnYi91vj58gkhbiOOI2fZU6WGiGz5KYYV38H\n0YZ4Gj5zRS99t0VKzhDSwLv/BeiyxUryE257lcdjsK7A6xHThB2u51GA2rtrDAdJ\n9f93CWqnlzOYETE/vmuOuRWqL5jofWCT2ob3N72zFeJJp/kJtPWL62cCgYEA/HtL\nEs7qZ6tV6f/oV3XwvGWkKjUW2uaFV/PPx31Jh0Jf9ohSh9ZmckPwys0xIa1OC3pP\nmQI5cvpcIECSntOWPQXIs6jwfB64f1QVxx3vqGvZ950eBo57z69q5PILHzb2IR67\n33AsgJYlEUhTBEZy1C/X+tNnSaIxuE+7OdsEH9MCgYB5IsdPCEvyx0+uWWy1xLpY\nxPFHul66rADKQ1qrv4myIseW2iT/AbQzLcdFmHg5+zg1RJSuzPw94RdSGfolDuFy\nNC9ooFNuFb7YlcTO2bxxljRo8zGrV4uNGLYrx/lrL7jHGHITOqa13q1bTUXYnpql\nZxqM/S3Gpz4Z9wOSlvpQbQKBgD2pxw4S5wDmwZbi31XtAhyhHlUInkpcHpj6fPaV\nzM1yEondhXqTjHW/ziFZt/QnpXX8K1CNUIaaSsG9w5Fyz7Cbpwbp7cICpsDCQodc\n8llJ7fQhtWGYjviMOSktTDYVcEtqfCv384Z8JRVxeoUCx6y2+qLR2toK+OWw42Mf\n8IPtAoGAZU9buC06zJLIQGZCXZMOAln7mdqxc9M6E1rTE11bDIKJJN4kvzyO3h1W\nG451nG+xnC96oIOrhmOU80s2J956qfhJRiwWVDxJjslEkH+cLjhMBId36inepL1P\nKPGeRlK5QW7MOhhQuOnMJiRL1OrOKLCRvMKQWmoNuiktKA0A4EA=\n-----END RSA PRIVATE KEY-----\n"},"name":"my-key-asymmetric","type":"rsa-2048"},"wrap_info":null,"warnings":null,"auth":null} \ No newline at end of file diff --git a/src/test/resources/com/nike/vault/client/asymmetric_key.json b/src/test/resources/com/nike/vault/client/asymmetric_key.json new file mode 100644 index 0000000..ee3eba9 --- /dev/null +++ b/src/test/resources/com/nike/vault/client/asymmetric_key.json @@ -0,0 +1 @@ +{"request_id":"d607de77-58f3-9874-656d-46c620568d73","lease_id":"","renewable":false,"lease_duration":0,"data":{"deletion_allowed":false,"derived":false,"exportable":true,"keys":{"1":{"creation_time":"2017-11-19T20:20:15.854167+08:00","name":"rsa-4096","public_key":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvFIMubJbyTOb227rO+l7\nmNDLLJalrjq6X2DKGsa0l4wq4k7ONyvbNogTSPDobmh2unJHuB1uvJR8BslwsR2X\nTpGYUdNcA/9ZY+racwtJy2pQEfev+fBjv8HpZiw22COL/SHZ/vh7UWoGYvMeQJ7o\nUOiZvAsNfBu2vu6HCjnoNXLuva7qHTGEPm+TOWFtHbLq8kt+R7y+Feh6d0PhdBdj\neMzJadjX6rPkPC1fyWNPhR9KIEW/uX8V141BN+Vc4WC5SHMKN/Ef84YC2UzE8Bsc\nG8mvCLAX7c4lRJUFlaeyPo/LAVW51S/hLb84WrhDPemrpk4mQY2VnRmcLyg3VK62\nD17tLlhZxnnEligHZHj6QBZtIXZASeMSaXCpBWwN96glwGp9AFFRRzXfzqLOfoCO\nqaoyPoLZD0t6sSyCQCO+619xFi7yoVbvHdw92N133Zbc6eRfwPs4SeQd995ytJBo\nb3+W7zhXRzzrUpo+wUxBxcz249Fb054JA0b3dk5k4IDPYLTdwg2GF/n0+9cj74+a\n+HQe7JQIFSrx8oEw4+X7l0ZOIm9p21WVkjcJMlkX5Ba/8cFpF2TXsQS4bSvDrq/a\nBkdlN5oaA+JqH4TCq0bjdg3M2dZ/Qv0MtLrxXdJbPixFRSa/9/O7W5+fURghd/Rs\nMYsOFFthKdg06JZLm8Nb/VMCAwEAAQ==\n-----END PUBLIC KEY-----\n"}},"latest_version":1,"min_decryption_version":1,"min_encryption_version":0,"name":"test-key-2","supports_decryption":true,"supports_derivation":false,"supports_encryption":true,"supports_signing":true,"type":"rsa-4096"},"wrap_info":null,"warnings":null,"auth":null} \ No newline at end of file diff --git a/src/test/resources/com/nike/vault/client/decrypt.json b/src/test/resources/com/nike/vault/client/decrypt.json new file mode 100644 index 0000000..0f59e81 --- /dev/null +++ b/src/test/resources/com/nike/vault/client/decrypt.json @@ -0,0 +1 @@ +{"request_id":"2e0ebbd5-cdb5-3e65-6e7e-e1e55b3e9345","lease_id":"","renewable":false,"lease_duration":0,"data":{"plaintext":"QWxsIHdlIGFyZSBidXQgc3BlY2tzIGluIGEgaHVnZSB1bml2ZXJzZSAuLi4="},"wrap_info":null,"warnings":null,"auth":null} \ No newline at end of file diff --git a/src/test/resources/com/nike/vault/client/encrypt.json b/src/test/resources/com/nike/vault/client/encrypt.json new file mode 100644 index 0000000..de36cf2 --- /dev/null +++ b/src/test/resources/com/nike/vault/client/encrypt.json @@ -0,0 +1 @@ +{"request_id":"5ccc2bde-882a-583f-56ce-dd6d7afbd314","lease_id":"","renewable":false,"lease_duration":0,"data":{"ciphertext":"vault:v1:ZqkozlOaopGR4jf+/OhVS1iHyczM19Ax6ku1VJq4il7X7Wrqqc21dqKN8MTiOV4Iku8784X12NvOIfQcSvIWV2mW44q0HMlG"},"wrap_info":null,"warnings":null,"auth":null} \ No newline at end of file diff --git a/src/test/resources/com/nike/vault/client/symmetric_export.json b/src/test/resources/com/nike/vault/client/symmetric_export.json new file mode 100644 index 0000000..2aaca63 --- /dev/null +++ b/src/test/resources/com/nike/vault/client/symmetric_export.json @@ -0,0 +1 @@ +{"request_id":"4ce7d18e-deef-a633-eac2-766094a6558d","lease_id":"","renewable":false,"lease_duration":0,"data":{"keys":{"1":"1WTeJCE75wZs/hwrywrVrEUbB4UJXlsappeJHcPXNDY="},"name":"my-key-symmetric","type":"aes256-gcm96"},"wrap_info":null,"warnings":null,"auth":null} \ No newline at end of file diff --git a/src/test/resources/com/nike/vault/client/symmetric_key.json b/src/test/resources/com/nike/vault/client/symmetric_key.json new file mode 100644 index 0000000..468ef1b --- /dev/null +++ b/src/test/resources/com/nike/vault/client/symmetric_key.json @@ -0,0 +1 @@ +{"request_id":"edbef751-178a-6393-1a6d-ece5d06f7d59","lease_id":"","renewable":false,"lease_duration":0,"data":{"deletion_allowed":false,"derived":false,"exportable":true,"keys":{"1":1511100122},"latest_version":1,"min_decryption_version":1,"min_encryption_version":0,"name":"test-key-3","supports_decryption":true,"supports_derivation":true,"supports_encryption":true,"supports_signing":false,"type":"aes256-gcm96"},"wrap_info":null,"warnings":null,"auth":null} \ No newline at end of file