Fix Keycloak SAML Invalid Signature Errors

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
quay.io/keycloak/keycloak:26.8.0
openssl-3.5.5-6.el10_2.x86_64
libxml2-2.12.5-10.el10_2.2.x86_64
xmlsec1-1.2.39-3.el10.x86_64
Applies to Any Linux host with Podman or Docker
Privilege Normal user (rootless Podman). Realm setup uses the Keycloak bootstrap admin
Scope Why Keycloak reports an invalid SAML signature, how to tell a stale signing certificate from a signature on the wrong element or an unsigned AuthnRequest, and how to confirm the repair with the public certificate. Does not cover JWT or access-token signature errors, and turning validation off is not the fix.
Related guides Keycloak SAML metadata
Keycloak SP-Initiated vs IdP-Initiated SAML SSO
Keycloak SAML NameID
Keycloak SAML attribute mapper
Map Keycloak roles and groups into SAML assertions
Short on time?Beta Get the gist of this 15-minute read in seconds, then ask follow-up questions

First identify which Keycloak realm rejected the message. That tells you which signature and certificate to inspect. An invalid SAML signature means the receiver could not satisfy its signature check. It does not mean the login password was wrong.

Common causes:

  • The wrong public certificate
  • A modified signed element
  • An unsupported or mismatched signing algorithm
  • A signature required on a different SAML element

Start from the two-realm login in Keycloak SAML SSO. Realm company is the identity provider on 127.0.0.1:18107. Realm apps is the SAML broker on 127.0.0.1:18108. Sign and encrypt Keycloak SAML assertions is where those switches are configured. This page is the path I used after the broker, or the company realm, rejected a message that had been accepted a minute earlier.


Quick Answer

First identify which side rejected the message.

  • The apps broker says Invalid signature in response from identity provider: inspect the identity provider's signed SAMLResponse or Assertion, and the identity provider certificate stored on the broker.
  • The company realm says Invalid requester before login: inspect the service provider's AuthnRequest, and the service provider certificate stored on the SAML client.
  • If the signature validates with the expected public key but Keycloak still rejects it, check whether Keycloak expected the Assertion to be signed instead of only the outer Response.

The certificate used to verify an incoming message belongs to the sender, not the receiver.

SP / broker (apps)                              IdP (company)

AuthnRequest, signed by apps  ───────────────►  verifies the apps certificate

verifies the company certificate  ◄───────────  SAMLResponse, signed by company

First Identify Which Signature Failed

A signature error on the broker and a signature error on the identity provider involve different sender certificates. Note which realm painted the error, and which SAML message was in that POST.

What you see Direction Who verifies Message to inspect
Invalid signature in response from identity provider. on the apps realm IdP to SP Broker verifies the IdP SAMLResponse
Invalid requester on the company realm, before the login form SP to IdP IdP verifies the SP AuthnRequest
A signed SAML logout fails the same way Sender to receiver The peer that received the logout message LogoutRequest or LogoutResponse

Apps realm error card reading Invalid signature in response from identity provider

I produced the apps error by leaving Validate signatures on and replacing the company certificate with a different public certificate. The company realm had still signed the response. The broker was verifying it with the wrong key.

Do not start by turning Validate signatures off.

  • This Keycloak stops reporting the mismatch
  • A service provider outside Keycloak still rejects the signature
  • You lose the error that tells you which check failed

A clock difference is a different failure. Keycloak reports an assertion that is not yet valid, or that has expired, from the Conditions timestamps. That check is not the XML signature. When xmlsec1 prints FAIL, or the fingerprints differ, fixing the clock will not make the signature match.


Check Whether Response or Assertion Is Signed

Sign documents signs the Response. Sign assertions signs the Assertion.

SAMLResponse
├── Signature      ← Sign documents
└── Assertion
    └── Signature  ← Sign assertions

When Want Assertions signed is enabled, the broker requires a signature on the inner Assertion. A valid signature on only the outer Response does not satisfy that requirement. The response below is from a login that reached Update Account Information, with Sign documents on and Sign assertions off.

Those switches are on the company client's Settings tab. Signature algorithm, SAML signature key name, and Canonicalization method are on the same block:

Company realm SAML client with Sign documents on, Sign assertions off, RSA_SHA256, KEY_ID, and EXCLUSIVE

The response ID and the signature reference are the same value:

bash
xmllint --xpath 'string(//*[local-name()="Response"]/@ID)' response.xml
output
ID_a1dcc9b2-cf65-43c4-a94f-2415968059eb

An XML signature contains a Reference to the element whose digest it protects.

  • The URI matches the Response ID: the outer Response is signed
  • The URI matches the Assertion ID: the Assertion is signed

This reference points at the response, not at the assertion:

bash
xmllint --xpath 'string(//*[local-name()="Reference"]/@URI)' response.xml
output
#ID_a1dcc9b2-cf65-43c4-a94f-2415968059eb

The assertion in that file is ID_e8e5cba2-4d05-4a7d-99a1-07957a61e7f0. The signature does not name it. The signature method is RSA-SHA256:

bash
grep -o 'SignatureMethod Algorithm="[^"]*"' response.xml
output
SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"

The next section checks this file with the company public key, and xmlsec1 prints OK. I then turned Want Assertions signed on and left Sign assertions off. The next login showed the same apps error, Invalid signature in response from identity provider.

  • xmlsec1 still printed OK for that new response
  • The signature was valid
  • It still covered the Response, not the Assertion
  • The broker was asking for a signature on the assertion

Turn on Sign assertions, or turn Want Assertions signed off when a response signature is what you agreed. Replacing the certificate does not fix this case.


Compare the Keycloak Signing Certificate

The company realm signs with its active RS256 private key. Compare the same public certificate in each of these places:

  • Realm metadata
  • Realm settings, Keys
  • The broker field Validating X509 certificates

A Java truststore is not that broker field.

On the company-idp provider, Validate signatures is on and Use metadata descriptor URL is off, so Keycloak uses the certificate pasted below that switch:

Apps realm identity provider with Validate signatures on and a certificate in Validating X509 certificates

The descriptor is http://127.0.0.1:18107/realms/company/protocol/saml/descriptor. I saved a copy to idp.xml while the realm still published one signing certificate:

bash
grep -o 'use="signing"' idp.xml | wc -l
output
1

Wrap the first signing certificate. The command prints nothing:

bash
{
  echo '-----BEGIN CERTIFICATE-----'
  xmllint --xpath 'string((//*[local-name()="KeyDescriptor" and @use="signing"]//*[local-name()="X509Certificate"])[1])' idp.xml \
    | tr -d '[:space:]' | fold -w 64
  echo
  echo '-----END CERTIFICATE-----'
} > idp-signing.pem

Read the subject and SHA-256 fingerprint:

bash
openssl x509 -in idp-signing.pem -noout -subject -fingerprint -sha256
output
subject=CN=company
sha256 Fingerprint=0B:01:C9:C0:11:9D:00:0B:30:F5:FD:57:17:CB:F5:86:60:EF:81:6E:8D:4C:CC:0D:D0:AE:1F:BB:D1:A5:F7:AB

In this generated certificate:

  • The subject CN is company, matching the realm name
  • The SHA-256 fingerprint is a short identifier for the certificate
  • A different fingerprint on the broker means it is trusting a different key

Write the public key for xmlsec1. That command is silent:

bash
openssl x509 -in idp-signing.pem -pubkey -noout > idp-signing.pub

Verify the saved response with that key. xmlsec1 first warns that the realm certificate is self-signed. The result line is OK:

bash
xmlsec1 --verify --pubkey-pem idp-signing.pub \
  --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response response.xml
output
OK
SignedInfo References (ok/all): 1/1
Manifests References (ok/all): 0/0

OK on that file means:

  • This captured XML validates with the public key you supplied
  • It does not prove the broker is configured with that same certificate
  • It does not prove Keycloak required the signature on the same element

I then replaced the broker's validating certificate with the apps realm signing certificate, fingerprint 77:B4:88:BD:C1:87:2E:6F:73:3F:FD:42:0D:35:C7:58:6F:5D:19:2A:A4:2C:D0:FF:93:58:25:B8:C4:42:27:03. The next login showed the invalid-signature card above. The captured response was unchanged. Only the public key used for verification was different. Checking the same file with the apps public key fails, and the command exits 1:

bash
xmlsec1 --verify --pubkey-pem sp-signing.pub \
  --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response response.xml
output
func=xmlSecOpenSSLEvpSignatureVerify:file=evp_signatures.c:line=453:obj=rsa-sha256:subj=unknown:error=18:data do not match:details=EVP_VerifyFinal: signature verification failed
FAIL
SignedInfo References (ok/all): 1/1
Manifests References (ok/all): 0/0
Error: failed to verify file "response.xml"

1/1 means the digest of the response still matches. The signature value does not match this public key, so the certificate is wrong and the XML was not edited. I put the 0B:01:... certificate back, and the next login reached Update Account Information again.


Fix Stale Metadata After Key Rotation or Upgrade

Peers keep the previous certificate after any of these changes:

  • A Keycloak upgrade
  • A new realm
  • A new realm key

I added an rsa-generated RS256 key at priority 200 and left the original key active at priority 100. Download the descriptor after the new key is active:

bash
curl -fsS -o idp-rotated.xml -w '%{http_code} %{content_type}\n' \
  http://127.0.0.1:18107/realms/company/protocol/saml/descriptor
output
200 application/xml;charset=UTF-8

The realm now publishes two signing certificates:

bash
grep -o 'use="signing"' idp-rotated.xml | wc -l
output
2

In this 26.8.0 test, the first signing certificate in the descriptor was the newly active key. Verify the key ID or fingerprint instead of relying on metadata order alone. Extract that first certificate the same way. The command prints nothing:

bash
{
  echo '-----BEGIN CERTIFICATE-----'
  xmllint --xpath 'string((//*[local-name()="KeyDescriptor" and @use="signing"]//*[local-name()="X509Certificate"])[1])' idp-rotated.xml \
    | tr -d '[:space:]' | fold -w 64
  echo
  echo '-----END CERTIFICATE-----'
} > idp-new.pem

The fingerprint is no longer 0B:01:...:

bash
openssl x509 -in idp-new.pem -noout -fingerprint -sha256
output
sha256 Fingerprint=E8:50:FC:B3:78:32:AC:EE:A3:F7:36:60:76:76:56:41:BF:0B:3A:49:01:69:1E:78:BB:F0:3E:5C:14:CA:16:B2

The new KeyName in metadata is fAe0nwine7ELG6dClI4_e57Sgm7fudx98DoDZk1bryU. The response verified earlier carried the old key id, WLsf79FApT4MUOK5PB6Pj_VRjQ1ul3R09IDynQ3Hsqk.

The broker was still pinning the old certificate, and Use metadata descriptor URL was off. The next login used the same invalid-signature page. Listing both certificates in metadata does not update a pasted certificate. I replaced the broker certificate with the new PEM body, without the BEGIN and END lines. The next login reached Update Account Information.

Use metadata descriptor URL changes where the broker gets that certificate:

  • On: Keycloak can refresh and cache validation certificates from the identity provider metadata
  • Off: a pasted certificate stays stale until you update it

Keycloak SAML metadata is where that descriptor is published.

After a move to a current Keycloak, compare this fingerprint with the certificate the service provider has stored. If they differ, refresh that peer. Do not turn validation off to get past the upgrade.


Fix Client Signature Required Errors

Client signature required protects the opposite direction, from the service provider to the identity provider.

  • It is set on the identity provider
  • Keycloak verifies SAML documents coming from the service provider
  • The public certificate is the one stored on that SAML client
  • The check does not use the company signing key

On the company realm's SAML client representing apps, import the apps realm public signing certificate under the client's Keys configuration. I turned Client signature required on, imported that certificate, and left Want AuthnRequests signed off. That switch is on the client's Keys tab, under Signing keys config:

Company realm SAML client Keys tab with Client signature required turned on

The company page stopped the login before the username form:

Company realm error card reading Invalid requester

The AuthnRequest in that POST had no Signature element. The company realm was asked to verify a signature that was not there.

  • In this test, the cause was that missing AuthnRequest signature
  • The same page can cover other request-validation failures
  • It is not the apps error from the certificate section
  • Pasting the company certificate onto the broker does not clear it

Confirm the server log and inspect the AuthnRequest before changing certificates.

On the apps realm's SAML identity-provider configuration, Want AuthnRequests signed makes the broker sign outgoing AuthnRequest messages with the apps realm signing key. I turned that on. The signature method was http://www.w3.org/2001/04/xmldsig-more#rsa-sha256, and xmlsec1 accepted it with the apps public key. The login then continued through the company form to the apps realm. If the service provider cannot sign requests, turn Client signature required off. Leaving it on and sending an unsigned request will keep producing Invalid requester.


Check Signature and Canonicalization Algorithms

The signed response and the signed AuthnRequest used the same settings. On the company client they are the three fields under Sign documents:

  • Signature algorithm: RSA_SHA256, which is http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
  • Canonicalization: EXCLUSIVE, which is http://www.w3.org/2001/10/xml-exc-c14n#
  • Signature key name: KEY_ID, so KeyName is the realm key id

Confirm that the signer uses an algorithm the receiving product supports.

  • The XML signature records the signature and canonicalization algorithms
  • A receiver may reject an algorithm it does not permit even when the certificate is correct
  • Keycloak marks SHA-1 signature algorithms as deprecated. Avoid SHA-1
  • A peer that only accepts SHA-256 rejects a SHA-1 signature even when the fingerprint matches

I did not use SHA-1 in this test.


Rule Out Proxy or XML Modification

Signature validation fails if the signed XML content changes after signing.

  • A proxy matters only when it alters the decoded XML
  • Transporting or re-encoding the HTTP form, without changing that XML, does not break the signature
  • Decoding, editing, reformatting, and then re-encoding the signed XML can make validation fail
  • Keep an untouched decoded copy for verification, as shown in Decode Keycloak SAML

The fingerprint can still match while the signature fails.

I changed the NameID in a copy of the valid response from alice to alicx and checked it with the same company public key that had printed OK. The digest no longer matches, so the reference count is 0/1 rather than 1/1:

bash
xmlsec1 --verify --pubkey-pem idp-signing.pub \
  --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response response-tampered.xml
output
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=355:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
SignedInfo References (ok/all): 0/1
Manifests References (ok/all): 0/0
Error: failed to verify file "response-tampered.xml"

Think of the checks in two stages:

  1. Digest check: is the signed XML still the same?
  2. Signature check: was that digest signed by the expected private key?

0/1 means the content changed. 1/1 plus a signature failure means the content is intact and the public key or signature does not match.

xmlsec1 or what you observe Likely problem
Correct public key, result OK The signature is cryptographically valid for that key
Wrong public key, EVP_VerifyFinal, references 1/1 Wrong or stale certificate
Correct key, digest mismatch, references 0/1 The signed XML changed
XML uses an unsupported or deprecated algorithm Algorithm policy or interoperability
Response signature is valid, but Keycloak requires an Assertion signature The wrong element was signed

Do not treat a changed document as a certificate rotation.


Verify the Fix with a Fresh SAML Trace

After the broker had the new certificate, I signed in again and saved that response as response-rotated.xml. Its KeyName was the new realm key. Verify it with the new public key, not with the certificate you replaced:

bash
xmlsec1 --verify --pubkey-pem idp-new.pub \
  --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response response-rotated.xml
output
OK
SignedInfo References (ok/all): 1/1
Manifests References (ok/all): 0/0

The old public key still fails this file. The command exits 1:

bash
xmlsec1 --verify --pubkey-pem idp-signing.pub \
  --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response response-rotated.xml
output
func=xmlSecOpenSSLEvpSignatureVerify:file=evp_signatures.c:line=453:obj=rsa-sha256:subj=unknown:error=18:data do not match:details=EVP_VerifyFinal: signature verification failed
FAIL
SignedInfo References (ok/all): 1/1
Manifests References (ok/all): 0/0
Error: failed to verify file "response-rotated.xml"

Record three things from the login that works:

  • The SHA-256 fingerprint
  • The Reference URI
  • Whether the browser left the error card

Update Account Information on the apps realm meant the broker accepted this response. A later peer that still has the 0B:01:... certificate will fail until it gets E8:50:....


Check the Realm Log for the Failing Signature

The error card names the realm. The log on that same server is where the check is recorded. No extra log level is required to see which side failed.

Realm Log to read What that log is checking
apps broker The apps server log Response and assertion verification
company identity provider The company server log AuthnRequest verification

Search that log for:

  • signature
  • invalid requester
  • verification

A line on the apps server is the identity provider's signature. A line on the company server is the service provider's signature.


Summary

The apps error Invalid signature in response from identity provider. means the broker rejected the company signature. Check the Reference URI first. If it names the response and Want Assertions signed is on, the signature is on the wrong element. If xmlsec1 prints OK with the company public key and FAIL with the certificate the broker has, replace that certificate.

When Keycloak starts signing with a new realm key, the signing certificate advertised in metadata changes. The broker kept 0B:01:... after the active key became E8:50:..., and the same error came back until I pasted the new certificate. Invalid requester on the company realm is a separate check. In this test, Client signature required saw an AuthnRequest with no signature. The same page can also appear for other request-validation failures, so read the company log before you replace a certificate.

A modified NameID fails with a digest mismatch (0/1) even when the public key is the one that signed the original response. Turning signature validation off hides these differences. Match the certificate, the signed element, and RSA_SHA256, then confirm the next response with xmlsec1.


References


Frequently Asked Questions

Why did SAML signatures fail after a Keycloak upgrade?

The service provider is usually still trusting the signing certificate from before the upgrade. A new realm or a new realm key publishes a different certificate in metadata. I added a new RS256 key and the fingerprint changed from 0B:01:... to E8:50:.... The broker rejected responses until I replaced its validating certificate. Compare those fingerprints before you turn validation off.

Where is Keycloak's SAML signing certificate?

Open Realm settings, then Keys, and use the active RS256 key. The same public certificate is published in a signing KeyDescriptor in the realm SAML metadata at /realms/{realm}/protocol/saml/descriptor. In this 26.8.0 rotation test the newly active key was the first signing certificate, but confirm the key ID or fingerprint rather than trusting metadata order. The private key stays on that Keycloak. Give peers only the certificate or the metadata.

Can I disable signature validation?

You can turn Validate signatures off on a broker, or turn Client signature required off on a client. That only stops this Keycloak from checking. It does not make another product accept a signature, and it hides a stale certificate. Put the current signing certificate on the peer instead.

What does Client signature required do?

It tells the identity provider to verify the service provider's signature on the AuthnRequest. The check uses the service provider certificate stored on the client. An unsigned request stops on the company realm with Invalid requester. It does not produce the apps-realm invalid-signature page.

Does certificate rotation require a metadata refresh?

Yes, for every peer that pinned the old certificate or cached the old metadata. A descriptor that lists the old and new certificates does not update a certificate you pasted while Use metadata descriptor URL is off. After rotation, compare the SHA-256 fingerprint and replace the pinned certificate.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)