OCI Vault

The Helidon SE OCI Vault integration provides a reactive API for the Oracle Cloud Vault service.

Deprecated

The custom Helidon SE OCI clients documented here are deprecated. It is recommended that you use the OCI Java SDK directly, in particular the Async clients. For more information see:

Experimental

Helidon integration with Oracle Cloud Infrastructure is still experimental and not intended for production use. APIs and features have not yet been fully tested and are subject to change.

Maven Coordinates

To enable OCI Vault add the following dependency to your project’s pom.xml (see Managing Dependencies).

<dependency>
    <groupId>io.helidon.integrations.oci</groupId>
    <artifactId>helidon-integrations-oci-vault</artifactId>
</dependency>
Copied

Setting up the OCI Vault

In order to use the OCI Vault integration, the following setup should be made.

  • The configuration required for Vault integration includes:

  • Vault OCID - to use the correct Vault, as more than one can be configured

  • Compartment OCID - OCI-specific compartment

  • Encryption Key OCID - required when doing encryption/decryption

  • Signature Key OCID - required when doing signatures/verification

  • Cryptographic endpoint - required for all except secrets

First specify OCIDs and URLs of Vault items in application.yaml:

oci:
  vault:
    vault-ocid: "<...>"
    compartment-ocid: "<...>"
    encryption-key-ocid: "<...>"
    signature-key-ocid: "<...>"
    cryptographic-endpoint: "<...>"
Copied

Current configuration requires ~/.oci/config to be available in the home folder. This configuration file can be downloaded from OCI.

The OCIDs can be set up and found in OCI under Security tab.

OCI Vault

Next, these values should be read and provided to VaultService:

Config vaultConfig = config.get("oci.vault");
// the following three parameters are required
String vaultOcid = vaultConfig.get("vault-ocid").asString().get();
String compartmentOcid = vaultConfig.get("compartment-ocid").asString().get();
String encryptionKey = vaultConfig.get("encryption-key-ocid").asString().get();
String signatureKey = vaultConfig.get("signature-key-ocid").asString().get();

// this requires OCI configuration in the usual place
// ~/.oci/config
OciVaultRx ociVault = OciVaultRx.create(config.get("oci"));

WebServer.builder()
        .config(config.get("server"))
        .routing(Routing.builder()
                         .register("/vault", new VaultService(ociVault,
                                                              vaultOcid,
                                                              compartmentOcid,
                                                              encryptionKey,
                                                              signatureKey)))
        .build()
        .start()
        .await();
Copied

The VaultService should define an update method to map paths to handler methods:

@Override
public void update(Routing.Rules rules) {
    rules.get("/encrypt/{text:.*}", this::encrypt)
            .get("/decrypt/{text:.*}", this::decrypt)
            .get("/sign/{text}", this::sign)
            .get("/verify/{text}/{signature:.*}", this::verify)
            .get("/secret/{id}", this::getSecret)
            .post("/secret/{name}", Handler.create(String.class, this::createSecret))
            .delete("/secret/{id}", this::deleteSecret);
}
Copied

OCI Vault usage

Encryption

To encrypt a text, submit a GET request to the /encrypt endpoint:

private void encrypt(ServerRequest req, ServerResponse res) {
    vault.encrypt(Encrypt.Request.builder()
                          .keyId(encryptionKeyOcid)
                          .data(Base64Value.create(req.path().param("text"))))
            .map(Encrypt.Response::cipherText)
            .forSingle(res::send)
            .exceptionally(res::send);
}
Copied

Decryption

To decrypt a text, submit a GET request to /decrypt endpoint:

private void decrypt(ServerRequest req, ServerResponse res) {
    vault.decrypt(Decrypt.Request.builder()
                          .keyId(encryptionKeyOcid)
                          .cipherText(req.path().param("text")))
            .map(Decrypt.Response::decrypted)
            .map(Base64Value::toDecodedString)
            .forSingle(res::send)
            .exceptionally(res::send);
}
Copied

Signature

To retrieve a signature, submit a GET request to /sign endpoint:

private void sign(ServerRequest req, ServerResponse res) {
    vault.sign(Sign.Request.builder()
                       .keyId(signatureKeyOcid)
                       .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS)
                       .message(Base64Value.create(req.path().param("text"))))
            .map(Sign.Response::signature)
            .map(Base64Value::toBase64)
            .forSingle(res::send)
            .exceptionally(res::send);
}
Copied

Verification of a Signature

To verify the correctness of the signature, submit a GET request to /verify endpoint:

private void verify(ServerRequest req, ServerResponse res) {
    String text = req.path().param("text");
    String signature = req.path().param("signature");

    vault.verify(Verify.Request.builder()
                         .keyId(signatureKeyOcid)
                         .algorithm(Sign.Request.ALGORITHM_SHA_224_RSA_PKCS_PSS)
                         .message(Base64Value.create(text))
                         .signature(Base64Value.createFromEncoded(signature)))
            .map(Verify.Response::isValid)
            .map(it -> it ? "Signature Valid" : "Signature Invalid")
            .forSingle(res::send)
            .exceptionally(res::send);
}
Copied

Creating a Signature

To create a secret with a provided name, submit a GET request to /secret:

private void createSecret(ServerRequest req, ServerResponse res, String secretText) {
    vault.createSecret(CreateSecret.Request.builder()
                               .secretContent(CreateSecret.SecretContent.create(secretText))
                               .vaultId(vaultOcid)
                               .compartmentId(compartmentOcid)
                               .encryptionKeyId(encryptionKeyOcid)
                               .secretName(req.path().param("name")))
            .map(CreateSecret.Response::secret)
            .map(Secret::id)
            .forSingle(res::send)
            .exceptionally(res::send);
}
Copied

Getting a Signature

To get a secret by its OCID, submit a GET request to /secret:

private void getSecret(ServerRequest req, ServerResponse res) {
    vault.getSecretBundle(GetSecretBundle.Request.create(req.path().param("id")))
            .forSingle(apiResponse -> {
                Optional<GetSecretBundle.Response> entity = apiResponse.entity();
                if (entity.isEmpty()) {
                    res.status(Http.Status.NOT_FOUND_404).send();
                } else {
                    GetSecretBundle.Response response = entity.get();
                    res.send(response.secretString().orElse(""));
                }
            })
            .exceptionally(res::send);
}
Copied

Deleting a Signature

To delete a secret, a DELETE request to /secret should be used:

private void deleteSecret(ServerRequest req, ServerResponse res) {
    Instant deleteTime = Instant.now().plus(30, ChronoUnit.DAYS);

    vault.deleteSecret(DeleteSecret.Request.builder()
                               .secretId(req.path().param("id"))
                               .timeOfDeletion(deleteTime))
            .forSingle(it -> res.status(it.status()).send())
            .exceptionally(res::send);

}
Copied