77from cryptography .hazmat .primitives .asymmetric .padding import MGF1 , PSS , PKCS1v15
88from cryptography .hazmat .primitives .hmac import HMAC
99from cryptography .hazmat .primitives .serialization import Encoding , load_pem_private_key
10- from lxml .etree import Element , SubElement , _Element
10+ from lxml .etree import Element , QName , SubElement , _Element
1111
1212from .algorithms import (
1313 CanonicalizationMethod ,
@@ -117,6 +117,20 @@ def __init__(
117117 self ._parser = None
118118 self .signature_annotators = [self ._add_key_info ]
119119
120+ def _ds_tag (self , tag ):
121+ """
122+ Create a QName for the ds namespace, respecting the configured namespace mapping.
123+
124+ When the default namespace is set to the ds namespace ({None: namespaces.ds}),
125+ elements should be created without an explicit namespace so they inherit from
126+ the nsmap context. This avoids spurious xmlns="" undeclarations in C14N output.
127+
128+ See https://github.com/XML-Security/signxml/issues/275
129+ """
130+ if None in self .namespaces and self .namespaces [None ] == namespaces .ds :
131+ return QName (None , tag )
132+ return ds_tag (tag )
133+
120134 def check_deprecated_methods (self ):
121135 if "SHA1" in self .sign_alg .name or "SHA1" in self .digest_alg .name :
122136 msg = "SHA1-based algorithms are not supported in the default configuration because they are not secure"
@@ -304,19 +318,19 @@ def _add_key_info(self, sig_root, signing_settings: SigningSettings):
304318 if self .sign_alg .name .startswith ("HMAC_" ):
305319 return
306320 if signing_settings .key_info is None :
307- key_info = SubElement (sig_root , ds_tag ("KeyInfo" ))
321+ key_info = SubElement (sig_root , self . _ds_tag ("KeyInfo" ))
308322 if signing_settings .key_name is not None :
309- keyname = SubElement (key_info , ds_tag ("KeyName" ))
323+ keyname = SubElement (key_info , self . _ds_tag ("KeyName" ))
310324 keyname .text = signing_settings .key_name
311325
312326 if signing_settings .cert_chain is None or signing_settings .always_add_key_value :
313327 self ._serialize_key_value (signing_settings .key , key_info )
314328
315329 if signing_settings .cert_chain is not None :
316330 assert len (signing_settings .cert_chain ) > 0
317- x509_data = SubElement (key_info , ds_tag ("X509Data" ))
331+ x509_data = SubElement (key_info , self . _ds_tag ("X509Data" ))
318332 for cert in signing_settings .cert_chain :
319- x509_certificate = SubElement (x509_data , ds_tag ("X509Certificate" ))
333+ x509_certificate = SubElement (x509_data , self . _ds_tag ("X509Certificate" ))
320334 if isinstance (cert , (str , bytes )):
321335 x509_certificate .text = strip_pem_header (cert )
322336 else :
@@ -333,7 +347,7 @@ def _get_c14n_inputs_from_references(self, doc_root, references: List[SignatureR
333347 return c14n_inputs , new_references
334348
335349 def _unpack (self , data , references : List [SignatureReference ]):
336- sig_root = Element (ds_tag ("Signature" ), nsmap = self .namespaces )
350+ sig_root = Element (self . _ds_tag ("Signature" ), nsmap = self .namespaces )
337351 if self .construction_method == SignatureConstructionMethod .enveloped :
338352 if isinstance (data , (str , bytes )):
339353 raise InvalidInput ("When using enveloped signature, **data** must be an XML element" )
@@ -376,7 +390,7 @@ def _unpack(self, data, references: List[SignatureReference]):
376390 c14n_inputs = [self .get_root (data )]
377391 elif self .construction_method == SignatureConstructionMethod .enveloping :
378392 doc_root = sig_root
379- c14n_inputs = [Element (ds_tag ("Object" ), nsmap = self .namespaces , Id = "object" )]
393+ c14n_inputs = [Element (self . _ds_tag ("Object" ), nsmap = self .namespaces , Id = "object" )]
380394 if isinstance (data , (str , bytes )):
381395 c14n_inputs [0 ].text = data
382396 else :
@@ -389,14 +403,14 @@ def _build_transforms_for_reference(
389403 ):
390404 assert reference .c14n_method is not None
391405 if self .construction_method == SignatureConstructionMethod .enveloped :
392- SubElement (transforms_node , ds_tag ("Transform" ), Algorithm = SignatureConstructionMethod .enveloped .value )
406+ SubElement (transforms_node , self . _ds_tag ("Transform" ), Algorithm = SignatureConstructionMethod .enveloped .value )
393407 if not exclude_c14n_transform_element :
394- SubElement (transforms_node , ds_tag ("Transform" ), Algorithm = reference .c14n_method .value )
408+ SubElement (transforms_node , self . _ds_tag ("Transform" ), Algorithm = reference .c14n_method .value )
395409 else :
396410 if not exclude_c14n_transform_element :
397411 c14n_xform = SubElement (
398412 transforms_node ,
399- ds_tag ("Transform" ),
413+ self . _ds_tag ("Transform" ),
400414 Algorithm = reference .c14n_method .value ,
401415 )
402416 if reference .inclusive_ns_prefixes :
@@ -407,41 +421,41 @@ def _build_transforms_for_reference(
407421 def _build_sig (
408422 self , sig_root , references , c14n_inputs , inclusive_ns_prefixes , exclude_c14n_transform_element = False
409423 ):
410- signed_info = SubElement (sig_root , ds_tag ("SignedInfo" ), nsmap = self .namespaces )
411- sig_c14n_method = SubElement (signed_info , ds_tag ("CanonicalizationMethod" ), Algorithm = self .c14n_alg .value )
424+ signed_info = SubElement (sig_root , self . _ds_tag ("SignedInfo" ), nsmap = self .namespaces )
425+ sig_c14n_method = SubElement (signed_info , self . _ds_tag ("CanonicalizationMethod" ), Algorithm = self .c14n_alg .value )
412426 if inclusive_ns_prefixes :
413427 SubElement (sig_c14n_method , ec_tag ("InclusiveNamespaces" ), PrefixList = " " .join (inclusive_ns_prefixes ))
414428
415- SubElement (signed_info , ds_tag ("SignatureMethod" ), Algorithm = self .sign_alg .value )
429+ SubElement (signed_info , self . _ds_tag ("SignatureMethod" ), Algorithm = self .sign_alg .value )
416430 for i , reference in enumerate (references ):
417431 if reference .c14n_method is None :
418432 reference = replace (reference , c14n_method = self .c14n_alg )
419433 if reference .inclusive_ns_prefixes is None :
420434 reference = replace (reference , inclusive_ns_prefixes = inclusive_ns_prefixes )
421- reference_node = SubElement (signed_info , ds_tag ("Reference" ), URI = reference .URI )
422- transforms = SubElement (reference_node , ds_tag ("Transforms" ))
435+ reference_node = SubElement (signed_info , self . _ds_tag ("Reference" ), URI = reference .URI )
436+ transforms = SubElement (reference_node , self . _ds_tag ("Transforms" ))
423437 self ._build_transforms_for_reference (
424438 transforms_node = transforms ,
425439 reference = reference ,
426440 exclude_c14n_transform_element = exclude_c14n_transform_element ,
427441 )
428- SubElement (reference_node , ds_tag ("DigestMethod" ), Algorithm = self .digest_alg .value )
429- digest_value = SubElement (reference_node , ds_tag ("DigestValue" ))
442+ SubElement (reference_node , self . _ds_tag ("DigestMethod" ), Algorithm = self .digest_alg .value )
443+ digest_value = SubElement (reference_node , self . _ds_tag ("DigestValue" ))
430444 payload_c14n = self ._c14n (
431445 c14n_inputs [i ], algorithm = reference .c14n_method , inclusive_ns_prefixes = reference .inclusive_ns_prefixes
432446 )
433447 digest = self ._get_digest (payload_c14n , algorithm = self .digest_alg )
434448 digest_value .text = b64encode (digest ).decode ()
435- signature_value = SubElement (sig_root , ds_tag ("SignatureValue" ))
449+ signature_value = SubElement (sig_root , self . _ds_tag ("SignatureValue" ))
436450 return signed_info , signature_value
437451
438452 def _build_signature_properties (self , signature_properties ):
439453 # FIXME: make this use the annotator API
440- obj = Element (ds_tag ("Object" ), attrib = {"Id" : "prop" }, nsmap = self .namespaces )
441- signature_properties_el = Element (ds_tag ("SignatureProperties" ))
454+ obj = Element (self . _ds_tag ("Object" ), attrib = {"Id" : "prop" }, nsmap = self .namespaces )
455+ signature_properties_el = Element (self . _ds_tag ("SignatureProperties" ))
442456 for i , el in enumerate (signature_properties ):
443457 signature_property = Element (
444- ds_tag ("SignatureProperty" ),
458+ self . _ds_tag ("SignatureProperty" ),
445459 attrib = {
446460 "Id" : el .attrib .pop ("Id" , f"sigprop{ i } " ),
447461 "Target" : el .attrib .pop ("Target" , f"#sigproptarget{ i } " ),
@@ -456,17 +470,17 @@ def _serialize_key_value(self, key, key_info_node):
456470 """
457471 Add the public components of the key to the signature (see https://www.w3.org/TR/xmldsig-core2/#sec-KeyValue).
458472 """
459- key_value = SubElement (key_info_node , ds_tag ("KeyValue" ))
473+ key_value = SubElement (key_info_node , self . _ds_tag ("KeyValue" ))
460474 if self .sign_alg .name .startswith ("RSA_" ) or self .sign_alg .name .startswith ("SHA" ):
461- rsa_key_value = SubElement (key_value , ds_tag ("RSAKeyValue" ))
462- modulus = SubElement (rsa_key_value , ds_tag ("Modulus" ))
475+ rsa_key_value = SubElement (key_value , self . _ds_tag ("RSAKeyValue" ))
476+ modulus = SubElement (rsa_key_value , self . _ds_tag ("Modulus" ))
463477 modulus .text = b64encode (long_to_bytes (key .public_key ().public_numbers ().n )).decode ()
464- exponent = SubElement (rsa_key_value , ds_tag ("Exponent" ))
478+ exponent = SubElement (rsa_key_value , self . _ds_tag ("Exponent" ))
465479 exponent .text = b64encode (long_to_bytes (key .public_key ().public_numbers ().e )).decode ()
466480 elif self .sign_alg .name .startswith ("DSA_" ):
467- dsa_key_value = SubElement (key_value , ds_tag ("DSAKeyValue" ))
481+ dsa_key_value = SubElement (key_value , self . _ds_tag ("DSAKeyValue" ))
468482 for field in "p" , "q" , "g" , "y" :
469- e = SubElement (dsa_key_value , ds_tag (field .upper ()))
483+ e = SubElement (dsa_key_value , self . _ds_tag (field .upper ()))
470484
471485 if field == "y" :
472486 key_params = key .public_key ().public_numbers ()
0 commit comments