Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
397 changes: 397 additions & 0 deletions java/app-encryption/docs/CryptoBenchmarks.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions java/app-encryption/dynamodb-local-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"installationId":"ff1906ba-da4e-459f-b350-81c90bd27002","telemetryEnabled":"true"}
61 changes: 38 additions & 23 deletions java/app-encryption/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.godaddy.asherah</groupId>
<artifactId>appencryption</artifactId>
<version>0.3.3</version>
<version>0.3.4</version>
<name>Asherah</name>
<description>
An application-layer encryption SDK that provides advanced encryption features and in depth defense against
Expand Down Expand Up @@ -42,40 +42,40 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<skip.surefire.tests>${skipTests}</skip.surefire.tests>
<!-- Project Dependencies-->
<apache.commons.version>3.19.0</apache.commons.version>
<amazonaws.version>2.0.0</amazonaws.version>
<aws.sdk.version>1.12.793</aws.sdk.version>
<aws.sdk-v2.version>2.37.0</aws.sdk-v2.version>
<bouncycastle.version>1.82</bouncycastle.version>
<apache.commons.version>3.20.0</apache.commons.version>
<amazonaws.version>2.6.1</amazonaws.version>
<aws.sdk.version>1.12.794</aws.sdk.version>
<aws.sdk-v2.version>2.40.1</aws.sdk-v2.version>
<bouncycastle.version>1.83</bouncycastle.version>
<build.helper.version>3.6.1</build.helper.version>
<caffeine.version>3.2.3</caffeine.version>
<checkerframework.version>3.51.1</checkerframework.version>
<commons.codec.version>1.19.0</commons.codec.version>
<checkerframework.version>3.52.1</checkerframework.version>
<commons.codec.version>1.20.0</commons.codec.version>
<commons.logging.version>1.3.5</commons.logging.version>
<commons.text.version>1.14.0</commons.text.version>
<guava.version>33.5.0-jre</guava.version>
<jackson.version>2.20.0</jackson.version>
<jackson.version>2.20.1</jackson.version>
<jacoco.version>0.8.14</jacoco.version>
<json.version>20250517</json.version>
<junit.jupiter.version>5.13.4</junit.jupiter.version>
<junit.platform.version>1.7.0</junit.platform.version>
<logback.version>1.5.20</logback.version>
<junit.jupiter.version>6.0.1</junit.jupiter.version>
<!-- junit.platform.version removed - JUnit 6 uses unified versioning -->
<logback.version>1.5.21</logback.version>
<maven.checkstyle.version>3.6.0</maven.checkstyle.version>
<maven.compiler.version>3.14.1</maven.compiler.version>
<maven.dependency.version>3.9.0</maven.dependency.version>
<maven.gpg.version>3.2.8</maven.gpg.version>
<maven.javadoc.version>3.12.0</maven.javadoc.version>
<maven.scm.version>2.2.1</maven.scm.version>
<maven.source.version>3.3.1</maven.source.version>
<maven.source.version>3.4.0</maven.source.version>
<maven.surefire.version>3.5.4</maven.surefire.version>
<micrometer.version>1.15.5</micrometer.version>
<micrometer.version>1.16.0</micrometer.version>
<mockito.core.version>5.20.0</mockito.core.version>
<central.publishing.maven.version>0.9.0</central.publishing.maven.version>
<securememory.version>0.1.6</securememory.version>
<securememory.version>0.1.7</securememory.version>
<slf4j.version>2.0.17</slf4j.version>
<sqlite4java.version>1.0.392</sqlite4java.version>
<testcontainers.version>1.11.2</testcontainers.version>
<zaxxer.version>7.0.2</zaxxer.version>
<jmh.version>1.37</jmh.version>
</properties>

<build>
Expand All @@ -84,17 +84,17 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven.checkstyle.version}</version>
<configuration>
<inputEncoding>UTF-8</inputEncoding>
<consoleOutput>true</consoleOutput>
<configLocation>${checkstyle.config}</configLocation>
<includeResources>false</includeResources>
<includeTestResources>false</includeTestResources>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<inputEncoding>UTF-8</inputEncoding>
<consoleOutput>true</consoleOutput>
<configLocation>${checkstyle.config}</configLocation>
<includeResources>false</includeResources>
<includeTestResources>false</includeTestResources>
</configuration>
<goals>
<goal>check</goal>
</goals>
Expand Down Expand Up @@ -428,6 +428,21 @@
<scope>test</scope>
</dependency>

<!-- JMH for benchmarking -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>

</dependencies>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public final class PayloadGenerator {
private static final RandomStringGenerator RANDOM_STRING_GENERATOR = new RandomStringGenerator.Builder()
.withinRange('0', 'z')
.filteredBy(Character::isLetterOrDigit)
.build();
.get();
private static final int DEFAULT_BYTE_SIZE = 20;

private PayloadGenerator() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.godaddy.asherah.utils;

import com.godaddy.asherah.TestSetup;
import com.godaddy.asherah.appencryption.SessionFactory;
import com.godaddy.asherah.appencryption.kms.KeyManagementService;
import com.godaddy.asherah.appencryption.persistence.Metastore;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import com.godaddy.asherah.appencryption.utils.SafeAutoCloseable;
import com.godaddy.asherah.crypto.CryptoPolicy;
import com.godaddy.asherah.crypto.NeverExpiredCryptoPolicy;
import com.godaddy.asherah.crypto.engine.bouncycastle.BouncyAes256GcmCrypto;
import com.godaddy.asherah.crypto.engine.CryptoEngineType;
import com.godaddy.asherah.crypto.envelope.AeadEnvelopeCrypto;
import com.godaddy.asherah.crypto.keys.SecureCryptoKeyMap;
import com.google.common.annotations.VisibleForTesting;

Expand All @@ -42,11 +43,12 @@ public class SessionFactory implements SafeAutoCloseable {
private final SecureCryptoKeyMap<Instant> systemKeyCache;
private final CryptoPolicy cryptoPolicy;
private final KeyManagementService keyManagementService;
private final CryptoEngineType cryptoEngineType;
private final Cache<String, CachedSession> sessionCache;

/**
* Creates a new {@code SessionFactory} instance using the provided parameters. A session factory is required to
* generate cryptographic sessions.
* generate cryptographic sessions. Uses the default crypto engine (BouncyCastle).
*
* @param productId A unique identifier for a product.
* @param serviceId A unique identifier for a service.
Expand All @@ -64,12 +66,38 @@ public SessionFactory(
final SecureCryptoKeyMap<Instant> systemKeyCache,
final CryptoPolicy cryptoPolicy,
final KeyManagementService keyManagementService) {
this(productId, serviceId, metastore, systemKeyCache, cryptoPolicy, keyManagementService, CryptoEngineType.DEFAULT);
}

/**
* Creates a new {@code SessionFactory} instance using the provided parameters. A session factory is required to
* generate cryptographic sessions.
*
* @param productId A unique identifier for a product.
* @param serviceId A unique identifier for a service.
* @param metastore A {@link Metastore} implementation used to store system and intermediate keys.
* @param systemKeyCache A {@link java.util.concurrent.ConcurrentSkipListMap} based implementation for caching
* system keys.
* @param cryptoPolicy A {@link CryptoPolicy} implementation that dictates the various behaviors of Asherah.
* @param keyManagementService A {@link KeyManagementService} implementation that generates the top level master key
* and encrypts the system keys using the master key.
* @param cryptoEngineType The {@link CryptoEngineType} to use for encryption operations.
*/
public SessionFactory(
final String productId,
final String serviceId,
final Metastore<JSONObject> metastore,
final SecureCryptoKeyMap<Instant> systemKeyCache,
final CryptoPolicy cryptoPolicy,
final KeyManagementService keyManagementService,
final CryptoEngineType cryptoEngineType) {
this.productId = productId;
this.serviceId = serviceId;
this.metastore = metastore;
this.systemKeyCache = systemKeyCache;
this.cryptoPolicy = cryptoPolicy;
this.keyManagementService = keyManagementService;
this.cryptoEngineType = cryptoEngineType;

this.sessionCache = Caffeine.newBuilder()
.weigher((String intermediateKeyId, CachedSession session) -> {
Expand Down Expand Up @@ -238,13 +266,14 @@ private EnvelopeEncryption<JSONObject> getEnvelopeEncryptionJson(final String pa
// Wrap the creation logic in a lambda so the cache entry acquisition can create a new instance when needed
Function<String, EnvelopeEncryptionJsonImpl> createFunc = id -> {
Partition partition = getPartition(partitionId);
AeadEnvelopeCrypto crypto = cryptoEngineType.createCryptoEngine();

return new EnvelopeEncryptionJsonImpl(
partition,
metastore,
systemKeyCache,
new SecureCryptoKeyMap<>(cryptoPolicy.getRevokeCheckPeriodMillis()),
new BouncyAes256GcmCrypto(),
crypto,
cryptoPolicy,
keyManagementService);
};
Expand Down Expand Up @@ -297,6 +326,7 @@ public static final class Builder implements MetastoreStep, CryptoPolicyStep, Ke
private Metastore<JSONObject> metastore;
private CryptoPolicy cryptoPolicy;
private KeyManagementService keyManagementService;
private CryptoEngineType cryptoEngineType = CryptoEngineType.DEFAULT;
private boolean metricsEnabled = false;

private Builder(final String productId, final String serviceId) {
Expand Down Expand Up @@ -347,6 +377,12 @@ public BuildStep withMetricsEnabled() {
return this;
}

@Override
public BuildStep withCryptoEngine(final CryptoEngineType engineType) {
this.cryptoEngineType = engineType;
return this;
}

@Override
public SessionFactory build() {
if (!metricsEnabled) {
Expand All @@ -355,7 +391,8 @@ public SessionFactory build() {
}

return new SessionFactory(productId, serviceId, metastore,
new SecureCryptoKeyMap<>(cryptoPolicy.getRevokeCheckPeriodMillis()), cryptoPolicy, keyManagementService);
new SecureCryptoKeyMap<>(cryptoPolicy.getRevokeCheckPeriodMillis()), cryptoPolicy, keyManagementService,
cryptoEngineType);
}
}

Expand Down Expand Up @@ -423,6 +460,17 @@ public interface BuildStep {
*/
BuildStep withMetricsEnabled();

/**
* Specifies the crypto engine type to use for encryption operations.
* Defaults to {@link CryptoEngineType#DEFAULT} (BouncyCastle) for backward compatibility.
*
* <p>Use {@link CryptoEngineType#JDK} for GraalVM native-image compatibility and better performance.
*
* @param engineType The {@link CryptoEngineType} to use.
* @return The current {@code BuildStep} instance with the specified crypto engine.
*/
BuildStep withCryptoEngine(CryptoEngineType engineType);

/**
* Builds the finalized session factory with the parameters specified in the {@code Builder}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import com.godaddy.asherah.appencryption.exceptions.KmsException;
import com.godaddy.asherah.appencryption.utils.Json;
import com.godaddy.asherah.appencryption.utils.MetricsUtil;
import com.godaddy.asherah.crypto.engine.bouncycastle.BouncyAes256GcmCrypto;
import com.godaddy.asherah.crypto.engine.CryptoEngineType;
import com.godaddy.asherah.crypto.envelope.AeadEnvelopeCrypto;
import com.godaddy.asherah.crypto.keys.CryptoKey;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
Expand Down Expand Up @@ -331,30 +331,46 @@ public static final class Builder {
private final Map<String, String> regionToArnMap;
private final String preferredRegion;
private AwsKmsClientFactory kmsClientFactory = new AwsKmsClientFactory.DefaultAwsKmsClientFactory();
private CryptoEngineType cryptoEngineType = CryptoEngineType.DEFAULT;

private Builder(final Map<String, String> regionToArnMap, final String region) {
this.regionToArnMap = regionToArnMap;
this.preferredRegion = region;
}

/** Specifies custom kms client factory to use.
/**
* Specifies custom kms client factory to use.
*
* @param clientFactory The AWS KMS client factory.
* @return The current {@code BuildStep} instance.
* @return The current {@code Builder} instance.
*/
public Builder withKmsClientFactory(final AwsKmsClientFactory clientFactory) {
kmsClientFactory = clientFactory;
return this;
}

/**
* Specifies the crypto engine type to use for encryption operations.
* Defaults to {@link CryptoEngineType#DEFAULT} (BouncyCastle) for backward compatibility.
*
* <p>Use {@link CryptoEngineType#JDK} for GraalVM native-image compatibility and better performance.
*
* @param engineType The {@link CryptoEngineType} to use.
* @return The current {@code Builder} instance.
*/
public Builder withCryptoEngine(final CryptoEngineType engineType) {
this.cryptoEngineType = engineType;
return this;
}

/**
* Builds the {@link AwsKeyManagementServiceImpl} object.
*
* @return The fully instantiated {@link AwsKeyManagementServiceImpl} object.
*/
public AwsKeyManagementServiceImpl build() {
return new AwsKeyManagementServiceImpl(regionToArnMap, preferredRegion, new BouncyAes256GcmCrypto(),
kmsClientFactory);
return new AwsKeyManagementServiceImpl(regionToArnMap, preferredRegion,
cryptoEngineType.createCryptoEngine(), kmsClientFactory);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@
/**
* A factory to create an AWS KMS client based on the region provided.
*/
interface AwsKmsClientFactory {
public interface AwsKmsClientFactory {

/**
* Builds a KMS client for the specified AWS region.
*
* @param region the AWS region identifier
* @return a configured KmsClient instance
*/
KmsClient build(String region);

/**
* Returns the default factory implementation that creates standard KMS clients.
*
* @return the default KMS client factory
*/
static DefaultAwsKmsClientFactory defaultFactory() {
return new DefaultAwsKmsClientFactory();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,44 @@

import java.time.Instant;

import com.godaddy.asherah.crypto.engine.bouncycastle.BouncyAes256GcmCrypto;
import com.godaddy.asherah.crypto.engine.CryptoEngineType;
import com.godaddy.asherah.crypto.envelope.AeadEnvelopeCrypto;
import com.godaddy.asherah.crypto.keys.CryptoKey;
import com.godaddy.asherah.crypto.keys.SecretCryptoKey;
import com.godaddy.asherah.securememory.Secret;
import com.godaddy.asherah.securememory.TransientSecretFactory;

/**
* An implementation of {@link KeyManagementService} that uses {@link BouncyAes256GcmCrypto} to encrypt/decrypt keys.
* An implementation of {@link KeyManagementService} that uses AES-256-GCM to encrypt/decrypt keys.
* Note: This should NEVER be used in production.
*/
public class StaticKeyManagementServiceImpl implements KeyManagementService {
private final CryptoKey encryptionKey;
private final BouncyAes256GcmCrypto crypto = new BouncyAes256GcmCrypto();
private final AeadEnvelopeCrypto crypto;

/**
* Creates a new {@code StaticKeyManagementServiceImpl} instance. This implementation of {@link KeyManagementService}
* uses a static master key to encrypt the system keys.
* uses a static master key to encrypt the system keys. Uses the default crypto engine (BouncyCastle).
*
* @param key The static master key.
*/
public StaticKeyManagementServiceImpl(final String key) {
this(key, CryptoEngineType.DEFAULT);
}

/**
* Creates a new {@code StaticKeyManagementServiceImpl} instance. This implementation of {@link KeyManagementService}
* uses a static master key to encrypt the system keys.
*
* @param key The static master key.
* @param cryptoEngineType The {@link CryptoEngineType} to use for encryption operations.
*/
public StaticKeyManagementServiceImpl(final String key, final CryptoEngineType cryptoEngineType) {
byte[] keyBytes = key.getBytes();
Secret secretKey = new TransientSecretFactory().createSecret(keyBytes);

encryptionKey = new SecretCryptoKey(secretKey, Instant.now(), false);
crypto = cryptoEngineType.createCryptoEngine();
}

@Override
Expand Down
Loading