[java-opensaml] branch main updated: OSJ-367: Decrypter does not guard against malicious references ...
Brent Putman
putmanb at georgetown.edu
Tue Jan 10 04:34:52 UTC 2023
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch main
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=170798f6ff076f515a4dfe2c3a408b72fb0d3c7f
The following commit(s) were added to refs/heads/main by this push:
new 170798f6f OSJ-367: Decrypter does not guard against malicious references ...
170798f6f is described below
commit 170798f6ff076f515a4dfe2c3a408b72fb0d3c7f
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Mon Jan 9 22:26:22 2023 -0500
OSJ-367: Decrypter does not guard against malicious references ...
Decrypter does not guard against malicious references or transforms.
---
.../xmlsec/encryption/support/Decrypter.java | 57 ++++++++++++++-
.../support/DefaultPreDecryptionValidator.java | 65 ++++++++++++++++++
.../support/PreDecryptionValidationException.java | 67 ++++++++++++++++++
.../encryption/support/PreDecryptionValidator.java | 48 +++++++++++++
.../tests/DefaultPreDecryptionValidatorTest.java | 80 ++++++++++++++++++++++
.../support/tests/SimpleDecryptionTest.java | 72 +++++++++++++++++++
6 files changed, 387 insertions(+), 2 deletions(-)
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java
index b6222a49c..9dcfd8250 100644
--- a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/Decrypter.java
@@ -222,6 +222,9 @@ public class Decrypter {
* root of a new DOM document. */
private boolean defaultRootInNewDocument;
+ /** The pre-decryption validator instance. */
+ private PreDecryptionValidator preDecryptionValidator;
+
/**
* Constructor.
*
@@ -288,8 +291,28 @@ public class Decrypter {
unmarshallerFactory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
defaultRootInNewDocument = false;
+
+ preDecryptionValidator = new DefaultPreDecryptionValidator();
}
+ /**
+ * Get the instance of {@link PreDecryptionValidator}.
+ *
+ * @return the validator, may be null
+ */
+ @Nullable PreDecryptionValidator getPreDecryptionValidator() {
+ return preDecryptionValidator;
+ }
+
+ /**
+ * Set the instance of {@link PreDecryptionValidator}.
+ *
+ * @param validator the validator, may be null
+ */
+ public void setPreDecryptionValidator(@Nullable final PreDecryptionValidator validator) {
+ preDecryptionValidator = validator;
+ }
+
/**
* Get the flag which indicates whether by default the DOM Element which backs a decrypted SAML object
* will be the root of a new DOM document. Defaults to false.
@@ -572,6 +595,9 @@ public class Decrypter {
log.error("Error marshalling EncryptedData for decryption", e);
throw e;
}
+
+ preProcessEncryptedData(encryptedData, dataEncKey);
+
final Element targetElement = encryptedData.getDOM();
final XMLCipher xmlCipher;
@@ -675,6 +701,7 @@ public class Decrypter {
log.error("Error marshalling EncryptedKey for decryption: {}", e.getMessage());
throw e;
}
+
preProcessEncryptedKey(encryptedKey, algorithm, kek);
final XMLCipher xmlCipher;
@@ -717,7 +744,31 @@ public class Decrypter {
}
/**
- * Preprocess the EncryptedKey. For example, check for supported algorithms.
+ * Preprocess the EncryptedData.
+ *
+ * <p>
+ * For example, perform pre-decryption validation of the encrypted type.
+ * </p>
+ *
+ * @param encryptedData encrypted data element containing the encrypted data to be decrypted
+ * @param dataEncKey the key with which to attempt decryption of the encrypted data
+ *
+ * @throws DecryptionException exception indicating a decryption error
+ */
+ protected void preProcessEncryptedData(@Nonnull final EncryptedData encryptedData,
+ @Nonnull final Key dataEncKey) throws DecryptionException {
+
+ if (getPreDecryptionValidator() != null) {
+ getPreDecryptionValidator().validate(encryptedData);
+ }
+ }
+
+ /**
+ * Preprocess the EncryptedKey.
+ *
+ * <p>
+ * For example, perform pre-decryption validation of the encrypted type.
+ * </p>
*
* @param encryptedKey encrypted key element containing the encrypted key to be decrypted
* @param algorithm the algorithm associated with the decrypted key
@@ -728,7 +779,9 @@ public class Decrypter {
protected void preProcessEncryptedKey(@Nonnull final EncryptedKey encryptedKey, @Nonnull final String algorithm,
@Nonnull final Key kek) throws DecryptionException {
- // No-op for now. Subclasses can do things here if they want.
+ if (getPreDecryptionValidator() != null) {
+ getPreDecryptionValidator().validate(encryptedKey);
+ }
}
/**
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/DefaultPreDecryptionValidator.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/DefaultPreDecryptionValidator.java
new file mode 100644
index 000000000..f8a9a239f
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/DefaultPreDecryptionValidator.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.xmlsec.encryption.support;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.xmlsec.encryption.EncryptedData;
+import org.opensaml.xmlsec.encryption.EncryptedKey;
+import org.opensaml.xmlsec.encryption.EncryptedType;
+
+/**
+ * Default implementation of {@link PreDecryptionValidator}.
+ */
+public class DefaultPreDecryptionValidator implements PreDecryptionValidator {
+
+ /** {@inheritDoc} */
+ @Override
+ public void validate(EncryptedData encryptedData) throws PreDecryptionValidationException {
+ performCommonValidation(encryptedData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void validate(EncryptedKey encryptedKey) throws PreDecryptionValidationException {
+ performCommonValidation(encryptedKey);
+ }
+
+ /**
+ * Perform validation common to both {@link EncryptedData} and {@link EncryptedKey}.
+ *
+ * @param encryptedType the target to validate
+ *
+ * @throws PreDecryptionValidationException if the target fails validation
+ */
+ protected void performCommonValidation(@Nonnull final EncryptedType encryptedType)
+ throws PreDecryptionValidationException {
+
+ if (encryptedType.getCipherData() == null) {
+ throw new PreDecryptionValidationException(
+ String.format("%s contains no CipherData child element, which is mandatory",
+ encryptedType.getClass().getSimpleName()));
+ }
+
+ if (encryptedType.getCipherData().getCipherReference() != null) {
+ throw new PreDecryptionValidationException(
+ String.format("%s contains a CipherReference, which is not allowed",
+ encryptedType.getClass().getSimpleName()));
+ }
+ }
+}
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/PreDecryptionValidationException.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/PreDecryptionValidationException.java
new file mode 100644
index 000000000..1a6842b08
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/PreDecryptionValidationException.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.xmlsec.encryption.support;
+
+import javax.annotation.Nullable;
+
+/**
+ * Exception thrown when an error occurs during pre-decryption validation.
+ */
+public class PreDecryptionValidationException extends DecryptionException {
+
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 5697805272721615415L;
+
+ /**
+ * Constructor.
+ */
+ public PreDecryptionValidationException() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message exception message
+ */
+ public PreDecryptionValidationException(@Nullable final String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param wrappedException exception to be wrapped by this one
+ */
+ public PreDecryptionValidationException(@Nullable final Exception wrappedException) {
+ super(wrappedException);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message exception message
+ * @param wrappedException exception to be wrapped by this one
+ */
+ public PreDecryptionValidationException(@Nullable final String message, @Nullable final Exception wrappedException) {
+ super(message, wrappedException);
+ }
+
+}
\ No newline at end of file
diff --git a/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/PreDecryptionValidator.java b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/PreDecryptionValidator.java
new file mode 100644
index 000000000..58f484390
--- /dev/null
+++ b/opensaml-xmlsec-api/src/main/java/org/opensaml/xmlsec/encryption/support/PreDecryptionValidator.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.xmlsec.encryption.support;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.xmlsec.encryption.EncryptedData;
+import org.opensaml.xmlsec.encryption.EncryptedKey;
+
+/**
+ * Component which performs validation of encrypted types prior to decryption.
+ */
+public interface PreDecryptionValidator {
+
+ /**
+ * Validate an instance of {@link EncryptedData}.
+ *
+ * @param encryptedData the target to validate
+ *
+ * @throws PreDecryptionValidationException if the target fails validation
+ */
+ public void validate(@Nonnull final EncryptedData encryptedData) throws PreDecryptionValidationException;
+
+ /**
+ * Validate an instance of {@link EncryptedKey}.
+ *
+ * @param encryptedKey the target to validate
+ *
+ * @throws PreDecryptionValidationException if the target fails validation
+ */
+ public void validate(@Nonnull final EncryptedKey encryptedKey) throws PreDecryptionValidationException;
+
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DefaultPreDecryptionValidatorTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DefaultPreDecryptionValidatorTest.java
new file mode 100644
index 000000000..8c7f27b54
--- /dev/null
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/DefaultPreDecryptionValidatorTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.xmlsec.encryption.support.tests;
+
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
+import org.opensaml.core.xml.util.XMLObjectSupport;
+import org.opensaml.xmlsec.encryption.CipherData;
+import org.opensaml.xmlsec.encryption.CipherReference;
+import org.opensaml.xmlsec.encryption.EncryptedData;
+import org.opensaml.xmlsec.encryption.EncryptedKey;
+import org.opensaml.xmlsec.encryption.support.DefaultPreDecryptionValidator;
+import org.opensaml.xmlsec.encryption.support.PreDecryptionValidationException;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ */
+public class DefaultPreDecryptionValidatorTest extends XMLObjectBaseTestCase {
+
+ private DefaultPreDecryptionValidator validator;
+
+ private EncryptedData encryptedData;
+ private EncryptedKey encryptedKey;
+
+ @BeforeMethod
+ public void setup() throws Exception {
+ validator = new DefaultPreDecryptionValidator();
+
+ encryptedData = (EncryptedData) XMLObjectSupport.buildXMLObject(EncryptedData.DEFAULT_ELEMENT_NAME);
+ encryptedData.setCipherData((CipherData) XMLObjectSupport.buildXMLObject(CipherData.DEFAULT_ELEMENT_NAME));
+
+ encryptedKey = (EncryptedKey) XMLObjectSupport.buildXMLObject(EncryptedKey.DEFAULT_ELEMENT_NAME);
+ encryptedKey.setCipherData((CipherData) XMLObjectSupport.buildXMLObject(CipherData.DEFAULT_ELEMENT_NAME));
+ }
+
+ @Test
+ public void goodEncryptedData() throws Exception {
+ validator.validate(encryptedData);
+ }
+
+ @Test
+ public void goodEncryptedKey() throws Exception {
+ validator.validate(encryptedKey);
+ }
+
+ @Test(expectedExceptions=PreDecryptionValidationException.class)
+ public void noCipherData() throws Exception {
+ encryptedData.setCipherData(null);
+ validator.validate(encryptedData);
+ }
+
+ @Test(expectedExceptions=PreDecryptionValidationException.class)
+ public void encryptedDataWithCipherReference() throws Exception {
+ encryptedData.getCipherData().setCipherReference((CipherReference) XMLObjectSupport.buildXMLObject(CipherReference.DEFAULT_ELEMENT_NAME));
+ validator.validate(encryptedData);
+ }
+
+ @Test(expectedExceptions=PreDecryptionValidationException.class)
+ public void encryptedKeyWithCipherReference() throws Exception {
+ encryptedKey.getCipherData().setCipherReference((CipherReference) XMLObjectSupport.buildXMLObject(CipherReference.DEFAULT_ELEMENT_NAME));
+ validator.validate(encryptedKey);
+ }
+
+}
diff --git a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleDecryptionTest.java b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleDecryptionTest.java
index b46091a98..ecdb0bfa0 100644
--- a/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleDecryptionTest.java
+++ b/opensaml-xmlsec-impl/src/test/java/org/opensaml/xmlsec/encryption/support/tests/SimpleDecryptionTest.java
@@ -29,10 +29,12 @@ import javax.crypto.SecretKey;
import org.opensaml.core.testing.XMLObjectBaseTestCase;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.testing.SecurityProviderTestSupport;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
+import org.opensaml.xmlsec.encryption.CipherReference;
import org.opensaml.xmlsec.encryption.EncryptedData;
import org.opensaml.xmlsec.encryption.EncryptedKey;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
@@ -44,6 +46,7 @@ import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.encryption.support.EncryptionException;
import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
+import org.opensaml.xmlsec.encryption.support.PreDecryptionValidationException;
import org.opensaml.xmlsec.encryption.support.RSAOAEPParameters;
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.StaticKeyInfoCredentialResolver;
@@ -165,6 +168,75 @@ public class SimpleDecryptionTest extends XMLObjectBaseTestCase {
}
+ /**
+ * Test simple decryption of an EncryptedKey object with a null pre-decryption validator.
+ */
+ @Test
+ public void testEncryptedKeyWithNoPreValidator() {
+ Decrypter decrypter = new Decrypter(null, kekResolver, null);
+
+ decrypter.setPreDecryptionValidator(null);
+
+ Key decryptedKey = null;
+ try {
+ decryptedKey = decrypter.decryptKey(encryptedKey, encURI);
+ } catch (DecryptionException e) {
+ Assert.fail("Error on decryption of EncryptedKey: " + e);
+ }
+
+ Assert.assertEquals(encKey, decryptedKey, "Decrypted EncryptedKey");
+
+ }
+
+ /**
+ * Test simple decryption of an EncryptedData object which is of type Element,
+ * with a null pre-decryption validator.
+ */
+ @Test
+ public void testEncryptedElementWithNoPrevalidator() {
+ Decrypter decrypter = new Decrypter(keyResolver, null, null);
+
+ decrypter.setPreDecryptionValidator(null);
+
+ XMLObject decryptedXMLObject = null;
+ try {
+ decryptedXMLObject = decrypter.decryptData(encryptedData);
+ } catch (DecryptionException e) {
+ Assert.fail("Error on decryption of EncryptedData to element: " + e);
+ }
+
+ assertXMLEquals(targetDOM, decryptedXMLObject);
+
+ }
+
+ /**
+ * Test EncryptedData decryption which should fail due to presence of CipherReference failing
+ * default pre-decryption validator.
+ *
+ * @throws DecryptionException
+ */
+ @Test(expectedExceptions=DecryptionException.class)
+ public void testEncryptedDataWithCipherReference() throws DecryptionException {
+ encryptedData.getCipherData().setCipherReference((CipherReference) XMLObjectSupport.buildXMLObject(CipherReference.DEFAULT_ELEMENT_NAME));
+
+ Decrypter decrypter = new Decrypter(keyResolver, null, null);
+ decrypter.decryptData(encryptedData);
+ }
+
+ /**
+ * Test EncryptedData decryption which should fail due to presence of CipherReference failing
+ * default pre-decryption validator.
+ *
+ * @throws DecryptionException
+ */
+ @Test(expectedExceptions=DecryptionException.class)
+ public void testEncryptedKeyWithCipherReference() throws DecryptionException {
+ encryptedKey.getCipherData().setCipherReference((CipherReference) XMLObjectSupport.buildXMLObject(CipherReference.DEFAULT_ELEMENT_NAME));
+
+ Decrypter decrypter = new Decrypter(null, kekResolver, null);
+ decrypter.decryptKey(encryptedKey, encURI);
+ }
+
/**
* Test EncryptedData decryption which should fail due to blacklist validation.
*
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list