Browse Source

[SAML] Filter potential signing keys by algorithm (#98119)

When performing signature verification, skip any keys that have a
non-matching algorithm.

This is needed in order to upgrade to newer versions of OpenSAML (e.g.
4.3) because of changes in how the underlying (saml) Signature object
stores state between verification attempts - in newer version
attempting to verify with an incompatible key will put the signature
into a state that it cannot recover from and all future verification
attempts will fail.
Tim Vernum 2 years ago
parent
commit
d161e3e65c

+ 17 - 0
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlObjectHandler.java

@@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.elasticsearch.ElasticsearchSecurityException;
 import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.hash.MessageDigests;
 import org.elasticsearch.core.CheckedFunction;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.core.Streams;
@@ -25,6 +26,7 @@ import org.opensaml.saml.saml2.encryption.Decrypter;
 import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.security.x509.X509Credential;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
 import org.opensaml.xmlsec.crypto.XMLSigningUtil;
 import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;
 import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
@@ -170,6 +172,21 @@ public class SamlObjectHandler {
 
         checkIdpSignature(credential -> {
             try {
+                final String signatureAlg = AlgorithmSupport.getKeyAlgorithm(signature.getSignatureAlgorithm());
+                final String keyAlg = credential.getPublicKey().getAlgorithm();
+                if (signatureAlg != null && signatureAlg.equals(keyAlg) == false) {
+                    if (logger.isDebugEnabled()) {
+                        String keyFingerprint = "SHA265:"
+                            + MessageDigests.toHexString(MessageDigests.sha256().digest(credential.getPublicKey().getEncoded()));
+                        logger.debug(
+                            "Skipping [{}] key [{}] because it is not compatible with signature algorithm [{}]",
+                            keyAlg,
+                            keyFingerprint,
+                            signatureAlg
+                        );
+                    }
+                    return false;
+                }
                 return AccessController.doPrivileged((PrivilegedExceptionAction<Boolean>) () -> {
                     try (RestorableContextClassLoader ignore = new RestorableContextClassLoader(SignatureValidator.class)) {
                         SignatureValidator.validate(signature, credential);