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,16 @@ 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 (
407+ transforms_node , self ._ds_tag ("Transform" ), Algorithm = SignatureConstructionMethod .enveloped .value
408+ )
393409 if not exclude_c14n_transform_element :
394- SubElement (transforms_node , ds_tag ("Transform" ), Algorithm = reference .c14n_method .value )
410+ SubElement (transforms_node , self . _ds_tag ("Transform" ), Algorithm = reference .c14n_method .value )
395411 else :
396412 if not exclude_c14n_transform_element :
397413 c14n_xform = SubElement (
398414 transforms_node ,
399- ds_tag ("Transform" ),
415+ self . _ds_tag ("Transform" ),
400416 Algorithm = reference .c14n_method .value ,
401417 )
402418 if reference .inclusive_ns_prefixes :
@@ -407,41 +423,41 @@ def _build_transforms_for_reference(
407423 def _build_sig (
408424 self , sig_root , references , c14n_inputs , inclusive_ns_prefixes , exclude_c14n_transform_element = False
409425 ):
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 )
426+ signed_info = SubElement (sig_root , self . _ds_tag ("SignedInfo" ), nsmap = self .namespaces )
427+ sig_c14n_method = SubElement (signed_info , self . _ds_tag ("CanonicalizationMethod" ), Algorithm = self .c14n_alg .value )
412428 if inclusive_ns_prefixes :
413429 SubElement (sig_c14n_method , ec_tag ("InclusiveNamespaces" ), PrefixList = " " .join (inclusive_ns_prefixes ))
414430
415- SubElement (signed_info , ds_tag ("SignatureMethod" ), Algorithm = self .sign_alg .value )
431+ SubElement (signed_info , self . _ds_tag ("SignatureMethod" ), Algorithm = self .sign_alg .value )
416432 for i , reference in enumerate (references ):
417433 if reference .c14n_method is None :
418434 reference = replace (reference , c14n_method = self .c14n_alg )
419435 if reference .inclusive_ns_prefixes is None :
420436 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" ))
437+ reference_node = SubElement (signed_info , self . _ds_tag ("Reference" ), URI = reference .URI )
438+ transforms = SubElement (reference_node , self . _ds_tag ("Transforms" ))
423439 self ._build_transforms_for_reference (
424440 transforms_node = transforms ,
425441 reference = reference ,
426442 exclude_c14n_transform_element = exclude_c14n_transform_element ,
427443 )
428- SubElement (reference_node , ds_tag ("DigestMethod" ), Algorithm = self .digest_alg .value )
429- digest_value = SubElement (reference_node , ds_tag ("DigestValue" ))
444+ SubElement (reference_node , self . _ds_tag ("DigestMethod" ), Algorithm = self .digest_alg .value )
445+ digest_value = SubElement (reference_node , self . _ds_tag ("DigestValue" ))
430446 payload_c14n = self ._c14n (
431447 c14n_inputs [i ], algorithm = reference .c14n_method , inclusive_ns_prefixes = reference .inclusive_ns_prefixes
432448 )
433449 digest = self ._get_digest (payload_c14n , algorithm = self .digest_alg )
434450 digest_value .text = b64encode (digest ).decode ()
435- signature_value = SubElement (sig_root , ds_tag ("SignatureValue" ))
451+ signature_value = SubElement (sig_root , self . _ds_tag ("SignatureValue" ))
436452 return signed_info , signature_value
437453
438454 def _build_signature_properties (self , signature_properties ):
439455 # 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" ))
456+ obj = Element (self . _ds_tag ("Object" ), attrib = {"Id" : "prop" }, nsmap = self .namespaces )
457+ signature_properties_el = Element (self . _ds_tag ("SignatureProperties" ))
442458 for i , el in enumerate (signature_properties ):
443459 signature_property = Element (
444- ds_tag ("SignatureProperty" ),
460+ self . _ds_tag ("SignatureProperty" ),
445461 attrib = {
446462 "Id" : el .attrib .pop ("Id" , f"sigprop{ i } " ),
447463 "Target" : el .attrib .pop ("Target" , f"#sigproptarget{ i } " ),
@@ -456,17 +472,17 @@ def _serialize_key_value(self, key, key_info_node):
456472 """
457473 Add the public components of the key to the signature (see https://www.w3.org/TR/xmldsig-core2/#sec-KeyValue).
458474 """
459- key_value = SubElement (key_info_node , ds_tag ("KeyValue" ))
475+ key_value = SubElement (key_info_node , self . _ds_tag ("KeyValue" ))
460476 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" ))
477+ rsa_key_value = SubElement (key_value , self . _ds_tag ("RSAKeyValue" ))
478+ modulus = SubElement (rsa_key_value , self . _ds_tag ("Modulus" ))
463479 modulus .text = b64encode (long_to_bytes (key .public_key ().public_numbers ().n )).decode ()
464- exponent = SubElement (rsa_key_value , ds_tag ("Exponent" ))
480+ exponent = SubElement (rsa_key_value , self . _ds_tag ("Exponent" ))
465481 exponent .text = b64encode (long_to_bytes (key .public_key ().public_numbers ().e )).decode ()
466482 elif self .sign_alg .name .startswith ("DSA_" ):
467- dsa_key_value = SubElement (key_value , ds_tag ("DSAKeyValue" ))
483+ dsa_key_value = SubElement (key_value , self . _ds_tag ("DSAKeyValue" ))
468484 for field in "p" , "q" , "g" , "y" :
469- e = SubElement (dsa_key_value , ds_tag (field .upper ()))
485+ e = SubElement (dsa_key_value , self . _ds_tag (field .upper ()))
470486
471487 if field == "y" :
472488 key_params = key .public_key ().public_numbers ()
0 commit comments