Skip to content
Merged
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
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# sha from https://hub.docker.com/layers/library/eclipse-temurin/21.0.9_10-jre-alpine-3.23/images/sha256-f599f6fa11f007b6dcf6e85ec2c372c1eba2b6940a7828eb6e665665ea5edd1c
FROM eclipse-temurin@sha256:243e711289b0f17e05a4df60454bbb1b8ed7b126db4de2d5535da994b7417111

RUN apk add --no-cache gcompat

WORKDIR /app
EXPOSE 8080

Expand Down
21 changes: 21 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<vertx.verticle>com.uid2.operator.vertx.UIDOperatorVerticle</vertx.verticle>
<!-- check micrometer.version vertx-micrometer-metrics consumes before bumping up -->
<micrometer.version>1.12.2</micrometer.version>
<accp.version>2.5.0</accp.version>
<enclave-api.version>2.1.6</enclave-api.version>
<enclave-aws.version>2.1.0</enclave-aws.version>
<enclave-azure.version>2.1.19</enclave-azure.version>
Expand Down Expand Up @@ -203,6 +204,26 @@
<version>5.12.0</version>
<scope>test</scope>
</dependency>

<!-- ACCP - Amazon Corretto Crypto Provider (Linux + macOS) -->
<dependency>
<groupId>software.amazon.cryptools</groupId>
<artifactId>AmazonCorrettoCryptoProvider</artifactId>
<version>${accp.version}</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>software.amazon.cryptools</groupId>
<artifactId>AmazonCorrettoCryptoProvider</artifactId>
<version>${accp.version}</version>
<classifier>osx-x86_64</classifier>
</dependency>
<dependency>
<groupId>software.amazon.cryptools</groupId>
<artifactId>AmazonCorrettoCryptoProvider</artifactId>
<version>${accp.version}</version>
<classifier>osx-aarch_64</classifier>
</dependency>
</dependencies>

<profiles>
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/com/uid2/operator/service/CryptoProviderService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.uid2.operator.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider;

import javax.crypto.KeyAgreement;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;

public class CryptoProviderService {
private static final Logger LOGGER = LoggerFactory.getLogger(CryptoProviderService.class);

// ECDH provider selection: tries ACCP first, falls back to default (SunEC)
private static final String ECDH_PROVIDER_NAME = initEcdhProvider();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just call initEcdhProvider in createKeyAgreement

Copy link
Contributor Author

@RSam25 RSam25 Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to ensure the provider is registered once globally. Calling initECDHProvider in createKeyAgreement would try to register it for every thread that tries to access it. While Security.addProvider is idempotent, I didn't want to repeat that operation unnecessarily, especially since if ACCP is not available for some reason, it would throw an exception (which is caught) that would unwind the stack and introduce some overhead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling initECDHProvider once vs 10 times is a bit of a microoptimisation which sacrifices readability, for example, I had to read twice to understand why this was a static variable, if it was used anywhere else, etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. However, I removed ThreadLocal caching due to the issue Andrei pointed out and now it is important we don't throw an exception every time if ACCP doesn't load for some reason.


private static String initEcdhProvider() {
// Try ACCP (Amazon Corretto Crypto Provider) first
try {
// Add ACCP at lowest priority so it doesn't become default for other algorithms
Security.addProvider(AmazonCorrettoCryptoProvider.INSTANCE);

// Verify it works for ECDH
KeyAgreement ka = KeyAgreement.getInstance("ECDH", AmazonCorrettoCryptoProvider.PROVIDER_NAME);
LOGGER.info("ECDH using AmazonCorrettoCryptoProvider (added at lowest priority)");
return AmazonCorrettoCryptoProvider.PROVIDER_NAME;
} catch (Throwable e) {
// ACCP not available
LOGGER.info("AmazonCorrettoCryptoProvider is not available: {}", e.getMessage());
}

// Fall back to default provider
LOGGER.info("ECDH using default provider (SunEC)");
return null;
}

/**
* Create ECDH Key Agreement using ACCP if available, fall back to SunEC if not
* @return ECDH KeyAgreement
* @throws NoSuchAlgorithmException
*/
public static KeyAgreement createKeyAgreement() throws NoSuchAlgorithmException {
if (ECDH_PROVIDER_NAME != null) {
try {
return KeyAgreement.getInstance("ECDH", ECDH_PROVIDER_NAME);
} catch (NoSuchProviderException e) {
LOGGER.info("{} is not available: {}", ECDH_PROVIDER_NAME, e.getMessage());
}
}
return KeyAgreement.getInstance("ECDH");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ private void handleClientSideTokenGenerateImpl(RoutingContext rc) throws NoSuchA
}

// Perform key agreement
final KeyAgreement ka = KeyAgreement.getInstance("ECDH");
final KeyAgreement ka = CryptoProviderService.createKeyAgreement();
ka.init(clientSideKeypair.getPrivateKey());
ka.doPhase(clientPublicKey, true);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.uid2.operator;

import com.uid2.operator.service.CryptoProviderService;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
Expand Down Expand Up @@ -37,7 +39,7 @@ public static PrivateKey stringToPrivateKey(String privateKeyString, KeyFactory
}

public static SecretKey deriveKey(PublicKey serverPublicKey, PrivateKey clientPrivateKey) throws NoSuchAlgorithmException, InvalidKeyException {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
KeyAgreement keyAgreement = CryptoProviderService.createKeyAgreement();
keyAgreement.init(clientPrivateKey);
keyAgreement.doPhase(serverPublicKey, true);

Expand Down
Loading