Initial commit (history reset)
This commit is contained in:
13
samples/.classpath
Normal file
13
samples/.classpath
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
23
samples/.project
Normal file
23
samples/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>samples</name>
|
||||
<comment>Project samples created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
31
samples/LICENSE
Normal file
31
samples/LICENSE
Normal file
@@ -0,0 +1,31 @@
|
||||
Copyright (C) 2025, Leo Galambos
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this software must
|
||||
display the following acknowledgement:
|
||||
This product includes software developed by the Egothor project.
|
||||
|
||||
4. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
45
samples/build.gradle
Normal file
45
samples/build.gradle
Normal file
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(project(":lib"))
|
||||
testImplementation 'org.egothor:conflux:[1.0,2.0)'
|
||||
testImplementation("org.bouncycastle:bcpkix-jdk18on:1.81")
|
||||
testImplementation(platform("org.junit:junit-bom:5.10.2"))
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
repositories {
|
||||
// for conflux lib
|
||||
maven {
|
||||
name = "GiteaMaven"
|
||||
url = uri("https://gitea.egothor.org/api/packages/Egothor/maven")
|
||||
}
|
||||
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// Compile, but don't produce any artifacts
|
||||
tasks.named("jar").configure { enabled = false }
|
||||
tasks.named("javadoc").configure { enabled = false }
|
||||
tasks.named("assemble").configure { enabled = false }
|
||||
|
||||
// Make these tests opt-in so they don’t run in normal CI builds
|
||||
tasks.named("test", Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags("sample") // only run tests that are explicitly tagged as samples
|
||||
}
|
||||
// Only run if -PrunSamples is supplied
|
||||
onlyIf {
|
||||
project.hasProperty("runSamples")
|
||||
}
|
||||
|
||||
// Don't fail the build if zero tests match the tag
|
||||
filter {
|
||||
failOnNoMatchingTests = false
|
||||
}
|
||||
}
|
||||
259
samples/src/test/java/demo/AesTest.java
Normal file
259
samples/src/test/java/demo/AesTest.java
Normal file
@@ -0,0 +1,259 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2025, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package demo;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import conflux.Ctx;
|
||||
import conflux.CtxInterface;
|
||||
import zeroecho.core.ConfluxKeys;
|
||||
import zeroecho.core.CryptoAlgorithm;
|
||||
import zeroecho.core.CryptoAlgorithms;
|
||||
import zeroecho.core.KeyUsage;
|
||||
import zeroecho.core.alg.aes.AesKeyGenSpec;
|
||||
import zeroecho.core.alg.aes.AesSpec;
|
||||
import zeroecho.core.context.EncryptionContext;
|
||||
import zeroecho.core.spi.ContextAware;
|
||||
import zeroecho.core.util.Strings;
|
||||
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
|
||||
import zeroecho.sdk.builders.core.DataContentChainBuilder;
|
||||
import zeroecho.sdk.builders.core.PlainBytesBuilder;
|
||||
import zeroecho.sdk.content.api.DataContent;
|
||||
|
||||
class AesTest {
|
||||
private static final Logger LOG = Logger.getLogger(AesTest.class.getName());
|
||||
|
||||
SecretKey generateAesKey() throws GeneralSecurityException {
|
||||
// Locate the AES algorithm in the catalog
|
||||
CryptoAlgorithm aes = CryptoAlgorithms.require("AES");
|
||||
SecretKey key = aes
|
||||
// Retrieve the builder that works with AesKeyGenSpec - the specification for
|
||||
// AES key generation
|
||||
.symmetricKeyBuilder(AesKeyGenSpec.class)
|
||||
// Generate a secret key according to the AES256 specification
|
||||
.generateSecret(AesKeyGenSpec.aes256());
|
||||
// Log the generated key (truncated to short hex for readability)
|
||||
LOG.log(Level.INFO, "AES256 key generated: {0}", Strings.toShortHexString(key.getEncoded()));
|
||||
|
||||
// or just: CryptoAlgorithms.generateSecret("AES", AesKeyGenSpec.aes256())
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@Test
|
||||
void aesEncryptCoreLevelAPI() throws GeneralSecurityException, IOException {
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
// AES-GCM specification with a 128-bit authentication tag
|
||||
// The null parameter indicates that no header codec is used (more on this
|
||||
// below)
|
||||
AesSpec spec = AesSpec.gcm128(null);
|
||||
// Encryption will generate a random IV for us. Normally it would be saved
|
||||
// through the header codec, but since we do not use it here, the IV and other
|
||||
// values are stored in a "context" instead. The "context" is a key-value
|
||||
// in-memory store; we create a private context with a unique name for this
|
||||
// session.
|
||||
CtxInterface session = Ctx.INSTANCE.getContext("aes-ctx-" + System.nanoTime());
|
||||
|
||||
SecretKey key = generateAesKey();
|
||||
byte[] encrypted;
|
||||
// Request an encryption context using the key and AES-GCM-128 specification
|
||||
try (EncryptionContext enc = CryptoAlgorithms.create("AES", KeyUsage.ENCRYPT, key, spec)) {
|
||||
// This context implements ContextAware, allowing us to associate our session
|
||||
((ContextAware) enc).setContext(session);
|
||||
// Get an encrypted stream that processes the plaintext on-the-fly
|
||||
try (InputStream encryptedStream = enc.attach(new ByteArrayInputStream(msg))) {
|
||||
// In this sample we consume the encrypted stream fully into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "Core: AES256/GCM128 IV={0} AAD={1} encrypted={2}", new Object[] {
|
||||
// The IV was generated automatically (we could have provided one, but
|
||||
// if omitted, it is created for us)
|
||||
Strings.toShortHexString(session.get(ConfluxKeys.iv("AES"))),
|
||||
// AAD is an additional parameter for GCM; again, automatically generated
|
||||
// because we did not provide it
|
||||
Strings.toShortHexString(session.get(ConfluxKeys.aad("AES"))),
|
||||
// The final ciphertext
|
||||
Strings.toShortHexString(encrypted) });
|
||||
}
|
||||
|
||||
@Test
|
||||
void aesEncryptSdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
SecretKey key = generateAesKey();
|
||||
AesSpec spec = AesSpec.gcm128(null);
|
||||
|
||||
// Separate context again to hold IV and AAD values so we can inspect them later
|
||||
CtxInterface session = Ctx.INSTANCE.getContext("aes-ctx-" + System.nanoTime());
|
||||
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder()
|
||||
// Use the generated AES key
|
||||
.importKeyRaw(key.getEncoded())
|
||||
// Specify AES-GCM-128 mode
|
||||
.spec(spec)
|
||||
// Provide the context to capture IV and AAD
|
||||
.context(session);
|
||||
|
||||
DataContent dccb = DataContentChainBuilder
|
||||
// Build an encryption pipeline
|
||||
.encrypt()
|
||||
// Input: the sample byte array
|
||||
.add(PlainBytesBuilder.builder().bytes(msg))
|
||||
// Process through AES encryption
|
||||
.add(aesBuilder)
|
||||
// Finalize the pipeline
|
||||
.build();
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "SDK: AES256/GCM128 IV={0} AAD={1} encrypted={2}", new Object[] {
|
||||
// IV generated for us (we could provide one; if omitted, a random IV is
|
||||
// created)
|
||||
Strings.toShortHexString(session.get(ConfluxKeys.iv("AES"))),
|
||||
// AAD generated automatically (not explicitly provided)
|
||||
Strings.toShortHexString(session.get(ConfluxKeys.aad("AES"))),
|
||||
// Final ciphertext
|
||||
Strings.toShortHexString(encrypted) });
|
||||
}
|
||||
|
||||
@Test
|
||||
void aesEncryptSmarterSdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder()
|
||||
// Automatically generate a 256-bit AES key
|
||||
.generateKey(256)
|
||||
// Store ad-hoc generated parameters (IV, AAD) in the stream header
|
||||
.withHeader()
|
||||
// Use AES-GCM with a 128-bit authentication tag
|
||||
.modeGcm(128);
|
||||
|
||||
// The builder stores all generated attributes inside the stream header
|
||||
DataContent dccb = DataContentChainBuilder
|
||||
// Build an encryption pipeline
|
||||
.encrypt()
|
||||
// Input: the sample byte array
|
||||
.add(PlainBytesBuilder.builder().bytes(msg))
|
||||
// Process through AES encryption
|
||||
.add(aesBuilder)
|
||||
// Finalize the pipeline
|
||||
.build();
|
||||
|
||||
LOG.log(Level.INFO, "SDK-smart: AES256 key generated {0}",
|
||||
// The key is generated within dccb's `build` method, not earlier
|
||||
Strings.toShortHexString(aesBuilder.generatedKey().getEncoded()));
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "SDK-smart: AES256/GCM128 IV=builtin AAD=builtin encrypted={0}",
|
||||
// The ciphertext, with IV and AAD already embedded in the header
|
||||
Strings.toShortHexString(encrypted));
|
||||
}
|
||||
|
||||
@Test
|
||||
void aesRoundSmarterSdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
|
||||
|
||||
// The builder stores generated IV and AAD inside the stream header
|
||||
DataContent dccb = DataContentChainBuilder.encrypt().add(PlainBytesBuilder.builder().bytes(msg)).add(aesBuilder)
|
||||
.build();
|
||||
|
||||
SecretKey key = aesBuilder.generatedKey();
|
||||
LOG.log(Level.INFO, "SDK-smart: AES256 key generated {0}", Strings.toShortHexString(key.getEncoded()));
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
dccb = DataContentChainBuilder.decrypt().add(PlainBytesBuilder.builder().bytes(encrypted))
|
||||
// Use the same AES key for decryption; IV and AAD are restored from the header
|
||||
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
|
||||
// Build the pipeline
|
||||
.build();
|
||||
byte[] decrypted;
|
||||
try (InputStream decryptedStream = dccb.getStream()) {
|
||||
// Consume the decrypted data into memory
|
||||
decrypted = readAll(decryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "original message={0} after AES roundtrip={1}",
|
||||
new Object[] { Strings.toShortHexString(msg), Strings.toShortHexString(decrypted) });
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
private static byte[] randomBytes(int len) {
|
||||
byte[] data = new byte[len];
|
||||
new SecureRandom().nextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] readAll(InputStream in) throws IOException {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
in.transferTo(out);
|
||||
out.flush();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
194
samples/src/test/java/demo/CombinedDeliveryTest.java
Normal file
194
samples/src/test/java/demo/CombinedDeliveryTest.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2025, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package demo;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
||||
import zeroecho.core.CryptoAlgorithms;
|
||||
import zeroecho.core.KeyUsage;
|
||||
import zeroecho.core.alg.elgamal.ElgamalParamSpec;
|
||||
import zeroecho.core.alg.kyber.KyberKeyGenSpec;
|
||||
import zeroecho.core.alg.rsa.RsaKeyGenSpec;
|
||||
import zeroecho.core.util.Strings;
|
||||
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
|
||||
import zeroecho.sdk.builders.core.DataContentChainBuilder;
|
||||
import zeroecho.sdk.builders.core.PlainBytesBuilder;
|
||||
import zeroecho.sdk.content.api.DataContent;
|
||||
import zeroecho.sdk.guard.MultiRecipientDataSourceBuilder;
|
||||
import zeroecho.sdk.guard.UnlockMaterial;
|
||||
import zeroecho.sdk.util.BouncyCastleActivator;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class CombinedDeliveryTest {
|
||||
private static final Logger LOG = Logger.getLogger(CombinedDeliveryTest.class.getName());
|
||||
|
||||
@BeforeAll
|
||||
void setupProviders() {
|
||||
BouncyCastleActivator.init();
|
||||
}
|
||||
|
||||
KeyPair generateKyberKeys() throws GeneralSecurityException {
|
||||
KeyPair kp = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
|
||||
|
||||
LOG.log(Level.INFO, "ML-KEM key public={0} private={1}",
|
||||
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
|
||||
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
KeyPair generateRsaKeys() throws GeneralSecurityException {
|
||||
KeyPair kp = CryptoAlgorithms.generateKeyPair("RSA", RsaKeyGenSpec.rsa4096());
|
||||
|
||||
LOG.log(Level.INFO, "RSA key public={0} private={1}",
|
||||
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
|
||||
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
KeyPair generateElGamalKeys() throws GeneralSecurityException {
|
||||
KeyPair kp = CryptoAlgorithms.generateKeyPair("ElGamal", ElgamalParamSpec.ffdhe2048());
|
||||
|
||||
LOG.log(Level.INFO, "ElGamal key public={0} private={1}",
|
||||
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
|
||||
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
@Test
|
||||
void combinedSdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
KeyPair kem = generateKyberKeys();
|
||||
KeyPair rsa = generateRsaKeys();
|
||||
KeyPair elg = generateElGamalKeys();
|
||||
char[] password = "p@ssw07d".toCharArray();
|
||||
|
||||
KeyPair decoy1rsa = generateRsaKeys();
|
||||
KeyPair decoy2rsa = generateRsaKeys();
|
||||
|
||||
AesDataContentBuilder payload = AesDataContentBuilder.builder().modeCbcPkcs5().withHeader();
|
||||
|
||||
MultiRecipientDataSourceBuilder multi = MultiRecipientDataSourceBuilder.builder()
|
||||
// AES-256 - 32 bytes of key material
|
||||
.payloadKeyBytes(32).withAes(payload)
|
||||
// add recipients - the context initialized with the public key
|
||||
.addRecipient(CryptoAlgorithms.create("ElGamal", KeyUsage.ENCRYPT, elg.getPublic()))
|
||||
.addRecipient(CryptoAlgorithms.create("RSA", KeyUsage.ENCRYPT, rsa.getPublic()))
|
||||
// ML-KEM PostQuantum uses AES for the inner payload
|
||||
.addRecipient(CryptoAlgorithms.create("ML-KEM", KeyUsage.ENCAPSULATE, kem.getPublic()),
|
||||
/* AES256 key size */
|
||||
32,
|
||||
/* salt size in hkdf */
|
||||
32)
|
||||
// Password (via KDF)
|
||||
.addPasswordRecipient(password, /* iterations */ 200_000, /* saltLen */ 16, /* kekBytes */ 32)
|
||||
// and some decoys
|
||||
.addRecipient(CryptoAlgorithms.create("RSA", KeyUsage.ENCRYPT, decoy1rsa.getPublic()))
|
||||
.addRecipient(CryptoAlgorithms.create("RSA", KeyUsage.ENCRYPT, decoy2rsa.getPublic()));
|
||||
|
||||
// shuffle all the recipients
|
||||
multi.shuffle();
|
||||
|
||||
DataContent dccb = DataContentChainBuilder.encrypt().add(PlainBytesBuilder.builder().bytes(msg))
|
||||
// encrypt for multi recipients of various types
|
||||
.add(multi).build();
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
recipientProcessing("ElGamal-ffdhe2048", msg, encrypted, new UnlockMaterial.Private(elg.getPrivate()));
|
||||
recipientProcessing("RSA4096", msg, encrypted, new UnlockMaterial.Private(rsa.getPrivate()));
|
||||
recipientProcessing("Kyber-1024", msg, encrypted, new UnlockMaterial.Private(kem.getPrivate()));
|
||||
recipientProcessing("Password", msg, encrypted, new UnlockMaterial.Password(password));
|
||||
}
|
||||
|
||||
private void recipientProcessing(String method, byte[] msg, byte[] encrypted, UnlockMaterial unlock)
|
||||
throws IOException {
|
||||
AesDataContentBuilder payload = AesDataContentBuilder.builder().modeCbcPkcs5().withHeader();
|
||||
MultiRecipientDataSourceBuilder multi = MultiRecipientDataSourceBuilder.builder()
|
||||
// define our payload
|
||||
.payloadKeyBytes(32).withAes(payload)
|
||||
// one recipient
|
||||
.unlockWith(unlock);
|
||||
|
||||
// Decryption
|
||||
DataContent dccb = DataContentChainBuilder.decrypt().add(PlainBytesBuilder.builder().bytes(encrypted))
|
||||
// decrypt via multi
|
||||
.add(multi).build();
|
||||
|
||||
byte[] decrypted;
|
||||
try (InputStream decryptedStream = dccb.getStream()) {
|
||||
// Consume the decrypted data into memory
|
||||
decrypted = readAll(decryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "original message={0} after {2}/AES256/CBC/PKCS5 roundtrip={1}",
|
||||
new Object[] { Strings.toShortHexString(msg), Strings.toShortHexString(decrypted), method });
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
private static byte[] randomBytes(int len) {
|
||||
byte[] data = new byte[len];
|
||||
new SecureRandom().nextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] readAll(InputStream in) throws IOException {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
in.transferTo(out);
|
||||
out.flush();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
131
samples/src/test/java/demo/PostQuantumTest.java
Normal file
131
samples/src/test/java/demo/PostQuantumTest.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2025, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package demo;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
||||
import zeroecho.core.CryptoAlgorithms;
|
||||
import zeroecho.core.alg.kyber.KyberKeyGenSpec;
|
||||
import zeroecho.core.util.Strings;
|
||||
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
|
||||
import zeroecho.sdk.builders.alg.KemDataContentBuilder;
|
||||
import zeroecho.sdk.builders.core.DataContentChainBuilder;
|
||||
import zeroecho.sdk.builders.core.PlainBytesBuilder;
|
||||
import zeroecho.sdk.content.api.DataContent;
|
||||
import zeroecho.sdk.util.BouncyCastleActivator;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class PostQuantumTest {
|
||||
private static final Logger LOG = Logger.getLogger(PostQuantumTest.class.getName());
|
||||
|
||||
@BeforeAll
|
||||
void setupProviders() {
|
||||
BouncyCastleActivator.init();
|
||||
}
|
||||
|
||||
KeyPair generateKyberKeys() throws GeneralSecurityException {
|
||||
KeyPair kp = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
|
||||
|
||||
LOG.log(Level.INFO, "ML-KEM key public={0} private={1}",
|
||||
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
|
||||
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
@Test
|
||||
void pqcSdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
KeyPair kp = generateKyberKeys();
|
||||
|
||||
DataContent dccb = DataContentChainBuilder.encrypt().add(PlainBytesBuilder.builder().bytes(msg))
|
||||
.add(KemDataContentBuilder.builder().kem("ML-KEM").recipientPublic(kp.getPublic())
|
||||
.hkdfSha256("KEM-demo".getBytes(StandardCharsets.US_ASCII))
|
||||
.withAes(AesDataContentBuilder.builder().modeGcm(128).withHeader()))
|
||||
.build();
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
// Decryption
|
||||
dccb = DataContentChainBuilder.decrypt().add(PlainBytesBuilder.builder().bytes(encrypted))
|
||||
.add(KemDataContentBuilder.builder().kem("ML-KEM").recipientPrivate(kp.getPrivate())
|
||||
.hkdfSha256("KEM-demo".getBytes(StandardCharsets.US_ASCII))
|
||||
.withAes(AesDataContentBuilder.builder().modeGcm(128).withHeader()))
|
||||
.build();
|
||||
|
||||
byte[] decrypted;
|
||||
try (InputStream decryptedStream = dccb.getStream()) {
|
||||
// Consume the decrypted data into memory
|
||||
decrypted = readAll(decryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "original message={0} after ML-KEM(Kyber)+AES/GCM128 roundtrip={1}",
|
||||
new Object[] { Strings.toShortHexString(msg), Strings.toShortHexString(decrypted) });
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
private static byte[] randomBytes(int len) {
|
||||
byte[] data = new byte[len];
|
||||
new SecureRandom().nextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] readAll(InputStream in) throws IOException {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
in.transferTo(out);
|
||||
out.flush();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
212
samples/src/test/java/demo/SigningAesTest.java
Normal file
212
samples/src/test/java/demo/SigningAesTest.java
Normal file
@@ -0,0 +1,212 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (C) 2025, Leo Galambos
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software must
|
||||
* display the following acknowledgement:
|
||||
* This product includes software developed by the Egothor project.
|
||||
*
|
||||
* 4. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
******************************************************************************/
|
||||
package demo;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import zeroecho.core.CryptoAlgorithm;
|
||||
import zeroecho.core.CryptoAlgorithms;
|
||||
import zeroecho.core.alg.aes.AesKeyGenSpec;
|
||||
import zeroecho.core.alg.rsa.RsaKeyGenSpec;
|
||||
import zeroecho.core.alg.rsa.RsaSigSpec;
|
||||
import zeroecho.core.tag.TagEngine;
|
||||
import zeroecho.core.tag.TagEngineBuilder;
|
||||
import zeroecho.core.util.Strings;
|
||||
import zeroecho.sdk.builders.TagTrailerDataContentBuilder;
|
||||
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
|
||||
import zeroecho.sdk.builders.core.DataContentChainBuilder;
|
||||
import zeroecho.sdk.builders.core.PlainBytesBuilder;
|
||||
import zeroecho.sdk.content.api.DataContent;
|
||||
|
||||
class SigningAesTest {
|
||||
private static final Logger LOG = Logger.getLogger(SigningAesTest.class.getName());
|
||||
|
||||
SecretKey generateAesKey() throws GeneralSecurityException {
|
||||
// Locate the AES algorithm in the catalog
|
||||
CryptoAlgorithm aes = CryptoAlgorithms.require("AES");
|
||||
SecretKey key = aes
|
||||
// Retrieve the builder that works with AesKeyGenSpec - the specification for
|
||||
// AES key generation
|
||||
.symmetricKeyBuilder(AesKeyGenSpec.class)
|
||||
// Generate a secret key according to the AES256 specification
|
||||
.generateSecret(AesKeyGenSpec.aes256());
|
||||
// Log the generated key (truncated to short hex for readability)
|
||||
LOG.log(Level.INFO, "AES256 key generated: {0}", Strings.toShortHexString(key.getEncoded()));
|
||||
|
||||
// or just: CryptoAlgorithms.generateSecret("AES", AesKeyGenSpec.aes256())
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
KeyPair generateRsaKeys() throws GeneralSecurityException {
|
||||
KeyPair kp = CryptoAlgorithms.generateKeyPair("RSA", RsaKeyGenSpec.rsa4096());
|
||||
|
||||
LOG.log(Level.INFO, "RSA key public={0} private={1}",
|
||||
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
|
||||
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
@Test
|
||||
void aesRoundStESdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
LOG.info("aesRoundSmarterSdkLevelAPI - Sign then Encrypt");
|
||||
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
|
||||
|
||||
// RSA-2048 keys (use registry for convenience)
|
||||
KeyPair rsa = generateRsaKeys();
|
||||
|
||||
// Tag engines (SHA-256, saltLen=32)
|
||||
RsaSigSpec pss = RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32);
|
||||
TagEngine<Signature> tagEnc = TagEngineBuilder.rsaSign(rsa.getPrivate(), pss).get();
|
||||
TagEngine<Signature> tagDec = TagEngineBuilder.rsaVerify(rsa.getPublic(), pss).get();
|
||||
|
||||
// The builder stores generated IV and AAD inside the stream header
|
||||
DataContent dccb = DataContentChainBuilder.encrypt().add(PlainBytesBuilder.builder().bytes(msg))
|
||||
// sign the data
|
||||
.add(new TagTrailerDataContentBuilder<>(tagEnc).bufferSize(8192))
|
||||
// and then encrypt
|
||||
.add(aesBuilder).build();
|
||||
|
||||
SecretKey key = aesBuilder.generatedKey();
|
||||
LOG.log(Level.INFO, "SDK-smart: AES256 key generated {0}", Strings.toShortHexString(key.getEncoded()));
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
dccb = DataContentChainBuilder.decrypt().add(PlainBytesBuilder.builder().bytes(encrypted))
|
||||
// Use the same AES key for decryption; IV and AAD are restored from the header
|
||||
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
|
||||
// the decrypted stream must be verified
|
||||
.add(new TagTrailerDataContentBuilder<>(tagDec).bufferSize(8192).throwOnMismatch())
|
||||
// Build the pipeline
|
||||
.build();
|
||||
byte[] decrypted;
|
||||
try (InputStream decryptedStream = dccb.getStream()) {
|
||||
// Consume the decrypted data into memory
|
||||
decrypted = readAll(decryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "original message={0} after AES roundtrip={1}",
|
||||
new Object[] { Strings.toShortHexString(msg), Strings.toShortHexString(decrypted) });
|
||||
}
|
||||
|
||||
@Test
|
||||
void aesRoundEtSSdkLevelAPI() throws GeneralSecurityException, IOException {
|
||||
LOG.info("aesRoundSmarterSdkLevelAPI - Encrypt then Sign");
|
||||
|
||||
// Sample message to encrypt
|
||||
byte[] msg = randomBytes(100);
|
||||
|
||||
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
|
||||
|
||||
// RSA-2048 keys (use registry for convenience)
|
||||
KeyPair rsa = generateRsaKeys();
|
||||
|
||||
// Tag engines (SHA-256, saltLen=32)
|
||||
RsaSigSpec pss = RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32);
|
||||
TagEngine<Signature> tagEnc = TagEngineBuilder.rsaSign(rsa.getPrivate(), pss).get();
|
||||
TagEngine<Signature> tagDec = TagEngineBuilder.rsaVerify(rsa.getPublic(), pss).get();
|
||||
|
||||
// The builder stores generated IV and AAD inside the stream header
|
||||
DataContent dccb = DataContentChainBuilder.encrypt().add(PlainBytesBuilder.builder().bytes(msg))
|
||||
// encrypt
|
||||
.add(aesBuilder)
|
||||
// and then sign
|
||||
.add(new TagTrailerDataContentBuilder<>(tagEnc).bufferSize(8192))
|
||||
//
|
||||
.build();
|
||||
|
||||
SecretKey key = aesBuilder.generatedKey();
|
||||
LOG.log(Level.INFO, "SDK-smart: AES256 key generated {0}", Strings.toShortHexString(key.getEncoded()));
|
||||
|
||||
byte[] encrypted;
|
||||
try (InputStream encryptedStream = dccb.getStream()) {
|
||||
// Consume the encrypted data into memory
|
||||
encrypted = readAll(encryptedStream);
|
||||
}
|
||||
|
||||
dccb = DataContentChainBuilder.decrypt().add(PlainBytesBuilder.builder().bytes(encrypted))
|
||||
// the stream must be verified, but encryption still runs as data flows through
|
||||
.add(new TagTrailerDataContentBuilder<>(tagDec).bufferSize(8192).throwOnMismatch())
|
||||
// Use the same AES key for decryption; IV and AAD are restored from the header
|
||||
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
|
||||
// Build the pipeline
|
||||
.build();
|
||||
byte[] decrypted;
|
||||
try (InputStream decryptedStream = dccb.getStream()) {
|
||||
// Consume the decrypted data into memory
|
||||
decrypted = readAll(decryptedStream);
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, "original message={0} after AES roundtrip={1}",
|
||||
new Object[] { Strings.toShortHexString(msg), Strings.toShortHexString(decrypted) });
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
private static byte[] randomBytes(int len) {
|
||||
byte[] data = new byte[len];
|
||||
new SecureRandom().nextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] readAll(InputStream in) throws IOException {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
in.transferTo(out);
|
||||
out.flush();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user