Repository: italia/spid-sp-test (language: python)

Issue:
ContactPerson validation issues when profile is spid-sp-ag-public-full
When validating metadata in spid-sp-ag-public-full profile mode, validation of `ContactPerson` elements is wrong.

See: https://github.com/italia/spid-sp-test/blob/10917dd825856e838944b0395972b2ff37cac345/src/spid_sp_test/metadata.py#L746

Here you're searching for a `ContactPerson` having the `contactType` attribute equal to `aggregator`.
The right query, however, is: search for a `ContactPerson` having the `contactType` attribute equal to `other` and the `entityType` attribute equal to `spid:aggregator`.
The same stands for the following query (regarding the aggregated entity).
After all, the comments on the previous lines say it correctly.

The same problem applies to the other aggregator profiles:
https://github.com/italia/spid-sp-test/blob/10917dd825856e838944b0395972b2ff37cac345/src/spid_sp_test/metadata.py#L770
https://github.com/italia/spid-sp-test/blob/10917dd825856e838944b0395972b2ff37cac345/src/spid_sp_test/metadata.py#L792
https://github.com/italia/spid-sp-test/blob/10917dd825856e838944b0395972b2ff37cac345/src/spid_sp_test/metadata.py#L811


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index fdc182c..b34edff 100644
--- a/README.md
+++ b/README.md
@@ -187,6 +187,12 @@ spid-sp-test offers the possibility to extend and configure new response tests t
 
 - customize the way to get the SAML2 Authn Request, using plugins wrote by your own. If you're using a IAM Proxy with some OAuth2/OIDC frontends of a custom API, you can write your plugin and use it in the cli arguments, eg: `spid_sp_test --metadata-url https://localhost:8000/spid/metadata --extra  --authn-url https://localhost:8000/spid/login/?idp=https://localhost:8080 --debug INFO -tr --authn-plugin spid_sp_test.plugins.authn_request.Dummy`
 
+- customize entityid and certificates path runtime, using ENV variables. The files MUST be named `private.key` and `public.cert`:
+  ````
+  IDP_ENTITYID=https://your.idp.eid/ IDP_CERT_PATH=../spid-django/example/certificates spid_sp_test --idp-metadata
+  ````
+
+
 Looking at `src/spid_sp_test/responses/settings.py` or `tests/example.test-suite.json`
 we found that every test have a `response` attribute. Each element configured in would overload the
 value that will be rendered in the template. Each template can load these variable from its template context or
diff --git a/setup.py b/setup.py
index b0acc0f..4f9e2bc 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ def readme():
 
 setup(
     name=_pkg_name,
-    version='0.9.7',
+    version='0.9.8',
     description="SAML2 SPID Service Provider validation tool that can be run from the command line",
     long_description=readme(),
     long_description_content_type='text/markdown',


Test patch:
diff --git a/src/spid_sp_test/constants.py b/src/spid_sp_test/constants.py
index b8396c4..806da5e 100644
--- a/src/spid_sp_test/constants.py
+++ b/src/spid_sp_test/constants.py
@@ -410,3 +410,7 @@
     "ZM",
     "ZW",
 ]
+
+XML_NAMESPACES = {
+    'spid':'https://spid.gov.it/saml-extensions'
+}
diff --git a/src/spid_sp_test/idp/settings.py b/src/spid_sp_test/idp/settings.py
index 505e03c..7ecf1e3 100644
--- a/src/spid_sp_test/idp/settings.py
+++ b/src/spid_sp_test/idp/settings.py
@@ -1,3 +1,5 @@
+import os
+
 from pathlib import Path
 from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
 
@@ -5,7 +7,8 @@
 
 
 BASE_DIR = Path(__file__).resolve().parent
-BASE = "https://localhost:8080"
+BASE = os.environ.get("IDP_ENTITYID", "https://localhost:8080")
+CERT_PATH = os.environ.get("IDP_CERT_PATH", BASE_DIR)
 
 SAML2_IDP_CONFIG = {
     "entityid": BASE,
@@ -25,8 +28,8 @@
         },
     },
     "debug": 0,
-    "key_file": f"{BASE_DIR}/private.key",
-    "cert_file": f"{BASE_DIR}/public.cert",
+    "key_file": f"{CERT_PATH}/private.key",
+    "cert_file": f"{CERT_PATH}/public.cert",
     # "xmlsec_binary": xmlsec_path,
     "metadata": [],
     # "attribute_map_dir": full_path("attributemaps"),
diff --git a/src/spid_sp_test/metadata.py b/src/spid_sp_test/metadata.py
index ea1e8b2..b0699a3 100644
--- a/src/spid_sp_test/metadata.py
+++ b/src/spid_sp_test/metadata.py
@@ -121,7 +121,7 @@ def test_EntityDescriptor(self):
             )
             self._assertHttpUrlWithoutPort(
                 self.doc.attrib.get("entityID"),
-                'The entityID attribute MUST not contains any custom tcp ports, eg: ":8000"',
+                'The entityID attribute MUST not contain any custom tcp ports, eg: ":8000"',
             )
 
         return self.is_ok(f"{self.__class__.__name__}.test_EntityDescriptor")
@@ -405,7 +405,7 @@ def test_SingleLogoutService(self):
                     )
                     self._assertHttpUrlWithoutPort(
                         _attr,
-                        'The entityID attribute MUST not contains any custom tcp ports, eg: ":8000"',
+                        'The entityID attribute MUST not contain any custom tcp ports, eg: ":8000"',
                     )
                 elif attr == "Location":
                     self._assertIsValidHttpUrl(
@@ -466,7 +466,7 @@ def test_AssertionConsumerService(self):
                     )
                     self._assertHttpUrlWithoutPort(
                         _attr,
-                        'The entityID attribute MUST not contains any custom tcp ports, eg: ":8000"',
+                        'The entityID attribute MUST not contain any custom tcp ports, eg: ":8000"',
                     )
                 else:
                     pass
@@ -732,7 +732,7 @@ def test_profile_spid_sp_private(self):
             xsds_files=["saml-schema-metadata-2.0.xsd", "spid-invoicing.xsd"]
         )
 
-    def test_profile_spid_ag_public_full(self):
+    def test_profile_spid_sp_ag_public_full(self):
         self.test_profile_spid_sp()
 
         self.test_extensions_public_private(ext_type="Public")
@@ -743,10 +743,10 @@ def test_profile_spid_ag_public_full(self):
 
         # The ContactPerson element of contactType “other” and spid:entityType “spid:aggregator” MUST be present
         # The ContactPerson element of contactType “other” and spid:entityType “spid:aggregated” MUST be present
-        self.test_Contacts_PubPriv(contact_type="aggregator")
-        self.test_Contacts_PubPriv(contact_type="aggregated")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregator")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregated")
 
-        # The entityID MUST not contains the query-string part
+        # The entityID MUST not contain the query-string part
         self.test_entityid_qs()
 
         # The entityID MUST contain the activity code “pub-ag-full”
@@ -758,7 +758,7 @@ def test_profile_spid_ag_public_full(self):
             must=True,
         )
 
-    def test_profile_spid_ag_public_lite(self):
+    def test_profile_spid_sp_ag_public_lite(self):
         self.test_profile_spid_sp()
         self.test_extensions_public_private(ext_type="Public")
 
@@ -767,8 +767,8 @@ def test_profile_spid_ag_public_lite(self):
 
         # Only one ContactPerson element of contactType “other” and spid:entityType “spid:aggregator” MUST be present
         # Only one ContactPerson element of contactType “other” and spid:entityType “spid:aggregated” MUST be present
-        self.test_Contacts_PubPriv(contact_type="aggregator")
-        self.test_Contacts_PubPriv(contact_type="aggregated")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregator")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregated")
 
         # TODO
         # If the ContactPerson is of spid:entityType “spid:aggregator” the Extensions element MUST contain the element spid:KeyDescriptor with attribute use “spid:validation”
@@ -779,17 +779,16 @@ def test_profile_spid_ag_public_lite(self):
             must=True,
         )
 
-    def test_profile_spid_op_public_full(self):
+    def test_profile_spid_sp_op_public_full(self):
         self.test_profile_spid_sp()
 
         self.test_Contacts_VATFC()
-        self.test_extensions_public_private(ext_type="Public")
 
         # The entityID MUST contain the activity code “pub-op-full”
-        self.test_entityid_contains(value="pub-ag-full")
+        self.test_entityid_contains(value="pub-op-full")
 
         # Only one ContactPerson element of contactType “other” and spid:entityType “spid:aggregator” MUST be present
-        self.test_Contacts_PubPriv(contact_type="aggregator")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregator")
 
         # The PublicServicesFullOperator element MUST be present
         self.test_extensions_public_ag(
@@ -797,7 +796,7 @@ def test_profile_spid_op_public_full(self):
             must=True,
         )
 
-    def test_profile_spid_op_public_lite(self):
+    def test_profile_spid_sp_op_public_lite(self):
         self.test_profile_spid_sp()
 
         self.test_Contacts_VATFC()
@@ -808,8 +807,8 @@ def test_profile_spid_op_public_lite(self):
 
         # Only one ContactPerson element of contactType “other” and spid:entityType “spid:aggregator” MUST be present
         # Only one ContactPerson element of contactType “other” and spid:entityType “spid:aggregated” MUST be present
-        self.test_Contacts_PubPriv(contact_type="aggregator")
-        self.test_Contacts_PubPriv(contact_type="aggregated")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregator")
+        self.test_Contacts_PubPriv(entity_type="spid:aggregated")
 
         # The PublicServicesLightOperator element MUST be present
         self.test_extensions_public_ag(
diff --git a/src/spid_sp_test/metadata_ag.py b/src/spid_sp_test/metadata_ag.py
index 165815f..f4d8e3b 100644
--- a/src/spid_sp_test/metadata_ag.py
+++ b/src/spid_sp_test/metadata_ag.py
@@ -32,7 +32,7 @@ def test_extensions_public_ag(
         return self.is_ok(f"{self.__class__.__name__}.test_extensions_public_ag")
 
     def test_entityid_qs(self):
-        """The entityID MUST not contains the query-string part"""
+        """The entityID MUST not contain the query-string part"""
 
         entity_desc = self.doc.xpath("//EntityDescriptor")
         eid = entity_desc[0].get("entityID")
@@ -40,8 +40,8 @@ def test_entityid_qs(self):
         qs = urllib.parse.splitquery(eid)
 
         self._assertFalse(
-            qs,
-            ("The entityID MUST not contains the query-string part"),
+            qs[1],
+            ("The entityID MUST not contain the query-string part"),
             description=eid,
         )
         return self.is_ok(f"{self.__class__.__name__}.test_entityid_qs")
diff --git a/src/spid_sp_test/metadata_public.py b/src/spid_sp_test/metadata_public.py
index ac52e4c..259d754 100644
--- a/src/spid_sp_test/metadata_public.py
+++ b/src/spid_sp_test/metadata_public.py
@@ -4,12 +4,23 @@
 
 from .constants import EMAIL_REGEXP
 from .constants import ISO3166_CODES
+from .constants import XML_NAMESPACES
 from .indicepa import get_indicepa_by_ipacode
 
 
 class SpidSpMetadataCheckPublic(object):
-    def test_Contacts_PubPriv(self, contact_type="other"):
-        entity_desc = self.doc.xpath(f'//ContactPerson[@contactType="{contact_type}"]')
+    def test_Contacts_PubPriv(
+                self, contact_type="other", entity_type=""):
+        xpatt = f"//ContactPerson[@contactType='{contact_type}'"
+        if entity_type:
+            xpatt += f" and @spid:entityType='{entity_type}']"
+        else:
+            xpatt += ']'
+
+        entity_desc = self.doc.xpath(
+            xpatt,
+            namespaces=XML_NAMESPACES
+        )
         self._assertTrue(entity_desc, "ContactPerson MUST be present")
 
         if entity_desc:
@@ -30,10 +41,11 @@ def test_Contacts_PubPriv(self, contact_type="other"):
                 description=entity_desc[0].get("contactType"),
             )
 
-        self._assertTrue(
-            len(entity_desc) == 1,
-            f'Only one ContactPerson element of contactType "{contact_type}" MUST be present',
-        )
+        if not entity_type:
+            self._assertTrue(
+                len(entity_desc) == 1,
+                f'Only one ContactPerson element of contactType "{contact_type}" MUST be present',
+            )
 
         return self.is_ok(f"{self.__class__.__name__}.test_Contacts_PubPriv")
 
diff --git a/src/spid_sp_test/response.py b/src/spid_sp_test/response.py
index 4e7c684..3b4d4c7 100644
--- a/src/spid_sp_test/response.py
+++ b/src/spid_sp_test/response.py
@@ -200,6 +200,8 @@ def do_authnrequest(self):
             "AuthnContextClassRef": self.acr
             or settings.DEFAULT_RESPONSE["AuthnContextClassRef"],
             "IssueInstantMillis": now.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
+            "sign_response": settings.DEFAULT_RESPONSE["sign_response"],
+            "sign_assertion": settings.DEFAULT_RESPONSE["sign_assertion"]
         }
         self.relay_state = self.kwargs.get("relay_state")
 
diff --git a/src/spid_sp_test/responses/settings.py b/src/spid_sp_test/responses/settings.py
index d7a8bca..622059c 100644
--- a/src/spid_sp_test/responses/settings.py
+++ b/src/spid_sp_test/responses/settings.py
@@ -49,6 +49,8 @@
     "SessionIndex": "",  # _ffa3114b-f589-417b-8602-4b0275f6bafc
     "AuthnContextClassRef": "https://www.spid.gov.it/SpidL1",
     "Attributes": ATTRIBUTES,
+    "sign_response": True,
+    "sign_assertion": True
 }
 
 
@@ -295,14 +297,14 @@
     },
     "30": {
         "name": "30. Response - Attributo Format di Issuer diverso",
-        "description": "L'attributo Format di Issuer deve essere omesso o assumere valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity. In questo test il valore è diverso. Risultato atteso: KO",
+        "description": "L'attributo Format di Issuer della Response deve essere omesso o assumere valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity. In questo test il valore è diverso. Risultato atteso: KO",
         "status_codes": HTTP_STATUS_ERROR_CODES,
         "path": "case-30.xml",
         "response": {},
     },
     "31": {
         "name": "31. Response - Attributo Format di Issuer omesso",
-        "description": "L'attributo Format di Issuer deve essere omesso o assumere valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity. In questo test il valore è omesso. Risultato atteso: Ok",
+        "description": "L'attributo Format di Issuer della Response deve essere omesso o assumere valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity. In questo test il valore è omesso. Risultato atteso: Ok",
         "status_codes": [200],
         "path": "case-31.xml",
         "response": {},
diff --git a/src/spid_sp_test/responses/templates/base.xml b/src/spid_sp_test/responses/templates/base.xml
index 462ba1b..e2f462e 100644
--- a/src/spid_sp_test/responses/templates/base.xml
+++ b/src/spid_sp_test/responses/templates/base.xml
@@ -10,7 +10,9 @@
     xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
     {% block Issuer %}<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">{{Issuer}}</saml:Issuer>{% endblock %}
     {% block response_signature %}
+    {% if sign_response %}
     <!-- Response Signature here -->
+    {% endif %}
     {% endblock response_signature %}
     {% block Status %}<samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>{% endblock %}
     {% block Assertion %}
@@ -21,7 +23,9 @@
     {% endblock AssertionTag %}
         {% block AssertionIssuer %}<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">{{Issuer}}</saml:Issuer>{% endblock %}
         {% block assertion_signature %}
+        {% if sign_assertion %}
         <!-- Assertion Signature here -->
+        {% endif %}
         {% endblock assertion_signature %}
         {% block SubjectTag %}
         <saml:Subject>
diff --git a/src/spid_sp_test/spid_sp_test b/src/spid_sp_test/spid_sp_test
index 27f51d4..1a13e51 100644
--- a/src/spid_sp_test/spid_sp_test
+++ b/src/spid_sp_test/spid_sp_test
@@ -167,7 +167,7 @@ if __name__ == '__main__':
         choices=('CRITICAL', 'ERROR',
                  'WARNING', 'INFO', 'DEBUG'),
         default='INFO',
-        help="Debug level, see python logging"
+        help="Debug level, see python logging; defaults to INFO if omitted"
     )
 
     parser.add_argument(
@@ -213,7 +213,7 @@ if __name__ == '__main__':
             'cie-sp-public',
             'cie-sp-private',
         ),
-        help="which profile to check"
+        help="which profile to check; defaults to spid-sp-public if omitted"
     )
 
     parser.add_argument(
@@ -225,21 +225,21 @@ if __name__ == '__main__':
     parser.add_argument(
         '-rm', '--request-method',
         required=False, default='GET',
-        help="GET or POST"
+        help="HTTP method to use when requesting authn-url e.g. GET or POST; defaults to GET if omitted"
     )
 
     parser.add_argument(
         '-rb', '--request-body',
         required=False, type=json.loads,
-        help='eg: {"idp": "https://localhost:8080"}'
+        help='key-value pairs payload to submit in the HTTP request body when requesting authn-url; e.g.: {"idp": "https://localhost:8080"}'
     )
 
     parser.add_argument(
         '-rct', '--request-content-type',
         required=False, default='data',
-        help=("python requests' defaults arguments: data or json. "
-              "'data' means application/x-www-form-urlencoded "
-              "'json' means application/json"
+        help=("python requests' defaults arguments: data or json; "
+              "use 'data' for application/x-www-form-urlencoded; "
+              "use 'json' for application/json; defaults to data if omitted"
         )
     )
 
diff --git a/tests/metadata/pub-ag-lite.xml b/tests/metadata/pub-ag-lite.xml
index 812f744..5a5d172 100644
--- a/tests/metadata/pub-ag-lite.xml
+++ b/tests/metadata/pub-ag-lite.xml
@@ -77,6 +77,7 @@
   <md:ContactPerson contactType="other" spid:entityType="spid:aggregated">
     <md:Extensions>
       <spid:IPACode>IPA</spid:IPACode>
+      <spid:Public/>
     </md:Extensions>
     <md:Company>Organizzazione fittizia per il collaudo</md:Company>
   </md:ContactPerson>
diff --git a/tests/metadata/pub-ag-lite_signed.xml b/tests/metadata/pub-ag-lite_signed.xml
index 9105a6a..a04cbd6 100644
--- a/tests/metadata/pub-ag-lite_signed.xml
+++ b/tests/metadata/pub-ag-lite_signed.xml
@@ -11,15 +11,15 @@
 <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
 </ds:Transforms>
 <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
-<ds:DigestValue>XOitqoMod1U3EpJBfsYbdKQD0EYFe4tfl+Zo2hjhTMo=</ds:DigestValue>
+<ds:DigestValue>+eSnyVeDIgpM/Q3Wgj1c3zaLgW8FWpHSVV61ov6sSUg=</ds:DigestValue>
 </ds:Reference>
 </ds:SignedInfo>
-<ds:SignatureValue>RoXIwBB3hwpHDKRmb6z5ifQrE38TKZ6qRqgWvjY/ASBiUzBu5gFakxLwt1ZTyIa5
-G6u+Ca5SwLvNq3Nd+Cls9O825dzFdObvt0FQ5i4wi8oqtFdgkfDW/xv9QBln+G+4
-Ta3SSCemjCxxZH8vvtH66Rajaqu5yWGFaiP79CoAqYeh8ybUtb3OsjoJbtobE139
-P+vZJoQXUHoj4LaT76FsaAz2oSDEZcCDUdEBpvZPGMIFx5GsmkujrvreTSOZHxUY
-fAIFVclqK4En/zGtghAqnDQ7HIeJgncxV8uteXgkmr2ul4r2C2sn0HGRS8fF6zEZ
-GYPelJZtzHRT/UmWKRvFRA==</ds:SignatureValue>
+<ds:SignatureValue>ZTGocqktbPpBqMPQhl1swaAI/b8RDS+m5Yenr3UhTTNRKNVNfdyeL0h5EXLhGEHB
+5T9ok4DC9TUdbMBysW8BPLiU6DwBNMLg+ck6i7Uea/ctDFTz30S87FytnZ7LeSu4
+qe2g8rIcKtDBWif5wM4EPWff7idV2lWbStNvx7qTiMjJ8lbfh6JAsDzZ2MxsBdw0
+frK2xfUxxs4vd7+KH8DBaOQgbLD3cGURG5c21iS8yN32QhhTwrk6KQnZl6clNZij
+WhbuA+zBX1YqyuglnwZqub87U/c4jAq7A6CCooQSqOlpXnXRV2pTz+W27dprjuz3
+cOHAk8eFI5IJRdO3tH/iTw==</ds:SignatureValue>
 <ds:KeyInfo>
 <ds:X509Data>
 <ds:X509Certificate>MIIEGDCCAwCgAwIBAgIJAOrYj9oLEJCwMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdDAeFw0xOTA0MTExMDAyMDhaFw0yNTAzMDgxMDAyMDhaMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8kJVo+ugRrbbv9xhXCuVrqi4B7/MQzQc62ocwlFFujJNd4m1mXkUHFbgvwhRkQqo2DAmFeHiwCkJT3K1eeXIFhNFFroEzGPzONyekLpjNvmYIs1CFvirGOj0bkEiGaKEs+/umzGjxIhy5JQlqXE96y1+Izp2QhJimDK0/KNij8I1bzxseP0Ygc4SFveKS+7QO+PrLzWklEWGMs4DM5Zc3VRK7g4LWPWZhKdImC1rnS+/lEmHSvHisdVp/DJtbSrZwSYTRvTTz5IZDSq4kAzrDfpj16h7b3t3nFGc8UoY2Ro4tRZ3ahJ2r3b79yK6C5phY7CAANuW3gDdhVjiBNYs0CAwEAAaOByjCBxzAdBgNVHQ4EFgQU3/7kV2tbdFtphbSA4LH7+w8SkcwwgZcGA1UdIwSBjzCBjIAU3/7kV2tbdFtphbSA4LH7+w8SkcyhaaRnMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdIIJAOrYj9oLEJCwMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJNFqXg/V3aimJKUmUaqmQEEoSc3qvXFITvT5f5bKw9yk/NVhR6wndL+z/24h1OdRqs76blgH8k116qWNkkDtt0AlSjQOx5qvFYh1UviOjNdRI4WkYONSw+vuavcx+fB6O5JDHNmMhMySKTnmRqTkyhjrch7zaFIWUSV7hsBuxpqmrWDoLWdXbV3eFH3mINA5AoIY/m0bZtzZ7YNgiFWzxQgekpxd0vcTseMnCcXnsAlctdir0FoCZztxMuZjlBjwLTtM6Ry3/48LMM8Z+lw7NMciKLLTGQyU8XmKKSSOh0dGh5Lrlt5GxIIJkH81C0YimWebz8464QPL3RbLnTKg+c=</ds:X509Certificate>
@@ -79,6 +79,7 @@ GYPelJZtzHRT/UmWKRvFRA==</ds:SignatureValue>
   <md:ContactPerson contactType="other" spid:entityType="spid:aggregated">
     <md:Extensions>
       <spid:IPACode>IPA</spid:IPACode>
+      <spid:Public/>
     </md:Extensions>
     <md:Company>Organizzazione fittizia per il collaudo</md:Company>
   </md:ContactPerson>