[java-support] 16/26: JSPT-116 - Give DataSealer ability to prefix wrapped results
Scott Cantor
cantor.2 at osu.edu
Tue Apr 12 14:38:41 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch dev/JSPT-111
in repository java-support.
View the commit online:
http://git.shibboleth.net/view/?p=java-support.git;a=commit;h=e68b05ef7d062e7f14dca6baec51b8a9e3094975
commit e68b05ef7d062e7f14dca6baec51b8a9e3094975
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Mar 2 13:39:43 2022 -0500
JSPT-116 - Give DataSealer ability to prefix wrapped results
https://shibboleth.atlassian.net/browse/JSPT-116
---
.../java/support/security/DataSealer.java | 53 ++++++++++++++++++++--
.../java/support/security/DataSealerTest.java | 37 ++++++++++++---
2 files changed, 80 insertions(+), 10 deletions(-)
diff --git a/src/main/java/net/shibboleth/utilities/java/support/security/DataSealer.java b/src/main/java/net/shibboleth/utilities/java/support/security/DataSealer.java
index c74d0ec..1440cd7 100644
--- a/src/main/java/net/shibboleth/utilities/java/support/security/DataSealer.java
+++ b/src/main/java/net/shibboleth/utilities/java/support/security/DataSealer.java
@@ -45,6 +45,7 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
import org.apache.commons.codec.BinaryDecoder;
import org.apache.commons.codec.BinaryEncoder;
@@ -61,6 +62,9 @@ import org.slf4j.LoggerFactory;
*/
public class DataSealer extends AbstractInitializableComponent {
+ /** Magic string to signal use of per-node prefix feature. */
+ @Nonnull @NotEmpty public static final String MAGIC_STRING = "PXR5";
+
/** Size of UTF-8 data chunks to read/write. */
private static final int CHUNK_SIZE = 60000;
@@ -82,6 +86,9 @@ public class DataSealer extends AbstractInitializableComponent {
/** Decodes encrypted string to bytes. */
@Nonnull private BinaryDecoder decoder;
+ /** Optional per-server node prefix to affix to encoded values. */
+ @Nullable private String nodePrefix;
+
/** Constructor. */
public DataSealer() {
encoder = new Base64(0, new byte[] { '\n' });
@@ -148,6 +155,22 @@ public class DataSealer extends AbstractInitializableComponent {
decoder = Constraint.isNotNull(d, "Decoder cannot be null");
}
+
+ /**
+ * Set a prefix to affix to wrapped values to support signaling to load balancers, etc.
+ *
+ * <p>The prefix will itself be prefixed by {@link #MAGIC_STRING} for compatibility with
+ * unprefixed values.</p>
+ *
+ * @param prefix node prefix
+ *
+ * @since 8.3.0
+ */
+ public void setNodePrefix(@Nullable @NotEmpty final String prefix) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ nodePrefix = StringSupport.trimOrNull(prefix);
+ }
/** {@inheritDoc} */
@Override
@@ -167,7 +190,11 @@ public class DataSealer extends AbstractInitializableComponent {
// Before we finish initialization, make sure that things are working.
testEncryption(keyStrategy.getDefaultKey().getSecond());
}
-
+
+ if (nodePrefix != null) {
+ log.info("DataSealer will attach prefix of {} + {} to wrapped values", MAGIC_STRING, nodePrefix);
+ }
+
} catch (final KeyException e) {
log.error(e.getMessage());
throw new ComponentInitializationException("Exception loading the keystore", e);
@@ -204,7 +231,17 @@ public class DataSealer extends AbstractInitializableComponent {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
try {
- final byte[] in = decoder.decode(wrapped.getBytes(StandardCharsets.UTF_8));
+ final byte[] in;
+ if (wrapped.startsWith(MAGIC_STRING)) {
+ if (wrapped.regionMatches(MAGIC_STRING.length(), nodePrefix, 0, nodePrefix.length())) {
+ in = decoder.decode(wrapped.substring(MAGIC_STRING.length() +
+ nodePrefix.length()).getBytes(StandardCharsets.UTF_8));
+ } else {
+ throw new DataSealerException("Data was node-prefixed but prefix did not match the expected value");
+ }
+ } else {
+ in = decoder.decode(wrapped.getBytes(StandardCharsets.UTF_8));
+ }
// Note: we don't technically need try-with-resources here b/c BAIS close() is a no-op
// and DIS close() just calls close() on the wrapped stream. But do for consistency.
@@ -308,6 +345,7 @@ public class DataSealer extends AbstractInitializableComponent {
return wrap(data, null);
}
+// Checkstyle: MethodLength OFF
/**
* Encodes data into an AEAD-encrypted blob, gzip(exp|data)
*
@@ -380,6 +418,14 @@ public class DataSealer extends AbstractInitializableComponent {
finalDataStream.flush();
finalByteStream.flush();
+ if (nodePrefix != null) {
+ final StringBuilder builder = new StringBuilder(MAGIC_STRING)
+ .append(nodePrefix)
+ .append(new String(encoder.encode(finalByteStream.toByteArray()),
+ StandardCharsets.UTF_8));
+ return builder.toString();
+ }
+
return new String(encoder.encode(finalByteStream.toByteArray()), StandardCharsets.UTF_8);
}
}
@@ -390,7 +436,8 @@ public class DataSealer extends AbstractInitializableComponent {
}
}
-
+// Checkstyle: MethodLength ON
+
/**
* Run a test over the configured bean properties.
*
diff --git a/src/test/java/net/shibboleth/utilities/java/support/security/DataSealerTest.java b/src/test/java/net/shibboleth/utilities/java/support/security/DataSealerTest.java
index 1ba3c26..925efed 100644
--- a/src/test/java/net/shibboleth/utilities/java/support/security/DataSealerTest.java
+++ b/src/test/java/net/shibboleth/utilities/java/support/security/DataSealerTest.java
@@ -28,6 +28,7 @@ import java.time.Instant;
import java.util.Arrays;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.springframework.core.io.ClassPathResource;
import org.testng.Assert;
@@ -67,7 +68,8 @@ public class DataSealerTest {
version2Resource = TestResourceConverter.of(resource);
}
- private DataSealer createDataSealer() throws DataSealerException, ComponentInitializationException {
+ private DataSealer createDataSealer(@Nullable @NotEmpty final String nodePrefix)
+ throws DataSealerException, ComponentInitializationException {
final BasicKeystoreKeyStrategy strategy = new BasicKeystoreKeyStrategy();
strategy.setKeyAlias("secret");
@@ -82,6 +84,7 @@ public class DataSealerTest {
final DataSealer sealer = new DataSealer();
sealer.setKeyStrategy(strategy);
+ sealer.setNodePrefix(nodePrefix);
sealer.initialize();
return sealer;
}
@@ -106,7 +109,7 @@ public class DataSealerTest {
}
@Test public void encodeDecode() throws DataSealerException, ComponentInitializationException {
- final DataSealer sealer = createDataSealer();
+ final DataSealer sealer = createDataSealer(null);
final String encoded = sealer.wrap(THE_DATA);
final StringBuffer alias = new StringBuffer();
@@ -114,8 +117,28 @@ public class DataSealerTest {
Assert.assertEquals(alias.toString(), "secret1");
}
+
+ @Test public void encodeDecodePrefixed() throws DataSealerException, ComponentInitializationException {
+ final DataSealer sealer = createDataSealer("serverA");
+
+ final String encoded = sealer.wrap(THE_DATA);
+ final StringBuffer alias = new StringBuffer();
+ Assert.assertEquals(sealer.unwrap(encoded, alias), THE_DATA);
+ Assert.assertEquals(alias.toString(), "secret1");
+ }
+
+
+ @Test(expectedExceptions=DataSealerException.class)
+ public void encodeDecodePrefixedWrong() throws DataSealerException, ComponentInitializationException {
+ final DataSealer sealer = createDataSealer("serverA");
+
+ final String encoded = sealer.wrap(THE_DATA);
+ final StringBuffer alias = new StringBuffer();
+ sealer.unwrap(encoded.replaceFirst("serverA", "serverB"), alias);
+ }
+
@Test public void encodeDecodeWithExp() throws DataSealerException, ComponentInitializationException {
- final DataSealer sealer = createDataSealer();
+ final DataSealer sealer = createDataSealer(null);
final String encoded = sealer.wrap(THE_DATA, Instant.now().plusSeconds(50));
final StringBuffer alias = new StringBuffer();
@@ -124,7 +147,7 @@ public class DataSealerTest {
}
@Test public void encodeDecodeSecondKey() throws DataSealerException, ComponentInitializationException {
- final DataSealer sealer = createDataSealer();
+ final DataSealer sealer = createDataSealer(null);
final DataSealer sealer2 = createDataSealer2();
final StringBuffer alias = new StringBuffer();
@@ -137,7 +160,7 @@ public class DataSealerTest {
}
@Test public void timeOut() throws DataSealerException, InterruptedException, ComponentInitializationException {
- final DataSealer sealer = createDataSealer();
+ final DataSealer sealer = createDataSealer(null);
String encoded = sealer.wrap(THE_DATA, Instant.now().plus(THE_DELAY));
Thread.sleep(THE_DELAY.toMillis() + 150);
@@ -150,7 +173,7 @@ public class DataSealerTest {
}
@Test public void encodeDecodeLong() throws DataSealerException, ComponentInitializationException {
- final DataSealer sealer = createDataSealer();
+ final DataSealer sealer = createDataSealer(null);
char[] buffer = new char[1000000];
Arrays.fill(buffer, 'x');
@@ -171,7 +194,7 @@ public class DataSealerTest {
}
- sealer = createDataSealer();
+ sealer = createDataSealer(null);
try {
sealer.unwrap("");
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list