Fix SecureBuffer data access

This commit is contained in:
Oracle 2026-04-30 14:17:44 +02:00
parent 7c6642085f
commit 4a1e3c915d
Signed by: Oracle
SSH key fingerprint: SHA256:x4/RtnjUyuHkdvmwNDsWSfcfF1V5PNr3OpriZqOvCX8
4 changed files with 34 additions and 27 deletions

View file

@ -17,6 +17,7 @@ import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@ -345,7 +346,7 @@ public class SecureCompletionClient {
Cipher cipher;
try {
cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey.getEncoded(), "AES"), new GCMParameterSpec(Constants.GCM_TAG_SIZE * Byte.SIZE, secureNonce.getData().asByteBuffer().array()));
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey.getEncoded(), "AES"), new GCMParameterSpec(Constants.GCM_TAG_SIZE * Byte.SIZE, secureNonce.getAsByteArray()));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException |
InvalidKeyException e) {
throw new RuntimeException(new SecurityError("AES-GCM cipher initialization failed: " + e.getMessage(), e));
@ -354,7 +355,7 @@ public class SecureCompletionClient {
byte[] ciphertext;
try {
ciphertext = cipher.doFinal(securePayload.getData().asByteBuffer().array());
ciphertext = cipher.doFinal(securePayload.getAsByteArray());
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException(new SecurityError("AES-GCM encryption failed: " + e.getMessage(), e));
}
@ -399,7 +400,7 @@ public class SecureCompletionClient {
byte[] encryptedAESKey;
try (SecureBuffer secureAesKeyEncoded = SecureMemory.secureByteArray(aesKey.getEncoded())) {
encryptedAESKey = rsa.doFinal(secureAesKeyEncoded.getData().asByteBuffer().array());
encryptedAESKey = rsa.doFinal(secureAesKeyEncoded.getAsByteArray());
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException(new SecurityError("RSA-OAEP key wrapping failed: " + e.getMessage(), e));
}
@ -777,10 +778,10 @@ public class SecureCompletionClient {
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.DECRYPT_MODE, this.privateKey, oaepParams);
byte[] aesKeyBytes = rsaCipher.doFinal(secureEncryptedAESKey.getData().asByteBuffer().array());
byte[] aesKeyBytes = rsaCipher.doFinal(secureEncryptedAESKey.getAsByteArray());
try (SecureBuffer secureAesKeyBytes = SecureMemory.secureByteArray(aesKeyBytes)) {
SecretKeySpec aesKey = new SecretKeySpec(secureAesKeyBytes.getData().asByteBuffer().array(), "AES");
SecretKeySpec aesKey = new SecretKeySpec(secureAesKeyBytes.getAsByteArray(), "AES");
// Decrypt payload
byte[] ciphertext = Base64.getDecoder().decode(encryptedPayload.get("ciphertext").getAsString());
@ -790,19 +791,20 @@ public class SecureCompletionClient {
try (SecureBuffer secureCiphertext = SecureMemory.secureByteArray(ciphertext); SecureBuffer secureNonce = SecureMemory.secureByteArray(nonce); SecureBuffer secureTag = SecureMemory.secureByteArray(tag)) {
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
aesCipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(Constants.GCM_TAG_SIZE * 8, secureNonce.getData().asByteBuffer().array()));
aesCipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(Constants.GCM_TAG_SIZE * 8, secureNonce.getAsByteArray()));
// Combine ciphertext (without tag) and tag for decryption using SecureBuffer
try (SecureBuffer secureCiphertextWithTag = SecureMemory.secureByteArray(new byte[ciphertext.length + tag.length])) {
secureCiphertextWithTag.getData().asByteBuffer().put(secureCiphertext.getData().asByteBuffer().array());
secureCiphertextWithTag.getData().asByteBuffer().put(secureTag.getData().asByteBuffer().array());
// Combine ciphertext (without tag) and tag for decryption using SecureBuffer
try (SecureBuffer secureCiphertextWithTag = SecureMemory.secureByteArray(new byte[ciphertext.length + tag.length])) {
ByteBuffer combinedBuf = secureCiphertextWithTag.getData().asByteBuffer();
combinedBuf.put(secureCiphertext.getAsByteArray());
combinedBuf.put(secureTag.getAsByteArray());
byte[] plaintextBytes = aesCipher.doFinal(secureCiphertextWithTag.getData().asByteBuffer().array());
byte[] plaintextBytes = aesCipher.doFinal(secureCiphertextWithTag.getAsByteArray());
// Parse JSON response
Map<String, Object> response;
try (SecureBuffer securePlaintext = SecureMemory.secureByteArray(plaintextBytes)) {
Object parsed = gson.fromJson(new String(securePlaintext.getData().asByteBuffer().array(), StandardCharsets.UTF_8), Object.class);
Object parsed = gson.fromJson(new String(securePlaintext.getAsByteArray(), StandardCharsets.UTF_8), Object.class);
@SuppressWarnings("unchecked") Map<String, Object> resultMap = (Map<String, Object>) parsed;
response = resultMap != null ? resultMap : new HashMap<>();
}

View file

@ -5,7 +5,6 @@ import lombok.Setter;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Map;
@ -145,7 +144,7 @@ public final class SecureMemory {
* @param lock whether to attempt memory locking
*/
public SecureBuffer(byte[] data, boolean lock) {
this.arena = Arena.ofConfined();
this.arena = Arena.ofShared();
this.data = data != null ? this.arena.allocate(data.length) : MemorySegment.NULL;
if (data != null) {
@ -164,6 +163,10 @@ public final class SecureMemory {
}
}
public byte[] getAsByteArray() {
return this.data.toArray(ValueLayout.JAVA_BYTE);
}
/**
* Locks buffer in memory (prevents disk swapping). Returns false if unavailable.
*/
@ -174,7 +177,7 @@ public final class SecureMemory {
try {
long result = (long) MLOCK_HANDLE.invokeExact(
MemorySegment.ofAddress(this.address),
this.address,
this.size);
this.locked = result == 0;
} catch (Throwable t) {
@ -194,7 +197,7 @@ public final class SecureMemory {
try {
long result = (long) MUNLOCK_HANDLE.invokeExact(
MemorySegment.ofAddress(this.address),
this.address,
this.size);
locked = result != 0;
return result == 0;
@ -208,7 +211,7 @@ public final class SecureMemory {
* Securely zeros buffer contents.
*/
public void zero() {
if (data != null) {
if (data != MemorySegment.NULL) {
data.fill((byte) 0);
}
}
@ -220,6 +223,7 @@ public final class SecureMemory {
zero();
unlock();
arena.close();
closed = true;
}

View file

@ -54,17 +54,17 @@ public final class Pass2Key {
byte[] iv = new byte[GCM_IV_LENGTH];
RANDOM.nextBytes(iv);
try (SecureBuffer secureSalt = SecureMemory.secureByteArray(salt); SecureBuffer secureIv = SecureMemory.secureByteArray(iv)) {
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, secureIv.getData().asByteBuffer().array());
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, secureIv.getAsByteArray());
byte[] ciphertext = encryptWithCipher(algorithm, key, spec, input);
try (SecureBuffer secureCiphertext = SecureMemory.secureByteArray(ciphertext)) {
payload = assemblePayloadGcm(secureSalt.getData().asByteBuffer().array(), secureIv.getData().asByteBuffer().array(), secureCiphertext.getData().asByteBuffer().array());
payload = assemblePayloadGcm(secureSalt.getAsByteArray(), secureIv.getAsByteArray(), secureCiphertext.getAsByteArray());
}
}
} else {
try (SecureBuffer secureSalt = SecureMemory.secureByteArray(salt)) {
byte[] ciphertext = encryptWithCipher(algorithm, key, input);
try (SecureBuffer secureCiphertext = SecureMemory.secureByteArray(ciphertext)) {
payload = assemblePayloadSalt(secureSalt.getData().asByteBuffer().array(), secureCiphertext.getData().asByteBuffer().array());
payload = assemblePayloadSalt(secureSalt.getAsByteArray(), secureCiphertext.getAsByteArray());
}
}
}
@ -92,13 +92,13 @@ public final class Pass2Key {
byte[] iv = java.util.Arrays.copyOfRange(decoded, SALT_LENGTH, SALT_LENGTH + GCM_IV_LENGTH);
byte[] ciphertext = java.util.Arrays.copyOfRange(decoded, SALT_LENGTH + GCM_IV_LENGTH, decoded.length);
try (SecureBuffer secureSalt = SecureMemory.secureByteArray(salt); SecureBuffer secureIv = SecureMemory.secureByteArray(iv); SecureBuffer secureCiphertext = SecureMemory.secureByteArray(ciphertext)) {
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, secureIv.getData().asByteBuffer().array());
result = decryptWithCipher(algorithm, key, spec, secureCiphertext.getData().asByteBuffer().array());
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, secureIv.getAsByteArray());
result = decryptWithCipher(algorithm, key, spec, secureCiphertext.getAsByteArray());
}
} else {
byte[] ciphertext = java.util.Arrays.copyOfRange(decoded, SALT_LENGTH, decoded.length);
try (SecureBuffer secureSalt = SecureMemory.secureByteArray(salt); SecureBuffer secureCiphertext = SecureMemory.secureByteArray(ciphertext)) {
result = decryptWithCipher(algorithm, key, secureCiphertext.getData().asByteBuffer().array());
result = decryptWithCipher(algorithm, key, secureCiphertext.getAsByteArray());
}
}
@ -172,7 +172,7 @@ public final class Pass2Key {
try (SecureBuffer secureKey = SecureMemory.secureByteArray(decodedKey)) {
// Create a PKCS8EncodedKeySpec object
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(secureKey.getData().asByteBuffer().array());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(secureKey.getAsByteArray());
// Get an instance of the KeyFactory for the desired algorithm (e.g., RSA)
KeyFactory keyFactory = KeyFactory.getInstance("RSA");

View file

@ -257,11 +257,12 @@ class SecureMemoryTest {
@Test
@DisplayName("SecureBuffer should preserve data contents")
void secureBuffer_shouldPreserveData() {
byte[] original = new byte[]{(byte) 0x00, (byte) 0xFF, (byte) 0xAA, (byte) 0x55, 42};
byte[] expected = new byte[]{(byte) 0x00, (byte) 0xFF, (byte) 0xAA, (byte) 0x55, 42};
byte[] original = expected.clone();
try (SecureMemory.SecureBuffer buffer = SecureMemory.secureByteArray(original)) {
byte[] retrieved = new byte[original.length];
byte[] retrieved = new byte[expected.length];
buffer.getData().asByteBuffer().get(retrieved);
assertArrayEquals(original, retrieved, "Data should be preserved in buffer");
assertArrayEquals(expected, retrieved, "Data should be preserved in buffer");
}
}