diff --git a/examples/pom.xml b/examples/pom.xml
index 92176b16..e782e24e 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -61,6 +61,11 @@
org.apache.logging.log4j
log4j-core
+
+ commons-cli
+ commons-cli
+ 1.4
+
org.apache.logging.log4j
log4j-api
diff --git a/examples/src/main/java/io/opentdf/platform/DecryptExample.java b/examples/src/main/java/io/opentdf/platform/DecryptExample.java
index 693192ca..92698ab2 100644
--- a/examples/src/main/java/io/opentdf/platform/DecryptExample.java
+++ b/examples/src/main/java/io/opentdf/platform/DecryptExample.java
@@ -1,13 +1,12 @@
package io.opentdf.platform;
+
import io.opentdf.platform.sdk.*;
import java.nio.file.StandardOpenOption;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
-
import com.nimbusds.jose.JOSEException;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -15,14 +14,33 @@
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
+import org.apache.commons.cli.*;
import org.apache.commons.codec.DecoderException;
-
public class DecryptExample {
public static void main(String[] args) throws IOException,
- InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException,
- BadPaddingException, InvalidKeyException, TDF.FailedToCreateGMAC,
- JOSEException, ParseException, NoSuchAlgorithmException, DecoderException {
+ InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException,
+ BadPaddingException, InvalidKeyException, TDF.FailedToCreateGMAC,
+ JOSEException, ParseException, NoSuchAlgorithmException, DecoderException, org.apache.commons.cli.ParseException {
+
+ // Create Options object
+ Options options = new Options();
+
+ // Add rewrap encapsulation algorithm option
+ options.addOption(Option.builder("A")
+ .longOpt("rewrap-encapsulation-algorithm")
+ .hasArg()
+ .desc("Key wrap response algorithm algorithm:parameters")
+ .build());
+
+ // Parse command line arguments
+ CommandLineParser parser = new DefaultParser();
+ CommandLine cmd = parser.parse(options, args);
+
+ // Get the rewrap encapsulation algorithm
+ String rewrapEncapsulationAlgorithm = cmd.getOptionValue("rewrap-encapsulation-algorithm", "rsa:2048");
+ var sessionKeyType = KeyType.fromString(rewrapEncapsulationAlgorithm.toLowerCase());
+
String clientId = "opentdf";
String clientSecret = "secret";
@@ -35,8 +53,11 @@ public static void main(String[] args) throws IOException,
Path path = Paths.get("my.ciphertext");
try (var in = FileChannel.open(path, StandardOpenOption.READ)) {
- var reader = new TDF().loadTDF(in, sdk.getServices().kas());
+ var reader = new TDF().loadTDF(in, sdk.getServices().kas(), Config.newTDFReaderConfig(Config.WithSessionKeyType(sessionKeyType)));
reader.readPayload(System.out);
}
+
+ // Print the rewrap encapsulation algorithm
+ System.out.println("Rewrap Encapsulation Algorithm: " + rewrapEncapsulationAlgorithm);
}
-}
+}
\ No newline at end of file
diff --git a/examples/src/main/java/io/opentdf/platform/EncryptExample.java b/examples/src/main/java/io/opentdf/platform/EncryptExample.java
index 6dcccb0b..f9ac0176 100644
--- a/examples/src/main/java/io/opentdf/platform/EncryptExample.java
+++ b/examples/src/main/java/io/opentdf/platform/EncryptExample.java
@@ -1,18 +1,35 @@
package io.opentdf.platform;
+
import io.opentdf.platform.sdk.*;
import java.io.ByteArrayInputStream;
-import java.io.BufferedOutputStream;
import java.nio.charset.StandardCharsets;
import java.io.FileOutputStream;
-
import com.nimbusds.jose.JOSEException;
+import org.apache.commons.cli.*;
import org.apache.commons.codec.DecoderException;
-
import java.io.IOException;
import java.util.concurrent.ExecutionException;
public class EncryptExample {
- public static void main(String[] args) throws IOException, JOSEException, AutoConfigureException, InterruptedException, ExecutionException, DecoderException {
+ public static void main(String[] args) throws IOException, JOSEException, AutoConfigureException,
+ InterruptedException, ExecutionException, DecoderException, ParseException {
+ // Create Options object
+ Options options = new Options();
+
+ // Add key encapsulation algorithm option
+ options.addOption(Option.builder("A")
+ .longOpt("key-encapsulation-algorithm")
+ .hasArg()
+ .desc("Key wrap algorithm algorithm:parameters")
+ .build());
+
+ // Parse command line arguments
+ CommandLineParser parser = new DefaultParser();
+ CommandLine cmd = parser.parse(options, args);
+
+ // Get the key encapsulation algorithm
+ String keyEncapsulationAlgorithm = cmd.getOptionValue("key-encapsulation-algorithm", "rsa:2048");
+
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
@@ -25,17 +42,19 @@ public static void main(String[] args) throws IOException, JOSEException, AutoCo
var kasInfo = new Config.KASInfo();
kasInfo.URL = "http://localhost:8080/kas";
- var tdfConfig = Config.newTDFConfig(Config.withKasInformation(kasInfo), Config.withDataAttributes("https://example.com/attr/color/value/red"));
-
+ var wrappingKeyType = KeyType.fromString(keyEncapsulationAlgorithm.toLowerCase());
+ var tdfConfig = Config.newTDFConfig(Config.withKasInformation(kasInfo),
+ Config.withDataAttributes("https://example.com/attr/color/value/red"),
+ Config.WithWrappingKeyAlg(wrappingKeyType));
String str = "Hello, World!";
-
+
// Convert String to InputStream
var in = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
FileOutputStream fos = new FileOutputStream("my.ciphertext");
new TDF().createTDF(in, fos, tdfConfig,
- sdk.getServices().kas(),
- sdk.getServices().attributes());
+ sdk.getServices().kas(),
+ sdk.getServices().attributes());
}
-}
+}
\ No newline at end of file
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java
index b90380d1..a06daaf0 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java
@@ -7,10 +7,8 @@
import io.opentdf.platform.sdk.nanotdf.SymmetricAndPayloadConfig;
import io.opentdf.platform.policy.Value;
-import org.bouncycastle.oer.its.ieee1609dot2.HeaderInfo;
import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
@@ -99,12 +97,14 @@ public static class TDFReaderConfig {
// Optional Map of Assertion Verification Keys
AssertionVerificationKeys assertionVerificationKeys = new AssertionVerificationKeys();
boolean disableAssertionVerification;
+ KeyType sessionKeyType;
}
@SafeVarargs
public static TDFReaderConfig newTDFReaderConfig(Consumer... options) {
TDFReaderConfig config = new TDFReaderConfig();
config.disableAssertionVerification = false;
+ config.sessionKeyType = KeyType.RSA2048Key;
for (Consumer option : options) {
option.accept(config);
}
@@ -120,6 +120,9 @@ public static Consumer withDisableAssertionVerification(boolean
return (TDFReaderConfig config) -> config.disableAssertionVerification = disable;
}
+ public static Consumer WithSessionKeyType(KeyType keyType) {
+ return (TDFReaderConfig config) -> config.sessionKeyType = keyType;
+ }
public static class TDFConfig {
public Boolean autoconfigure;
public int defaultSegmentSize;
@@ -136,6 +139,7 @@ public static class TDFConfig {
public List assertionConfigList;
public String mimeType;
public List splitPlan;
+ public KeyType wrappingKeyType;
public TDFConfig() {
this.autoconfigure = true;
@@ -149,6 +153,7 @@ public TDFConfig() {
this.assertionConfigList = new ArrayList<>();
this.mimeType = DEFAULT_MIME_TYPE;
this.splitPlan = new ArrayList<>();
+ this.wrappingKeyType = KeyType.RSA2048Key;
}
}
@@ -246,6 +251,10 @@ public static Consumer withAutoconfigure(boolean enable) {
};
}
+ public static Consumer WithWrappingKeyAlg(KeyType keyType) {
+ return (TDFConfig config) -> config.wrappingKeyType = keyType;
+ }
+
// public static Consumer withDisableEncryption() {
// return (TDFConfig config) -> config.enableEncryption = false;
// }
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java b/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java
index 404637df..e7f8c5c3 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java
@@ -3,6 +3,7 @@
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
+import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
/**
@@ -39,6 +40,30 @@ public static KeyPair generateRSAKeypair() {
return kpg.generateKeyPair();
}
+ public static KeyPair generateECKeypair(String curveName) {
+ KeyPairGenerator kpg;
+ try {
+ kpg = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec(curveName);
+ kpg.initialize(ecSpec);
+ } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
+ throw new SDKException("error creating EC keypair", e);
+ }
+ return kpg.generateKeyPair();
+ }
+
+ public static String getPublicKeyPEM(PublicKey publicKey) {
+ return "-----BEGIN PUBLIC KEY-----\r\n" +
+ Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) +
+ "\r\n-----END PUBLIC KEY-----";
+ }
+
+ public static String getPrivateKeyPEM(PrivateKey privateKey) {
+ return "-----BEGIN PRIVATE KEY-----\r\n" +
+ Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()) +
+ "\r\n-----END PRIVATE KEY-----";
+ }
+
public static String getRSAPublicKeyPEM(PublicKey publicKey) {
if (!"RSA".equals(publicKey.getAlgorithm())) {
throw new IllegalArgumentException("can't get public key PEM for algorithm [" + publicKey.getAlgorithm() + "]");
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
index 806d48d0..def107fc 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java
@@ -21,6 +21,7 @@
import io.opentdf.platform.sdk.nanotdf.NanoTDFType;
import io.opentdf.platform.sdk.TDF.KasBadRequestException;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.net.MalformedURLException;
@@ -32,6 +33,7 @@
import java.util.HashMap;
import java.util.function.Function;
+import static io.opentdf.platform.sdk.TDF.GLOBAL_KEY_SALT;
import static java.lang.String.format;
/**
@@ -43,9 +45,8 @@ public class KASClient implements SDK.KAS {
private final Function channelFactory;
private final RSASSASigner signer;
- private final AsymDecryption decryptor;
- private final String publicKeyPEM;
-
+ private AsymDecryption decryptor;
+ private String clientPublicKey;
private KASKeyCache kasKeyCache;
/***
@@ -62,10 +63,6 @@ public KASClient(Function channelFactory, RSAKey dpopKey
} catch (JOSEException e) {
throw new SDKException("error creating dpop signer", e);
}
- var encryptionKeypair = CryptoUtils.generateRSAKeypair();
- decryptor = new AsymDecryption(encryptionKeypair.getPrivate());
- publicKeyPEM = CryptoUtils.getRSAPublicKeyPEM(encryptionKeypair.getPublic());
-
this.kasKeyCache = new KASKeyCache();
}
@@ -86,7 +83,12 @@ public Config.KASInfo getPublicKey(Config.KASInfo kasInfo) {
if (cachedValue != null) {
return cachedValue;
}
- PublicKeyResponse resp = getStub(kasInfo.URL).publicKey(PublicKeyRequest.getDefaultInstance());
+
+ PublicKeyRequest request = (kasInfo.Algorithm == null || kasInfo.Algorithm.isEmpty())
+ ? PublicKeyRequest.getDefaultInstance()
+ : PublicKeyRequest.newBuilder().setAlgorithm(kasInfo.Algorithm).build();
+
+ PublicKeyResponse resp = getStub(kasInfo.URL).publicKey(request);
var kiCopy = new Config.KASInfo();
kiCopy.KID = resp.getKid();
@@ -161,13 +163,28 @@ static class NanoTDFRewrapRequestBody {
private static final Gson gson = new Gson();
@Override
- public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy) {
+ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessionKeyType) {
+ ECKeyPair ecKeyPair = null;
+
+ if (sessionKeyType.isEc()) {
+ var curveName = sessionKeyType.getCurveName();
+ ecKeyPair = new ECKeyPair(curveName, ECKeyPair.ECAlgorithm.ECDH);
+ clientPublicKey = ecKeyPair.publicKeyInPEMFormat();
+ } else {
+ // Initialize the RSA key pair only once and reuse it for future unwrap operations
+ if (decryptor == null) {
+ var encryptionKeypair = CryptoUtils.generateRSAKeypair();
+ decryptor = new AsymDecryption(encryptionKeypair.getPrivate());
+ clientPublicKey = CryptoUtils.getRSAPublicKeyPEM(encryptionKeypair.getPublic());
+ }
+ }
+
RewrapRequestBody body = new RewrapRequestBody();
body.policy = policy;
- body.clientPublicKey = publicKeyPEM;
+ body.clientPublicKey = clientPublicKey;
body.keyAccess = keyAccess;
- var requestBody = gson.toJson(body);
+ var requestBody = gson.toJson(body);
var claims = new JWTClaimsSet.Builder()
.claim("requestBody", requestBody)
.issueTime(Date.from(Instant.now()))
@@ -190,7 +207,24 @@ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy) {
try {
response = getStub(keyAccess.url).rewrap(request);
var wrappedKey = response.getEntityWrappedKey().toByteArray();
- return decryptor.decrypt(wrappedKey);
+ if (sessionKeyType != KeyType.RSA2048Key) {
+
+ if (ecKeyPair == null) {
+ throw new SDKException("ECKeyPair is null. Unable to proceed with the unwrap operation.");
+ }
+
+ var kasEphemeralPublicKey = response.getSessionPublicKey();
+ var publicKey = ECKeyPair.publicKeyFromPem(kasEphemeralPublicKey);
+ byte[] symKey = ECKeyPair.computeECDHKey(publicKey, ecKeyPair.getPrivateKey());
+
+ var sessionKey = ECKeyPair.calculateHKDF(GLOBAL_KEY_SALT, symKey);
+
+ AesGcm gcm = new AesGcm(sessionKey);
+ AesGcm.Encrypted encrypted = new AesGcm.Encrypted(wrappedKey);
+ return gcm.decrypt(encrypted);
+ } else {
+ return decryptor.decrypt(wrappedKey);
+ }
} catch (StatusRuntimeException e) {
if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) {
// 400 Bad Request
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java b/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java
new file mode 100644
index 00000000..9c5cf010
--- /dev/null
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java
@@ -0,0 +1,45 @@
+package io.opentdf.platform.sdk;
+
+public enum KeyType {
+ RSA2048Key("rsa:2048"),
+ EC256Key("ec:secp256r1"),
+ EC384Key("ec:secp384r1"),
+ EC521Key("ec:secp521r1");
+
+ private final String keyType;
+
+ KeyType(String keyType) {
+ this.keyType = keyType;
+ }
+
+ @Override
+ public String toString() {
+ return keyType;
+ }
+
+ public String getCurveName() {
+ switch (this) {
+ case EC256Key:
+ return "secp256r1";
+ case EC384Key:
+ return "secp384r1";
+ case EC521Key:
+ return "secp521r1";
+ default:
+ throw new IllegalArgumentException("Unsupported key type: " + this);
+ }
+ }
+
+ public static KeyType fromString(String keyType) {
+ for (KeyType type : KeyType.values()) {
+ if (type.keyType.equalsIgnoreCase(keyType)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException("No enum constant for key type: " + keyType);
+ }
+
+ public boolean isEc() {
+ return this != RSA2048Key;
+ }
+}
\ No newline at end of file
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
index 253c6207..32c50b19 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java
@@ -197,25 +197,25 @@ static public class KeyAccess {
public String kid;
public String sid;
public String schemaVersion;
+ public String ephemeralPublicKey;
@Override
public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
KeyAccess keyAccess = (KeyAccess) o;
return Objects.equals(keyType, keyAccess.keyType) && Objects.equals(url, keyAccess.url)
&& Objects.equals(protocol, keyAccess.protocol) && Objects.equals(wrappedKey, keyAccess.wrappedKey)
&& Objects.equals(policyBinding, keyAccess.policyBinding)
&& Objects.equals(encryptedMetadata, keyAccess.encryptedMetadata)
&& Objects.equals(kid, keyAccess.kid)
- && Objects.equals(schemaVersion, keyAccess.schemaVersion);
+ && Objects.equals(schemaVersion, keyAccess.schemaVersion)
+ && Objects.equals(ephemeralPublicKey, keyAccess.ephemeralPublicKey);
}
@Override
public int hashCode() {
- return Objects.hash(keyType, url, protocol, wrappedKey, policyBinding, encryptedMetadata, kid, schemaVersion);
+ return Objects.hash(keyType, url, protocol, wrappedKey, policyBinding, encryptedMetadata, kid, schemaVersion, ephemeralPublicKey);
}
}
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
index 14affa4c..db19066e 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
@@ -53,7 +53,8 @@ public interface KAS extends AutoCloseable {
Config.KASInfo getECPublicKey(Config.KASInfo kasInfo, NanoTDFType.ECCurve curve);
- byte[] unwrap(Manifest.KeyAccess keyAccess, String policy);
+ byte[] unwrap(Manifest.KeyAccess keyAccess, String policy,
+ KeyType sessionKeyType);
byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kasURL);
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
index 73e7fbe3..9be62212 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
@@ -10,8 +10,10 @@
import io.opentdf.platform.sdk.Autoconfigure.AttributeValueFQN;
import io.opentdf.platform.sdk.Config.KASInfo;
+import io.opentdf.platform.sdk.nanotdf.ECKeyPair;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,9 +37,10 @@
*/
public class TDF {
+ public static final byte[] GLOBAL_KEY_SALT = "salt".getBytes(StandardCharsets.UTF_8);
private static final String EMPTY_SPLIT_ID = "";
private static final String TDF_VERSION = "4.3.0";
- private static final String KEY_ACCESS_SECHMA_VERSION = "4.3.0";
+ private static final String KEY_ACCESS_SECHMA_VERSION = "1.0";
private final long maximumSize;
/**
@@ -63,6 +66,7 @@ public TDF() {
private static final int GCM_KEY_SIZE = 32;
private static final String kSplitKeyType = "split";
private static final String kWrapped = "wrapped";
+ private static final String kECWrapped = "ec-wrapped";
private static final String kKasProtocol = "kas";
private static final int kGcmIvSize = 12;
private static final int kAesBlockSize = 16;
@@ -168,6 +172,11 @@ public static class EncryptedMetadata {
private String iv;
}
+ public static class ECKeyWrappedKeyInfo {
+ private String publicKey;
+ private String wrappedKey;
+ }
+
public static class TDFObject {
private Manifest manifest;
private long size;
@@ -244,7 +253,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) {
logger.info("no public key provided for KAS at {}, retrieving", splitInfo.kas);
var getKI = new Config.KASInfo();
getKI.URL = splitInfo.kas;
- getKI.Algorithm = "rsa:2048";
+ getKI.Algorithm = tdfConfig.wrappingKeyType.toString();
getKI = kas.getPublicKey(getKI);
latestKASInfo.put(splitInfo.kas, getKI);
ki = getKI;
@@ -292,21 +301,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) {
throw new KasPublicKeyMissing("Kas public key is missing in kas information list");
}
- // Wrap the key with kas public key
- AsymEncryption asymmetricEncrypt = new AsymEncryption(kasInfo.PublicKey);
- byte[] wrappedKey = asymmetricEncrypt.encrypt(symKey);
-
- Manifest.KeyAccess keyAccess = new Manifest.KeyAccess();
- keyAccess.keyType = kWrapped;
- keyAccess.url = kasInfo.URL;
- keyAccess.kid = kasInfo.KID;
- keyAccess.protocol = kKasProtocol;
- keyAccess.policyBinding = policyBinding;
- keyAccess.wrappedKey = encoder.encodeToString(wrappedKey);
- keyAccess.encryptedMetadata = encryptedMetadata;
- keyAccess.sid = splitID;
- keyAccess.schemaVersion = KEY_ACCESS_SECHMA_VERSION;
-
+ var keyAccess = createKeyAccess(tdfConfig, kasInfo, symKey, policyBinding, encryptedMetadata, splitID);
manifest.encryptionInformation.keyAccessObj.add(keyAccess);
}
}
@@ -323,8 +318,56 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) {
this.aesGcm = new AesGcm(this.payloadKey);
}
+
+ private Manifest.KeyAccess createKeyAccess(Config.TDFConfig tdfConfig, Config.KASInfo kasInfo, byte[] symKey, Manifest.PolicyBinding policyBinding, String encryptedMetadata, String splitID) {
+ Manifest.KeyAccess keyAccess = new Manifest.KeyAccess();
+ keyAccess.keyType = kWrapped;
+ keyAccess.url = kasInfo.URL;
+ keyAccess.kid = kasInfo.KID;
+ keyAccess.protocol = kKasProtocol;
+ keyAccess.policyBinding = policyBinding;
+ keyAccess.encryptedMetadata = encryptedMetadata;
+ keyAccess.sid = splitID;
+ keyAccess.schemaVersion = KEY_ACCESS_SECHMA_VERSION;
+
+ if (tdfConfig.wrappingKeyType.isEc()) {
+ var ecKeyWrappedKeyInfo = createECWrappedKey(tdfConfig, kasInfo, symKey);
+ keyAccess.wrappedKey = ecKeyWrappedKeyInfo.wrappedKey;
+ keyAccess.ephemeralPublicKey = ecKeyWrappedKeyInfo.publicKey;
+ keyAccess.keyType = kECWrapped;
+ } else {
+ keyAccess.wrappedKey = createRSAWrappedKey(kasInfo, symKey);
+ keyAccess.keyType = kWrapped;
+ }
+ return keyAccess;
+ }
+
+ private ECKeyWrappedKeyInfo createECWrappedKey(Config.TDFConfig tdfConfig, Config.KASInfo kasInfo, byte[] symKey) {
+ var curveName = tdfConfig.wrappingKeyType.getCurveName();
+ var keyPair = new ECKeyPair(curveName, ECKeyPair.ECAlgorithm.ECDH);
+
+ ECPublicKey kasPubKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey);
+ byte[] symmetricKey = ECKeyPair.computeECDHKey(kasPubKey, keyPair.getPrivateKey());
+
+ var sessionKey = ECKeyPair.calculateHKDF(GLOBAL_KEY_SALT, symmetricKey);
+
+ AesGcm gcm = new AesGcm(sessionKey);
+ AesGcm.Encrypted wrappedKey = gcm.encrypt(symKey);
+
+ ECKeyWrappedKeyInfo wrappedKeyInfo = new ECKeyWrappedKeyInfo();
+ wrappedKeyInfo.publicKey = keyPair.publicKeyInPEMFormat();
+ wrappedKeyInfo.wrappedKey = Base64.getEncoder().encodeToString(wrappedKey.asBytes());
+ return wrappedKeyInfo;
+ }
+
+ private String createRSAWrappedKey(Config.KASInfo kasInfo, byte[] symKey) {
+ AsymEncryption asymEncrypt = new AsymEncryption(kasInfo.PublicKey);
+ byte[] wrappedKey = asymEncrypt.encrypt(symKey);
+ return Base64.getEncoder().encodeToString(wrappedKey);
+ }
}
+
private static final Base64.Decoder decoder = Base64.getDecoder();
public static class Reader {
@@ -419,8 +462,8 @@ private static byte[] calculateSignature(byte[] data, byte[] secret, Config.Inte
}
public TDFObject createTDF(InputStream payload,
- OutputStream outputStream,
- Config.TDFConfig tdfConfig, SDK.KAS kas, AttributesServiceFutureStub attrService)
+ OutputStream outputStream,
+ Config.TDFConfig tdfConfig, SDK.KAS kas, AttributesServiceFutureStub attrService)
throws IOException, JOSEException, AutoConfigureException, InterruptedException, ExecutionException, DecoderException {
if (tdfConfig.autoconfigure) {
@@ -441,7 +484,7 @@ public TDFObject createTDF(InputStream payload,
if (tdfConfig.splitPlan == null) {
throw new AutoConfigureException("Failed to generate Split Plan"); // Replace with appropriate error
- // handling
+ // handling
}
}
@@ -557,7 +600,7 @@ public TDFObject createTDF(InputStream payload,
}
tdfObject.manifest.assertions = signedAssertions;
- String manifestAsStr = Manifest.toJson(tdfObject.manifest);
+ String manifestAsStr = gson.toJson(tdfObject.manifest);
tdfWriter.appendManifest(manifestAsStr);
tdfObject.size = tdfWriter.finish();
@@ -584,7 +627,7 @@ public List defaultKases(TDFConfig config) {
public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas)
throws DecoderException, IOException, ParseException, NoSuchAlgorithmException, JOSEException {
- return loadTDF(tdf, kas, new Config.TDFReaderConfig());
+ return loadTDF(tdf, kas, Config.newTDFReaderConfig());
}
@@ -616,7 +659,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas,
}
knownSplits.add(ss.splitID);
try {
- unwrappedKey = kas.unwrap(keyAccess, manifest.encryptionInformation.policy);
+ unwrappedKey = kas.unwrap(keyAccess, manifest.encryptionInformation.policy, tdfReaderConfig.sessionKeyType);
} catch (Exception e) {
skippedSplits.put(ss, e);
continue;
diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/ECKeyPair.java b/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/ECKeyPair.java
index 72641e68..b7699c7d 100644
--- a/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/ECKeyPair.java
+++ b/sdk/src/main/java/io/opentdf/platform/sdk/nanotdf/ECKeyPair.java
@@ -1,5 +1,6 @@
package io.opentdf.platform.sdk.nanotdf;
+import io.opentdf.platform.sdk.KeyType;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
@@ -40,21 +41,27 @@ public enum ECAlgorithm {
private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
public enum NanoTDFECCurve {
- SECP256R1("secp256r1"),
- PRIME256V1("prime256v1"),
- SECP384R1("secp384r1"),
- SECP521R1("secp521r1");
+ SECP256R1("secp256r1", KeyType.EC256Key),
+ PRIME256V1("prime256v1", KeyType.EC256Key),
+ SECP384R1("secp384r1", KeyType.EC384Key),
+ SECP521R1("secp521r1", KeyType.EC521Key);
private String name;
+ private KeyType keyType;
- NanoTDFECCurve(String curveName) {
+ NanoTDFECCurve(String curveName, KeyType keyType) {
this.name = curveName;
+ this.keyType = keyType;
}
@Override
public String toString() {
return name;
}
+
+ public KeyType getKeyType() {
+ return keyType;
+ }
}
private KeyPair keyPair;
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/KASClientTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/KASClientTest.java
index f44bc564..496f4708 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/KASClientTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/KASClientTest.java
@@ -165,7 +165,7 @@ public void rewrap(RewrapRequest request, StreamObserver respons
var serverWrappedKey = new AsymEncryption(serverKeypair.getPublic()).encrypt(plaintextKey);
keyAccess.wrappedKey = Base64.getEncoder().encodeToString(serverWrappedKey);
- rewrapResponse = kas.unwrap(keyAccess, "the policy");
+ rewrapResponse = kas.unwrap(keyAccess, "the policy", KeyType.RSA2048Key);
}
assertThat(rewrapResponse).containsExactly(plaintextKey);
} finally {
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java
index cd4c056d..0c8203ae 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java
@@ -61,7 +61,7 @@ public KASInfo getECPublicKey(Config.KASInfo kasInfo, NanoTDFType.ECCurve curve)
}
@Override
- public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy) {
+ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessionKeyType) {
int index = Integer.parseInt(keyAccess.url);
var decryptor = new AsymDecryption(keypairs.get(index).getPrivate());
var bytes = Base64.getDecoder().decode(keyAccess.wrappedKey);
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/SDKBuilderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/SDKBuilderTest.java
index 0a85a181..1f1af97e 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/SDKBuilderTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/SDKBuilderTest.java
@@ -300,7 +300,7 @@ public ServerCall.Listener interceptCall(ServerCall tdfConfigPairs = List.of(
+ new TDFConfigPair(
+ Config.newTDFConfig(Config.withKasInformation(kasInfo)),
+ Config.newTDFReaderConfig()
+ ),
+ new TDFConfigPair(
+ Config.newTDFConfig(Config.withKasInformation(kasInfo), Config.WithWrappingKeyAlg(KeyType.EC256Key)),
+ Config.newTDFReaderConfig(Config.WithSessionKeyType(KeyType.EC256Key))
+ )
+ );
+
+ for (TDFConfigPair configPair : tdfConfigPairs) {
+ String plainText = "text";
+ InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes());
+ ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
- TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, sdk.kas(), sdk.attributes());
+ TDF tdf = new TDF();
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, configPair.tdfConfig, sdk.kas(), sdk.attributes());
- var unwrappedData = new java.io.ByteArrayOutputStream();
- var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.kas());
- reader.readPayload(unwrappedData);
+ var unwrappedData = new java.io.ByteArrayOutputStream();
+ var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.kas(), configPair.tdfReaderConfig);
+ reader.readPayload(unwrappedData);
- assertThat(unwrappedData.toString(StandardCharsets.UTF_8)).isEqualTo("text");
+ assertThat(unwrappedData.toString(StandardCharsets.UTF_8)).isEqualTo("text");
+ }
}
@Test @Disabled("this needs the backend services running to work")
diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
index 87090bf5..0c1ed084 100644
--- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
+++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java
@@ -3,8 +3,10 @@
import com.nimbusds.jose.JOSEException;
import io.opentdf.platform.sdk.Config.KASInfo;
import io.opentdf.platform.sdk.TDF.Reader;
+import io.opentdf.platform.sdk.nanotdf.ECKeyPair;
import io.opentdf.platform.sdk.nanotdf.NanoTDFType;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
+import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -18,10 +20,13 @@
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Base64;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
+import static io.opentdf.platform.sdk.TDF.GLOBAL_KEY_SALT;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -36,18 +41,33 @@ public Config.KASInfo getPublicKey(Config.KASInfo kasInfo) {
int index = Integer.parseInt(kasInfo.URL);
var kiCopy = new Config.KASInfo();
kiCopy.KID = "r1";
- kiCopy.PublicKey = CryptoUtils.getRSAPublicKeyPEM(keypairs.get(index).getPublic());
+ kiCopy.PublicKey = CryptoUtils.getPublicKeyPEM(keypairs.get(index).getPublic());
kiCopy.URL = kasInfo.URL;
return kiCopy;
}
@Override
- public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy) {
- int index = Integer.parseInt(keyAccess.url);
- var decryptor = new AsymDecryption(keypairs.get(index).getPrivate());
- var bytes = Base64.getDecoder().decode(keyAccess.wrappedKey);
+ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessionKeyType) {
+
try {
- return decryptor.decrypt(bytes);
+ int index = Integer.parseInt(keyAccess.url);
+ var bytes = Base64.getDecoder().decode(keyAccess.wrappedKey);
+ if (sessionKeyType.isEc()) {
+ var kasPrivateKey = CryptoUtils.getPrivateKeyPEM(keypairs.get(index).getPrivate());
+ var privateKey = ECKeyPair.privateKeyFromPem(kasPrivateKey);
+ var clientEphemeralPublicKey = keyAccess.ephemeralPublicKey;
+ var publicKey = ECKeyPair.publicKeyFromPem(clientEphemeralPublicKey);
+ byte[] symKey = ECKeyPair.computeECDHKey(publicKey, privateKey);
+
+ var sessionKey = ECKeyPair.calculateHKDF(GLOBAL_KEY_SALT, symKey);
+
+ AesGcm gcm = new AesGcm(sessionKey);
+ AesGcm.Encrypted encrypted = new AesGcm.Encrypted(bytes);
+ return gcm.decrypt(encrypted);
+ } else {
+ var decryptor = new AsymDecryption(keypairs.get(index).getPrivate());
+ return decryptor.decrypt(bytes);
+ }
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -73,18 +93,33 @@ public KASKeyCache getKeyCache() {
@BeforeAll
static void createKeypairs() {
- for (int i = 0; i < 1 + new Random().nextInt(5); i++) {
- keypairs.add(CryptoUtils.generateRSAKeypair());
+ for (int i = 0; i < 2 + new Random().nextInt(5); i++) {
+ if (i % 2 == 0) {
+ keypairs.add(CryptoUtils.generateRSAKeypair());
+ } else {
+ keypairs.add(CryptoUtils.generateECKeypair(KeyType.EC256Key.getCurveName()));
+ }
}
}
@Test
void testSimpleTDFEncryptAndDecrypt() throws Exception {
+
+ class TDFConfigPair {
+ public final Config.TDFConfig tdfConfig;
+ public final Config.TDFReaderConfig tdfReaderConfig;
+
+ public TDFConfigPair(Config.TDFConfig tdfConfig, Config.TDFReaderConfig tdfReaderConfig) {
+ this.tdfConfig = tdfConfig;
+ this.tdfReaderConfig = tdfReaderConfig;
+ }
+ }
+
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[32];
secureRandom.nextBytes(key);
- var assertion1 = new AssertionConfig();
+ var assertion1 = new AssertionConfig();
assertion1.id = "assertion1";
assertion1.type = AssertionConfig.Type.BaseAssertion;
assertion1.scope = AssertionConfig.Scope.TrustedDataObj;
@@ -95,43 +130,54 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception {
assertion1.statement.value = "ICAgIDxlZGoOkVkaD4=";
assertion1.signingKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key);
- Config.TDFConfig config = Config.newTDFConfig(
- Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
- Config.withMetaData("here is some metadata"),
- Config.withDataAttributes("https://example.org/attr/a/value/b", "https://example.org/attr/c/value/d"),
- Config.withAssertionConfig(assertion1));
-
- String plainText = "this is extremely sensitive stuff!!!";
- InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes());
- ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
-
- TDF tdf = new TDF();
- tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null);
-
var assertionVerificationKeys = new Config.AssertionVerificationKeys();
assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256,
key);
- var unwrappedData = new ByteArrayOutputStream();
- Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig(
- Config.withAssertionVerificationKeys(assertionVerificationKeys));
-
- var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
- readerConfig);
- assertThat(reader.getManifest().payload.mimeType).isEqualTo("application/octet-stream");
-
- reader.readPayload(unwrappedData);
-
- assertThat(unwrappedData.toString(StandardCharsets.UTF_8))
- .withFailMessage("extracted data does not match")
- .isEqualTo(plainText);
- assertThat(reader.getMetadata()).isEqualTo("here is some metadata");
-
- var policyObject = reader.readPolicyObject();
- assertThat(policyObject).isNotNull();
- assertThat(policyObject.body.dataAttributes.stream().map(a -> a.attribute).collect(Collectors.toList())).asList()
- .containsExactlyInAnyOrder("https://example.org/attr/a/value/b", "https://example.org/attr/c/value/d");
+ List tdfConfigPairs = List.of(
+ new TDFConfigPair(
+ Config.newTDFConfig( Config.withAutoconfigure(false), Config.withKasInformation(getRSAKASInfos()),
+ Config.withMetaData("here is some metadata"),
+ Config.withDataAttributes("https://example.org/attr/a/value/b", "https://example.org/attr/c/value/d"),
+ Config.withAssertionConfig(assertion1)),
+ Config.newTDFReaderConfig(Config.withAssertionVerificationKeys(assertionVerificationKeys))
+ ),
+ new TDFConfigPair(
+ Config.newTDFConfig( Config.withAutoconfigure(false), Config.withKasInformation(getECKASInfos()),
+ Config.withMetaData("here is some metadata"),
+ Config.WithWrappingKeyAlg(KeyType.EC256Key),
+ Config.withDataAttributes("https://example.org/attr/a/value/b", "https://example.org/attr/c/value/d"),
+ Config.withAssertionConfig(assertion1)),
+ Config.newTDFReaderConfig(Config.withAssertionVerificationKeys(assertionVerificationKeys),
+ Config.WithSessionKeyType(KeyType.EC256Key))
+ )
+ );
+
+ for (TDFConfigPair configPair : tdfConfigPairs) {
+ String plainText = "this is extremely sensitive stuff!!!";
+ InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes());
+ ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream();
+
+ TDF tdf = new TDF();
+ tdf.createTDF(plainTextInputStream, tdfOutputStream, configPair.tdfConfig, kas, null);
+
+ var unwrappedData = new ByteArrayOutputStream();
+ var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
+ configPair.tdfReaderConfig);
+ assertThat(reader.getManifest().payload.mimeType).isEqualTo("application/octet-stream");
+
+ reader.readPayload(unwrappedData);
+
+ assertThat(unwrappedData.toString(StandardCharsets.UTF_8))
+ .withFailMessage("extracted data does not match")
+ .isEqualTo(plainText);
+ assertThat(reader.getMetadata()).isEqualTo("here is some metadata");
+
+ var policyObject = reader.readPolicyObject();
+ assertThat(policyObject).isNotNull();
+ assertThat(policyObject.body.dataAttributes.stream().map(a -> a.attribute).collect(Collectors.toList())).asList()
+ .containsExactlyInAnyOrder("https://example.org/attr/a/value/b", "https://example.org/attr/c/value/d");
+ }
}
@Test
@@ -150,9 +196,12 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception {
assertionConfig.signingKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256,
keypair.getPrivate());
+ var rsaKasInfo = new Config.KASInfo();
+ rsaKasInfo.URL = Integer.toString(0);
+
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(rsaKasInfo),
Config.withAssertionConfig(assertionConfig));
String plainText = "this is extremely sensitive stuff!!!";
@@ -196,7 +245,7 @@ void testWithAssertionVerificationDisabled() throws Exception {
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(getRSAKASInfos()),
Config.withAssertionConfig(assertionConfig));
String plainText = "this is extremely sensitive stuff!!!";
@@ -213,7 +262,7 @@ void testWithAssertionVerificationDisabled() throws Exception {
var unwrappedData = new ByteArrayOutputStream();
assertThrows(JOSEException.class, () -> {
tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas,
- new Config.TDFReaderConfig());
+ Config.newTDFReaderConfig());
});
// try with assertion verification disabled and not passing the assertion verification keys
@@ -251,9 +300,12 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception {
assertionConfig2.statement.schema = "urn:nato:stanag:5636:A:1:elements:json";
assertionConfig2.statement.value = "{\"uuid\":\"f74efb60-4a9a-11ef-a6f1-8ee1a61c148a\",\"body\":{\"dataAttributes\":null,\"dissem\":null}}";
+ var rsaKasInfo = new Config.KASInfo();
+ rsaKasInfo.URL = Integer.toString(0);
+
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(rsaKasInfo),
Config.withAssertionConfig(assertionConfig1, assertionConfig2));
String plainText = "this is extremely sensitive stuff!!!";
@@ -265,7 +317,7 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception {
var unwrappedData = new ByteArrayOutputStream();
var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()),
- kas, new Config.TDFReaderConfig());
+ kas, Config.newTDFReaderConfig());
reader.readPayload(unwrappedData);
assertThat(unwrappedData.toString(StandardCharsets.UTF_8))
@@ -312,9 +364,12 @@ void testSimpleTDFWithAssertionWithHS256Failure() throws Exception {
assertionConfig1.statement.value = "ICAgIDxlZGoOkVkaD4=";
assertionConfig1.signingKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key);
+ var rsaKasInfo = new Config.KASInfo();
+ rsaKasInfo.URL = Integer.toString(0);
+
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(rsaKasInfo),
Config.withAssertionConfig(assertionConfig1));
String plainText = "this is extremely sensitive stuff!!!";
@@ -349,7 +404,7 @@ public void testCreatingTDFWithMultipleSegments() throws Exception {
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(getRSAKASInfos()),
Config.withSegmentSize(Config.MIN_SEGMENT_SIZE));
// data should be large enough to have multiple complete and a partial segment
@@ -407,7 +462,7 @@ public void write(byte[] b, int off, int len) {
var tdf = new TDF(maxSize);
var tdfConfig = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(getRSAKASInfos()),
Config.withSegmentSize(Config.MIN_SEGMENT_SIZE));
assertThrows(TDF.DataSizeNotSupported.class,
() -> tdf.createTDF(is, os, tdfConfig, kas, null),
@@ -423,7 +478,7 @@ public void testCreateTDFWithMimeType() throws Exception {
Config.TDFConfig config = Config.newTDFConfig(
Config.withAutoconfigure(false),
- Config.withKasInformation(getKASInfos()),
+ Config.withKasInformation(getRSAKASInfos()),
Config.withMimeType(mimeType));
String plainText = "this is extremely sensitive stuff!!!";
@@ -438,14 +493,26 @@ public void testCreateTDFWithMimeType() throws Exception {
}
@Nonnull
- private static Config.KASInfo[] getKASInfos() {
- var kasInfos = new ArrayList<>();
+ private static Config.KASInfo[] getKASInfos(Predicate filter) {
+ var kasInfos = new ArrayList();
for (int i = 0; i < keypairs.size(); i++) {
- var kasInfo = new Config.KASInfo();
- kasInfo.URL = Integer.toString(i);
- kasInfo.PublicKey = null;
- kasInfos.add(kasInfo);
+ if (filter.test(i)) {
+ var kasInfo = new Config.KASInfo();
+ kasInfo.URL = Integer.toString(i);
+ kasInfo.PublicKey = null;
+ kasInfos.add(kasInfo);
+ }
}
return kasInfos.toArray(Config.KASInfo[]::new);
}
+
+ @Nonnull
+ private static Config.KASInfo[] getRSAKASInfos() {
+ return getKASInfos(i -> i % 2 == 0);
+ }
+
+ @Nonnull
+ private static Config.KASInfo[] getECKASInfos() {
+ return getKASInfos(i -> i % 2 != 0);
+ }
}