summaryrefslogtreecommitdiff
path: root/eaaf_modules
diff options
context:
space:
mode:
authorThomas <>2023-05-22 08:34:20 +0200
committerThomas <>2023-05-22 08:34:20 +0200
commit84b36f837222efbd3160bd41a9437058e1c5f2ff (patch)
tree49560fc713147dbe4261dd8d1e836ba639d86879 /eaaf_modules
parent00042becb5593d15138f7b5317c9d1f8db8108b2 (diff)
downloadEAAF-Components-84b36f837222efbd3160bd41a9437058e1c5f2ff.tar.gz
EAAF-Components-84b36f837222efbd3160bd41a9437058e1c5f2ff.tar.bz2
EAAF-Components-84b36f837222efbd3160bd41a9437058e1c5f2ff.zip
chore(saml2): optimize metadata signature-verification filter
Select trusted X509 certificates based on KeyInfo from XML-Signature, if possible
Diffstat (limited to 'eaaf_modules')
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SimpleMetadataSignatureVerificationFilter.java79
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java53
2 files changed, 121 insertions, 11 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SimpleMetadataSignatureVerificationFilter.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SimpleMetadataSignatureVerificationFilter.java
index 5a97924f..f4b008af 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SimpleMetadataSignatureVerificationFilter.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/validation/metadata/SimpleMetadataSignatureVerificationFilter.java
@@ -25,17 +25,24 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata;
import java.security.KeyStore;
import java.security.KeyStoreException;
+import java.security.MessageDigest;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.xml.security.keys.KeyInfo;
+import org.apache.xml.security.keys.keyresolver.KeyResolverException;
import org.opensaml.saml.common.SignableSAMLObject;
import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
import org.opensaml.security.x509.BasicX509Credential;
+import org.opensaml.xmlsec.signature.Signature;
+import org.opensaml.xmlsec.signature.impl.SignatureImpl;
import org.opensaml.xmlsec.signature.support.SignatureException;
import org.opensaml.xmlsec.signature.support.SignatureValidator;
@@ -124,28 +131,78 @@ public class SimpleMetadataSignatureVerificationFilter extends AbstractMetadataS
}
// perform cryptographic signature verification
- boolean isTrusted = false;
- for (final BasicX509Credential cred : getTrustedCertificates()) {
- log.trace("Validating signature with credential: {} ... ",
- cred.getEntityCertificate().getSubjectDN());
+ if (!performCryptographicCheck(signedElement.getSignature())) {
+ log.info("PVP2 metadata: " + metadataUrl + " are NOT trusted!");
+ throw new SamlMetadataSignatureException(metadataUrl, ERROR_MSG_SIGNOTVALID);
+
+ }
+ }
+
+ private boolean performCryptographicCheck(Signature signature) throws EaafConfigurationException {
+
+ // extract trusted signer certificate from signature
+ BasicX509Credential x509FromSig = extractTrustedX509FromSignature(signature);
+ if (x509FromSig != null) {
try {
- SignatureValidator.validate(signedElement.getSignature(), cred);
- isTrusted = true;
+ SignatureValidator.validate(signature, x509FromSig);
+ return true;
} catch (final SignatureException e) {
log.debug("Failed to verfiy Signature with cert: {} Reason: {}",
- cred.getEntityCertificate().getSubjectDN(), e.getMessage());
+ x509FromSig.getEntityCertificate().getSubjectDN(), e.getMessage());
+
+ }
+ } else {
+ for (final BasicX509Credential cred : getTrustedCertificates()) {
+ log.trace("Validating signature with credential: {} ... ",
+ cred.getEntityCertificate().getSubjectDN());
+ try {
+ SignatureValidator.validate(signature, cred);
+ return true;
+
+ } catch (final SignatureException e) {
+ log.debug("Failed to verfiy Signature with cert: {} Reason: {}",
+ cred.getEntityCertificate().getSubjectDN(), e.getMessage());
+
+ }
}
}
- if (!isTrusted) {
- log.info("PVP2 metadata: " + metadataUrl + " are NOT trusted!");
- throw new SamlMetadataSignatureException(metadataUrl, ERROR_MSG_SIGNOTVALID);
+ return false;
+
+ }
+
+ private BasicX509Credential extractTrustedX509FromSignature(Signature signature) {
+ try {
+ KeyInfo keyInfo = ((SignatureImpl) signature).getXMLSignature().getKeyInfo();
+
+ byte[] encSigCert = keyInfo.getX509Certificate() != null
+ ? keyInfo.getX509Certificate().getEncoded()
+ : ArrayUtils.EMPTY_BYTE_ARRAY;
+
+ return getTrustedCertificates().stream()
+ .filter(el -> {
+ try {
+ return MessageDigest.isEqual(el.getEntityCertificate().getEncoded(), encSigCert);
+
+ } catch (CertificateEncodingException e) {
+ log.warn("Can not match certificates.", e);
+ return false;
+
+ }
+ })
+ .findFirst()
+ .orElse(null);
+
+ } catch (KeyResolverException | EaafConfigurationException | CertificateEncodingException e) {
+ log.info("Can not extract X509 certificate from XML signature. Reason: {}", e.getMessage());
}
+ return null;
+
}
-
+
private List<BasicX509Credential> getTrustedCertificates() throws EaafConfigurationException {
try {
final List<X509Certificate> certs =
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java
index 036d682b..511b7283 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java
@@ -337,6 +337,59 @@ public class MetadataResolverTest {
}
@Test
+ public void metadataSignatureValidCredentialsNoX509() throws CertificateException, Pvp2MetadataException,
+ ResolverException, XMLParserException, UnmarshallingException, SamlSigningException,
+ CredentialsNotAvailableException, MarshallingException, TransformerException, IOException,
+ KeyStoreException, NoSuchAlgorithmException {
+
+ mockWebServer.shutdown();
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/metadata");
+
+ final EntityDescriptor metadata = (EntityDescriptor) XMLObjectSupport.unmarshallFromInputStream(
+ XMLObjectProviderRegistrySupport.getParserPool(),
+ MetadataResolverTest.class.getResourceAsStream("/data/pvp_metadata_moaid_test.xml"));
+ metadata.setValidUntil(Instant.now().plus(Duration.ofDays(1)));
+ metadata.setSignature(null);
+ metadata.setEntityID(RandomStringUtils.randomAlphabetic(10));
+ final EntityDescriptor signedMatadata =
+ Saml2Utils.signSamlObject(metadata, credentialProvider.getMetaDataSigningCredential(), false);
+ final Element metadataElement = XMLObjectSupport.marshall(signedMatadata);
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody(SerializeSupport.nodeToString(metadataElement))
+ .setHeader("Content-Type", "text/html;charset=utf-8"));
+
+ final KeyStore keystore = KeyStore.getInstance("JKS");
+ keystore.load(null, "junit".toCharArray());
+ final CertificateFactory fact = CertificateFactory.getInstance("X.509");
+ keystore.setCertificateEntry("1", fact.generateCertificate(
+ MetadataResolverTest.class.getResourceAsStream("/data/metadata_sig_cert.crt")));
+ keystore.setCertificateEntry("2", fact.generateCertificate(
+ MetadataResolverTest.class.getResourceAsStream("/data/assertion_sig_cert.crt")));
+ keystore.setCertificateEntry("3", fact.generateCertificate(
+ MetadataResolverTest.class.getResourceAsStream("/data/junit_metadata_sig_cert.crt")));
+
+ final List<MetadataFilter> filterList = new ArrayList<>();
+ filterList.add(new SchemaValidationFilter(true));
+ filterList.add(new SimpleMetadataSignatureVerificationFilter(
+ keystore,
+ mockServerUrl.url().toString()));
+ filterList.add(new PvpEntityCategoryFilter(true));
+
+ final MetadataFilterChain filterChain = new MetadataFilterChain();
+ filterChain.setFilters(filterList);
+
+ final IPvp2MetadataProvider mdResolver = metadataResolverFactory.createMetadataProvider(
+ mockServerUrl.url().toString(),
+ filterChain, "jUnit test", httpClientFactory.getHttpClient());
+
+ final EntityDescriptor entityIdNotExists = mdResolver.getEntityDescriptor(
+ metadata.getEntityID());
+ Assert.assertNotNull("No EntityDescripter", entityIdNotExists);
+
+ }
+
+ @Test
public void metadataSignatureValidCredentialsSecond() throws CertificateException, Pvp2MetadataException,
ResolverException, XMLParserException, UnmarshallingException, SamlSigningException,
CredentialsNotAvailableException, MarshallingException, TransformerException, IOException,