@@ -77,11 +77,38 @@ def parse(self):
7777 self ._root_xml = b64decode (raw_response .encode ())
7878 self ._root = fromstring (self ._root_xml )
7979
80+ sig_errors = []
81+ if self ._source .verification_kp and self ._source .signed_response :
82+ resp_error = self ._verify_signed ("/samlp:Response" )
83+ if resp_error == "" :
84+ self .response_signature_verified = True
85+ else :
86+ self .response_signature_verified = False
87+ sig_errors .append (resp_error )
88+
8089 if self ._source .encryption_kp :
8190 self ._decrypt_response ()
8291
83- if self ._source .verification_kp :
84- self ._verify_signed ()
92+ if self ._source .verification_kp and self ._source .signed_assertion :
93+ assert_error = self ._verify_signed ("/samlp:Response/saml:Assertion" )
94+ if assert_error != "" :
95+ raise InvalidSignature (f"Assertion signature invalid: { assert_error } " )
96+
97+ if (
98+ self ._source .verification_kp
99+ and self ._source .signed_response
100+ and (self .response_signature_verified is not True )
101+ ):
102+ post_error = self ._verify_signed ("/samlp:Response" )
103+ if post_error == "" :
104+ self .response_signature_verified = True
105+ else :
106+ self .response_signature_verified = False
107+ sig_errors .append (resp_error )
108+
109+ if self ._source .signed_response and (self .response_signature_verified is False ):
110+ raise InvalidSignature (f"SAML Response signature invalid: { '; ' .join (sig_errors )} " )
111+
85112 self ._verify_request_id ()
86113 self ._verify_status ()
87114
@@ -114,45 +141,34 @@ def _decrypt_response(self):
114141 decrypted_assertion ,
115142 )
116143
117- def _verify_signed (self ) :
144+ def _verify_signed (self , xpath : str ) -> str :
118145 """Verify SAML Response's Signature"""
119- signatures = []
120-
121- if self ._source .signed_response :
122- signature_nodes = self ._root .xpath ("/samlp:Response/ds:Signature" , namespaces = NS_MAP )
123-
124- if len (signature_nodes ) != 1 :
125- raise InvalidSignature ("No Signature exists in the Response element." )
126- signatures .extend (signature_nodes )
127-
128- if self ._source .signed_assertion :
129- signature_nodes = self ._root .xpath (
130- "/samlp:Response/saml:Assertion/ds:Signature" , namespaces = NS_MAP
131- )
132-
133- if len (signature_nodes ) != 1 :
134- raise InvalidSignature ("No Signature exists in the Assertion element." )
135- signatures .extend (signature_nodes )
136-
137- if len (signatures ) == 0 :
138- raise InvalidSignature ()
139-
140- for signature_node in signatures :
141- xmlsec .tree .add_ids (self ._root , ["ID" ])
142-
143- ctx = xmlsec .SignatureContext ()
144- key = xmlsec .Key .from_memory (
145- self ._source .verification_kp .certificate_data ,
146- xmlsec .constants .KeyDataFormatCertPem ,
147- )
148- ctx .key = key
149-
150- ctx .set_enabled_key_data ([xmlsec .constants .KeyDataX509 ])
151- try :
152- ctx .verify (signature_node )
153- except xmlsec .Error as exc :
154- raise InvalidSignature () from exc
155- LOGGER .debug ("Successfully verified signature" )
146+ nodes = self ._root .xpath (xpath , namespaces = NS_MAP )
147+ if len (nodes ) != 1 :
148+ return f"no-node:{ xpath } "
149+ node = nodes [0 ]
150+ sigs = node .findall ("ds:Signature" , namespaces = NS_MAP )
151+ if not sigs :
152+ return f"{ xpath } : no-signature"
153+ if len (sigs ) > 1 :
154+ return f"{ xpath } : multiple-signatures ({ len (sigs )} )"
155+ sig = sigs [0 ]
156+
157+ xmlsec .tree .add_ids (self ._root , ["ID" ])
158+ ctx = xmlsec .SignatureContext ()
159+ key = xmlsec .Key .from_memory (
160+ self ._source .verification_kp .certificate_data ,
161+ xmlsec .constants .KeyDataFormatCertPem ,
162+ )
163+ ctx .key = key
164+ try :
165+ ctx .verify (sig )
166+ return "" # OK
167+ except xmlsec .Error as exc :
168+ tag = node .tag .split ("}" , 1 )[- 1 ]
169+ ref_uri = sig .xpath ("ds:SignedInfo/ds:Reference/@URI" , namespaces = NS_MAP )
170+ ref_uri = ref_uri [0 ] if ref_uri else "N/A"
171+ return f"{ tag } :ref={ ref_uri } : { exc } "
156172
157173 def _verify_request_id (self ):
158174 if self ._source .allow_idp_initiated :
0 commit comments