Skip to main content

Java SDK Quickstart

Back to SDK Quickstart

This guide covers the Java SDK implementation. For other languages or general information, see the SDK Quickstart page.

Prerequisites

  • Java 17 or later
  • Maven or Gradle
  • Your OpenTDF platform running locally (from Getting Started guide)
Platform Must Be Running

Before you begin, make sure your OpenTDF platform is running!

Verify it's running:

curl -k https://platform.opentdf.local:8443/healthz

Should return: {"status":"SERVING"}

If not running, start it:

cd ~/.opentdf/platform && docker compose up -d

See the Managing the Platform guide for details.

Step 1: Create a New Maven Project

Create a new directory with a pom.xml file:

mkdir opentdf-quickstart
cd opentdf-quickstart

Create pom.xml:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.opentdf</groupId>
<artifactId>opentdf-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.68.0</grpc.version>
<protobuf.version>4.29.2</protobuf.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-bom</artifactId>
<version>${grpc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.opentdf.platform</groupId>
<artifactId>sdk</artifactId>
<version>0.12.0</version>
</dependency>
<!-- Simple logging implementation to avoid SLF4J warnings -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
<!-- SSL utilities for handling self-signed certificates (DEV ONLY) -->
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>9.2.1</version>
</dependency>
</dependencies>
</project>

Step 2: Install Dependencies

mvn clean install

This will download the OpenTDF Java SDK and its dependencies (including Bouncy Castle for cryptography).

Step 3: Create Your Application

Create the directory structure:

mkdir -p src/main/java/io/opentdf/quickstart

Java Implementation Code

Create src/main/java/io/opentdf/quickstart/QuickstartApp.java:

src/main/java/io/opentdf/quickstart/QuickstartApp.java
package io.opentdf.quickstart;

import io.opentdf.platform.sdk.*;
import nl.altindag.ssl.SSLFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.channels.FileChannel;

public class QuickstartApp {
public static void main(String[] args) {
try {
System.out.println("🚀 Starting OpenTDF SDK Quickstart...");

String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "https://platform.opentdf.local:8443";

System.out.println("📡 Connecting to platform: " + platformEndpoint);

// Create SSL factory that trusts all certificates
// withTrustingAllCertificatesWithoutValidation() disables certificate verification
// This allows connections to the platform's self-signed certificate
// Only use this for local development - never in production!
System.out.println("🔓 Creating SSL factory for self-signed certificates...");
SSLFactory sslFactory = SSLFactory.builder()
.withTrustingAllCertificatesWithoutValidation()
.build();

// Create SDK client with client credentials (try-with-resources ensures proper cleanup)
System.out.println("🔐 Initializing SDK client with client credentials...");
SDKBuilder builder = SDKBuilder.newBuilder();
try (SDK sdk = builder.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.sslFactory(sslFactory)
.build()) {

System.out.println("✅ SDK client initialized successfully");

// Encrypt data
System.out.println("\n📝 Encrypting sensitive data...");
String sensitiveData = "Hello from the OpenTDF Java SDK! This data is encrypted.";

// KASInfo specifies the Key Access Service (KAS) endpoint
// KAS manages encryption keys and enforces access policies
var kasInfo = new Config.KASInfo();
kasInfo.URL = platformEndpoint + "/kas";

var tdfConfig = Config.newTDFConfig(Config.withKasInformation(kasInfo));

var inputStream = new ByteArrayInputStream(sensitiveData.getBytes(StandardCharsets.UTF_8));
var outputStream = new ByteArrayOutputStream();

System.out.println("🔒 Creating TDF...");
sdk.createTDF(inputStream, outputStream, tdfConfig);

System.out.println("✅ Data successfully encrypted");
System.out.println("📊 Encrypted TDF size: " + outputStream.size() + " bytes");

// Decrypt data
System.out.println("\n🔓 Decrypting TDF...");
byte[] encryptedData = outputStream.toByteArray();

// Save to temporary file for decryption
Path tempFile = Files.createTempFile("opentdf-quickstart", ".tdf");
try {
Files.write(tempFile, encryptedData);

try (FileChannel fileChannel = FileChannel.open(tempFile, StandardOpenOption.READ)) {
var readerConfig = Config.newTDFReaderConfig();
var reader = sdk.loadTDF(fileChannel, readerConfig);
var decryptedOutput = new ByteArrayOutputStream();
reader.readPayload(decryptedOutput);

String decryptedContent = new String(decryptedOutput.toByteArray(), StandardCharsets.UTF_8);
System.out.println("✅ Data successfully decrypted");
System.out.println("📤 Decrypted content:\n\n" + decryptedContent + "\n");
}
} finally {
// Clean up temporary file
Files.deleteIfExists(tempFile);
}

System.out.println("🎉 Quickstart complete!");
} // SDK auto-closes here, shutting down gRPC connections
} catch (Exception e) {
System.err.println("❌ Error occurred: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}

// Force JVM exit to terminate SDK background threads immediately.
// Note: In production applications, you should configure daemon threads properly
// or implement explicit shutdown hooks instead of using System.exit(0).
// This approach is acceptable for quickstart/demo purposes only.
System.exit(0);
}
}

Step 4: Run Your Application

mvn compile exec:java -Dexec.mainClass="io.opentdf.quickstart.QuickstartApp"
Expected output
🚀 Starting OpenTDF SDK Quickstart...
📡 Connecting to platform: https://platform.opentdf.local:8443
🔐 Initializing SDK client with client credentials...
✅ SDK client initialized successfully

📝 Encrypting sensitive data...
🔒 Creating TDF...
✅ Data successfully encrypted
📊 Encrypted TDF size: 1234 bytes

🔓 Decrypting TDF...
✅ Data successfully decrypted
📤 Decrypted content:

Hello from the OpenTDF Java SDK! This data is encrypted.

🎉 Quickstart complete!

Step 5: Add ABAC Features

Java-Specific Examples

The following examples are specific to the Java SDK.

Now that you have basic encryption working, you can add attribute-based access control to your application.

If you want to follow along, you can add the code to your quickstart file as we go. Otherwise, a complete version is available below.

More ABAC Examples

For additional policy management examples including managing attributes, namespaces, subject mappings, and more, see the Policy SDK Guide.

Create a New Attribute Value

First, let's create a new attribute value that we can use for access control. If you followed the Quickstart setup guide, you'll already have an attribute "department" with two values: finance and engineering. Let's add "marketing" to those values:

import io.opentdf.platform.policy.attributes.GetAttributeRequest;
import io.opentdf.platform.policy.attributes.GetAttributeResponse;
import io.opentdf.platform.policy.attributes.CreateAttributeValueRequest;
import com.connectrpc.ResponseMessageKt;
import java.util.Collections;

// Get the existing department attribute
String attrFqn = "https://opentdf.io/attr/department";
GetAttributeResponse getAttrResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.getAttributeBlocking(
GetAttributeRequest.newBuilder()
.setFqn(attrFqn)
.build(),
Collections.emptyMap())
.execute());

System.out.println("✅ Found existing attribute: " + getAttrResponse.getAttribute().getName());

// Check if "marketing" value already exists
String targetValue = "marketing";
boolean valueExists = false;
for (var value : getAttrResponse.getAttribute().getValuesList()) {
if (targetValue.equals(value.getValue())) {
valueExists = true;
break;
}
}

if (!valueExists) {
// Add the "marketing" value to the existing attribute
var createValueRequest = CreateAttributeValueRequest.newBuilder()
.setAttributeId(getAttrResponse.getAttribute().getId())
.setValue(targetValue)
.build();

ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.createAttributeValueBlocking(createValueRequest, Collections.emptyMap())
.execute());

System.out.println("✅ Added '" + targetValue + "' value to department attribute");
} else {
System.out.println("✅ Attribute 'department' already has '" + targetValue + "' value");
}

System.out.println("Full attribute FQN: https://opentdf.io/attr/department/value/" + targetValue);
warning

If you get a resource not found error, you may need to create the "department" attribute, along with the namespace:

Show full code for creating namespace and attribute
import io.opentdf.platform.policy.AttributeRuleTypeEnum;
import io.opentdf.platform.policy.namespaces.CreateNamespaceRequest;
import io.opentdf.platform.policy.namespaces.CreateNamespaceResponse;
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
import io.opentdf.platform.policy.namespaces.GetNamespaceResponse;
import io.opentdf.platform.policy.attributes.CreateAttributeRequest;
import io.opentdf.platform.policy.attributes.CreateAttributeResponse;
import io.opentdf.platform.policy.attributes.GetAttributeRequest;
import io.opentdf.platform.policy.attributes.GetAttributeResponse;
import io.opentdf.platform.policy.attributes.CreateAttributeValueRequest;
import com.connectrpc.ResponseMessageKt;
import java.util.Arrays;
import java.util.Collections;

// Get or create the namespace
CreateNamespaceResponse nsResponse;
try {
// Try to get existing namespace
GetNamespaceResponse getNamespaceResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.getNamespaceBlocking(
GetNamespaceRequest.newBuilder()
.setFqn("https://opentdf.io")
.build(),
Collections.emptyMap())
.execute());

nsResponse = CreateNamespaceResponse.newBuilder()
.setNamespace(getNamespaceResponse.getNamespace())
.build();
System.out.println("✅ Using existing namespace: " + getNamespaceResponse.getNamespace().getId());

} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("not_found")) {
// Namespace doesn't exist, create it
nsResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.createNamespaceBlocking(
CreateNamespaceRequest.newBuilder()
.setName("opentdf.io")
.build(),
Collections.emptyMap())
.execute());
System.out.println("✅ Created namespace: " + nsResponse.getNamespace().getId());
} else {
throw e;
}
}

// Get or create the department attribute, and ensure it has the "marketing" value
String attrFqn = "https://opentdf.io/attr/department";
try {
// Try to get the existing attribute
GetAttributeResponse getAttrResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.getAttributeBlocking(
GetAttributeRequest.newBuilder()
.setFqn(attrFqn)
.build(),
Collections.emptyMap())
.execute());

System.out.println("✅ Found existing attribute: " + getAttrResponse.getAttribute().getName());

// Check if "marketing" value already exists
String targetValue = "marketing";
boolean valueExists = false;
for (var value : getAttrResponse.getAttribute().getValuesList()) {
if (targetValue.equals(value.getValue())) {
valueExists = true;
break;
}
}

if (!valueExists) {
// Add the value to the existing attribute
var createValueRequest = CreateAttributeValueRequest.newBuilder()
.setAttributeId(getAttrResponse.getAttribute().getId())
.setValue(targetValue)
.build();

ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.createAttributeValueBlocking(createValueRequest, Collections.emptyMap())
.execute());

System.out.println("✅ Added '" + targetValue + "' value to department attribute");
} else {
System.out.println("✅ Attribute 'department' already has '" + targetValue + "' value");
}

} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("not_found")) {
// Attribute doesn't exist, create it with the value
String targetValue = "marketing";
var attrRequest = CreateAttributeRequest.newBuilder()
.setNamespaceId(nsResponse.getNamespace().getId())
.setName("department")
.setRule(AttributeRuleTypeEnum.ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF)
.addAllValues(Arrays.asList(targetValue))
.build();

CreateAttributeResponse createAttrResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.createAttributeBlocking(attrRequest, Collections.emptyMap())
.execute());
System.out.println("✅ Created attribute: " + createAttrResponse.getAttribute().getName());
} else {
throw e;
}
}

String targetValue = "marketing";
System.out.println("Full attribute FQN: https://opentdf.io/attr/department/value/" + targetValue);

Add Attributes for Access Control

Now that you've created the attribute, update your TDF config to include the attribute for access control:

var tdfConfig = Config.newTDFConfig(
Config.withKasInformation(kasInfo),
Config.withDataAttributes("https://opentdf.io/attr/department/value/marketing")
);
tip

Only users with the department/marketing entitlement will be able to decrypt this TDF. If you try to decrypt before granting access to the attribute, you will see a permission denied error. Try it now!

Grant Yourself Access to the Attribute

To decrypt the TDF you just created, you need to grant yourself the marketing entitlement by creating a subject mapping. This connects your identity to the attribute value, giving you permission to access data encrypted with it.

About the read Action

The subject mapping below uses the read action, which specifies what you can do with this attribute value. The read action is one of the standard actions used for TDF decryption. Other standard actions include create, update, and delete, and you can define custom actions for your specific use cases.

Learn more about actions in the Actions documentation.

import io.opentdf.platform.policy.Action;
import io.opentdf.platform.policy.Condition;
import io.opentdf.platform.policy.ConditionBooleanTypeEnum;
import io.opentdf.platform.policy.ConditionGroup;
import io.opentdf.platform.policy.SubjectMappingOperatorEnum;
import io.opentdf.platform.policy.SubjectSet;
import io.opentdf.platform.policy.subjectmapping.*;

// Get the attribute value ID from the attribute you fetched
String attributeValueId = getAttrResponse.getAttribute().getValues(0).getId();

// Create subject condition set that matches your identity
var condition = Condition.newBuilder()
.setSubjectExternalSelectorValue(".clientId")
.setOperator(SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("opentdf")
.build();

var conditionGroup = ConditionGroup.newBuilder()
.setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(condition)
.build();

var subjectSet = SubjectSet.newBuilder()
.addConditionGroups(conditionGroup)
.build();

var scsRequest = CreateSubjectConditionSetRequest.newBuilder()
.setSubjectConditionSet(
SubjectConditionSetCreate.newBuilder()
.addSubjectSets(subjectSet)
.build())
.build();

var scsResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectConditionSetBlocking(scsRequest, Collections.emptyMap())
.execute());

// Create the subject mapping to grant yourself the entitlement
var smRequest = CreateSubjectMappingRequest.newBuilder()
.setAttributeValueId(attributeValueId)
.addActions(Action.newBuilder().setName("read").build())
.setExistingSubjectConditionSetId(scsResponse.getSubjectConditionSet().getId())
.build();

ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectMappingBlocking(smRequest, Collections.emptyMap())
.execute());

System.out.println("✅ Granted yourself access to department/marketing");

Now you can decrypt the TDF you encrypted with the marketing attribute. 🎉

Save TDF to a File

In production applications, you'll often need to persist encrypted TDFs to disk for storage, transmission, or archival. This allows you to:

  • Separate encryption from distribution: Encrypt data once, then share the TDF file through your preferred channels (email, S3, SFTP, etc.)
  • Enable offline access: Recipients can decrypt TDFs without needing to re-fetch data from your application
  • Archive encrypted data: Store TDFs in backup systems or long-term storage with their access policies intact
import java.io.FileOutputStream;

// After creating TDF config
try (FileOutputStream fos = new FileOutputStream("encrypted.tdf")) {
sdk.createTDF(inputStream, fos, tdfConfig);
}

// Later, decrypt from file
try (FileChannel fileChannel = FileChannel.open(
Path.of("encrypted.tdf"),
StandardOpenOption.READ)) {
var readerConfig = Config.newTDFReaderConfig();
var reader = sdk.loadTDF(fileChannel, readerConfig);
var decryptedOutput = new ByteArrayOutputStream();
reader.readPayload(decryptedOutput);

String decryptedContent = new String(decryptedOutput.toByteArray(), StandardCharsets.UTF_8);
System.out.println("✅ Data successfully decrypted");
System.out.println("📤 Decrypted content: " + decryptedContent);
}

Handle Large Files with Streaming

import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;

// Encrypt a large file
try (FileChannel inputChannel = FileChannel.open(
Path.of("large-file.pdf"), StandardOpenOption.READ);
FileChannel outputChannel = FileChannel.open(
Path.of("large-file.pdf.tdf"),
StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {

sdk.createTDF(inputChannel, outputChannel, tdfConfig);
}

Step 6: Complete Reference Implementation

For reference, here's a complete example showing all the pieces together:

Create Attribute, Encrypt, Grant Access, and Decrypt
src/main/java/io/opentdf/quickstart/QuickstartApp.java
package io.opentdf.quickstart;

import io.opentdf.platform.sdk.*;
import nl.altindag.ssl.SSLFactory;
import io.opentdf.platform.policy.AttributeRuleTypeEnum;
import io.opentdf.platform.policy.Action;
import io.opentdf.platform.policy.Condition;
import io.opentdf.platform.policy.ConditionBooleanTypeEnum;
import io.opentdf.platform.policy.ConditionGroup;
import io.opentdf.platform.policy.SubjectMappingOperatorEnum;
import io.opentdf.platform.policy.SubjectSet;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.Attribute;
import io.opentdf.platform.policy.namespaces.CreateNamespaceRequest;
import io.opentdf.platform.policy.namespaces.CreateNamespaceResponse;
import io.opentdf.platform.policy.namespaces.ListNamespacesRequest;
import io.opentdf.platform.policy.attributes.CreateAttributeRequest;
import io.opentdf.platform.policy.attributes.CreateAttributeResponse;
import io.opentdf.platform.policy.attributes.ListAttributesRequest;
import io.opentdf.platform.policy.subjectmapping.*;
import com.connectrpc.ResponseMessageKt;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;

public class QuickstartApp {
public static void main(String[] args) throws Exception {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "https://platform.opentdf.local:8443";

// Create SSL factory that trusts all certificates
// withTrustingAllCertificatesWithoutValidation() disables certificate verification
// This allows connections to the platform's self-signed certificate
// Only use this for local development - never in production!
SSLFactory sslFactory = SSLFactory.builder()
.withTrustingAllCertificatesWithoutValidation()
.build();

// Create SDK client (try-with-resources ensures proper cleanup)
SDKBuilder builder = SDKBuilder.newBuilder();
try (SDK sdk = builder.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.sslFactory(sslFactory)
.build()) {

// KASInfo specifies the Key Access Service (KAS) endpoint
// KAS manages encryption keys and enforces access policies
var kasInfo = new Config.KASInfo();
kasInfo.URL = platformEndpoint + "/kas";

// 1. Create namespace (or use existing)
CreateNamespaceResponse nsResponse;
try {
nsResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.createNamespaceBlocking(
CreateNamespaceRequest.newBuilder()
.setName("opentdf.io")
.build(),
Collections.emptyMap())
.execute());
System.out.println("✅ Created namespace: " + nsResponse.getNamespace().getId());
} catch (Exception e) {
if (e.getMessage() != null && (e.getMessage().contains("already_exists") || e.getMessage().contains("unique field violation"))) {
// Namespace already exists, fetch it
var listResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.listNamespacesBlocking(
ListNamespacesRequest.newBuilder().build(),
Collections.emptyMap())
.execute());
Namespace existingNs = null;
for (Namespace ns : listResponse.getNamespacesList()) {
if ("opentdf.io".equals(ns.getName())) {
existingNs = ns;
break;
}
}
nsResponse = CreateNamespaceResponse.newBuilder()
.setNamespace(existingNs)
.build();
System.out.println("✅ Using existing namespace: " + existingNs.getId());
} else {
throw e;
}
}

// 2. Create attribute with marketing value (or use existing)
var attrRequest = CreateAttributeRequest.newBuilder()
.setNamespaceId(nsResponse.getNamespace().getId())
.setName("department")
.setRule(AttributeRuleTypeEnum.ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF)
.addAllValues(Arrays.asList("marketing"))
.build();

CreateAttributeResponse attrResponse;
try {
attrResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.createAttributeBlocking(attrRequest, Collections.emptyMap())
.execute());
System.out.println("✅ Created attribute: " + attrResponse.getAttribute().getName());
} catch (Exception e) {
if (e.getMessage() != null && (e.getMessage().contains("already_exists") || e.getMessage().contains("unique field violation"))) {
// Attribute already exists, fetch it
var listResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.listAttributesBlocking(
ListAttributesRequest.newBuilder().build(),
Collections.emptyMap())
.execute());
Attribute existingAttr = null;
for (Attribute attr : listResponse.getAttributesList()) {
if ("department".equals(attr.getName()) &&
attr.getNamespace().getId().equals(nsResponse.getNamespace().getId())) {
existingAttr = attr;
break;
}
}
attrResponse = CreateAttributeResponse.newBuilder()
.setAttribute(existingAttr)
.build();
System.out.println("✅ Using existing attribute: " + existingAttr.getName());
} else {
throw e;
}
}

// 3. Encrypt data with the marketing attribute
String plaintext = "Sensitive marketing campaign data";
var inputStream = new ByteArrayInputStream(plaintext.getBytes(StandardCharsets.UTF_8));
var outputStream = new ByteArrayOutputStream();

var tdfConfig = Config.newTDFConfig(
Config.withKasInformation(kasInfo),
Config.withDataAttributes("https://opentdf.io/attr/department/value/marketing")
);

sdk.createTDF(inputStream, outputStream, tdfConfig);
System.out.println("✅ Data encrypted with marketing attribute");

// 4. Save TDF to file
try (var fos = new FileOutputStream("encrypted.tdf")) {
fos.write(outputStream.toByteArray());
}
System.out.println("✅ TDF saved to encrypted.tdf");

// 5. Grant yourself access to the marketing attribute
// Find the marketing value by name
String attributeValueId = null;
for (var value : attrResponse.getAttribute().getValuesList()) {
if ("marketing".equals(value.getValue())) {
attributeValueId = value.getId();
break;
}
}
if (attributeValueId == null) {
throw new RuntimeException("Marketing value not found in department attribute");
}

var condition = Condition.newBuilder()
.setSubjectExternalSelectorValue(".clientId")
.setOperator(SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("opentdf")
.build();

var conditionGroup = ConditionGroup.newBuilder()
.setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(condition)
.build();

var subjectSet = SubjectSet.newBuilder()
.addConditionGroups(conditionGroup)
.build();

var scsRequest = CreateSubjectConditionSetRequest.newBuilder()
.setSubjectConditionSet(
SubjectConditionSetCreate.newBuilder()
.addSubjectSets(subjectSet)
.build())
.build();

var scsResponse = ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectConditionSetBlocking(scsRequest, Collections.emptyMap())
.execute());

var smRequest = CreateSubjectMappingRequest.newBuilder()
.setAttributeValueId(attributeValueId)
.addActions(Action.newBuilder().setName("read").build())
.setExistingSubjectConditionSetId(scsResponse.getSubjectConditionSet().getId())
.build();

try {
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectMappingBlocking(smRequest, Collections.emptyMap())
.execute());
System.out.println("✅ Granted yourself access to department/marketing");
} catch (Exception e) {
if (e.getMessage() != null && (e.getMessage().contains("already_exists") || e.getMessage().contains("unique field violation"))) {
System.out.println("✅ Subject mapping already exists for department/marketing");
} else {
throw e;
}
}

// 6. Load TDF from file and decrypt
System.out.println("✅ TDF loaded from encrypted.tdf");

try (java.nio.channels.FileChannel fileChannel = java.nio.channels.FileChannel.open(
Path.of("encrypted.tdf"),
java.nio.file.StandardOpenOption.READ)) {
var readerConfig = Config.newTDFReaderConfig();
var reader = sdk.loadTDF(fileChannel, readerConfig);
var decryptedStream = new ByteArrayOutputStream();
reader.readPayload(decryptedStream);

System.out.println("✅ Data successfully decrypted");
System.out.println("📤 Decrypted content: " + decryptedStream.toString(StandardCharsets.UTF_8));
}
} // SDK auto-closes here, shutting down gRPC connections

// Force JVM exit (SDK background threads may not shut down immediately)
System.exit(0);
}
}

Troubleshooting

Having issues? See the SDK Troubleshooting guide for solutions to common problems.